Skip to content

Add the ability to create a TreeRevFilter in LogCommand #149

Open
@Jonathing

Description

@Jonathing

Description

Name entails. Currently, LogCommand only allows consumers to add/exclude paths and to add rev filters. These filters are tested in an inherent AND context which is normally fine, but I would like the ability to test them in OR context so that only one or the other filter would need to succeed in order for the commit to be included in the log.

My proposal is simply to allow some way for consumers to have the log command use a TreeRevFilter that uses the tree filter present in the log command (see line 115).

Motivation

I admit this is a strange use case, but this is a part of an end goal of mine to cut down on the amount of individual Git repositories used per individual project. A lot of projects I make are small, but are not necessarily inherently related to each other, and thus need individual version numbers. To account for this, I created Git Version which is a framework-agnostic tool that allows me to calculate version numbers for individual projects based on pre-defined tag prefixes, project paths, and additional filters for tags in general.

As a part of tag-based versioning, we create a new tag to show that a new version (usually a major.minor) has been created. This unfortunately throws a small wrench into path-based project versioning, as the tag could be created on a commit that does not modify the path for the project. My solution for this is to use an OR filter where either a) the commit modifies the project path or b) the commit is tagged with a tag that starts with the tag prefix.

Alternatives considered

There is currently no way to do this with LogCommand natively, so I'm using an implementation detail that depends on the result of #call returning a RevWalk that is casted to an Iterable<RevCommit>. I would prefer not to do this, so I am writing this feature request as a way to begin discussions on how this could be solved with JGit. If the solution was simple, I would've gone straight to making a GerritHub merge request like last time.

Here's what I'm doing in Git Version, to account for this:

var walk = (RevWalk) log.call();
if (!hasFilters || StringUtils.isEmptyOrNull(tagPrefix) || commitsToTags == null || commitsToTags.isEmpty()) return walk;

walk.setRevFilter(OrRevFilter.create(new TreeRevFilter(walk, walk.getTreeFilter()), new RevFilter() {
    @Override
    public boolean include(RevWalk walker, RevCommit cmit) throws StopWalkException {
        return commitsToTags.getOrDefault(ObjectId.toString(cmit), "").startsWith(tagPrefix);
    }

    @Override
    public RevFilter clone() {
        throw new UnsupportedOperationException();
    }
}));
walk.setTreeFilter(null);

return walk;

Here's some additional context:

  • hasFilters - if I am using path filters (set using #addPath and #excludePath
  • commitsToTags - a Map<String, String> mapped by commit object ID to tag name
  • tagPrefix - the prefix of the tag I am filtering for

To make this TreeRevFilter, I am using the walker's tree filter as the tree filter for the object. I then unset the walker's tree filter so that it only uses the rev filter that inherently contains the tree filter.

Additional context

I'm more than willing to make a merge request through GerritHub like I did when I added DescribeCommand#setExcludes.

Potential Solution no. 1

My first thought on trying to solve this was to give LogCommand a new private attribute of type Function<TreeRevFilter, RevFilter> filter. This variable would be used in #call to replace the tree filter that the walker would be using.

public void setRevFilter(Function<TreeRevFilter, RevFilter> filter) {
    this.treeRevFilter = filter;
}

public Iterable<RevCommit> call() {
    // ...
    if (this.treeRevFilter != null) {
        walk.setFilter(this.treeRevFilter.accept(new TreeRevFilter(walk, walk.getTreeFilter()));
        walk.setTreeFilter(null);
    }
    // ...
}

This is a very primitive solution but I think it does a good job of describing what I'm trying to achieve, along with the workaround I provided above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions