1

Do you know what is the reasonable way to find the closest tag that contain certain commit?

git describe --contains my-commit-hash

works fine but I don't see a way to do the same with LibGit2Sharp

I was trying to do something like

foreach (var childCommit in GetChildrenOrSelf(commit))
{
    var tag = repo.Tags[childCommit];
    if (tag != null)
        return tag;
}

But I could not find an easy way to implement GetChildrenOrSelf

According to this How to find all commits having a particular parent? - it is not a simple task.

Can you advise anything?

Community
  • 1
  • 1
mnaoumov
  • 2,146
  • 2
  • 22
  • 31
  • You're asking for tags that contain your commit, that answer has *all* commits as candidates. The list of tags is much shorter, and there'll be an API call for it. Search backwards from there. Because merges result in split paths going backwards, you need to maintain a queue of commits whose parents you want to test next, adding parents to the queue, then dequeing the commit the fewest steps from the start. Queued items should know their originating tag, to be read when you reach the goal. This is just http://en.wikipedia.org/wiki/Dijkstra's_algorithm with unit weights. – bazzargh Mar 18 '14 at 01:59
  • @bazzargh We are using tags to automatically set build version by TeamCity. Currently we have 9955 tags. I don't think that's a good idea to iterate through all tags anyway, that's slow – mnaoumov Mar 18 '14 at 02:37
  • You can immediately eliminate any tags pointing at commits older than the target, and try tags in date order. This will give you a different answer from the above (closest in time, not closest in commit count). In this case, your plethora of tags is a help as it divides time up into smaller chunks, so there are fewer commits to search. – bazzargh Mar 18 '14 at 02:48

1 Answers1

1

Git describe is a work in progress in libgit2. You can subscribe to issue libgit2/libgit2#1066 to get notifications about its future progress.

Although that's only a first step to achieve your goal, you may get some help from the repo.Refs.ReachableFrom() method. It retrieves all the References that can reach a set of particular Commits.

The tests give a first start describing how to leverage this method. One of them even highlights how to only retrieve tags.

var result = repo.Refs.ReachableFrom(
    repo.Refs.Where(r => r.IsTag()),
    new[] { repo.Lookup<Commit>(myCommitHash) });

Now, in order to determine the "closest" tag, you'd have to find, among those tags, the one that is separated by the smallest number of commits from your target commit. The following method should just do that:

private Reference Closest(IRepository repo,
    IEnumerable<Reference> result, Commit commit)
{
    int max = int.MaxValue;
    Reference cl = null;

    var f = new CommitFilter { Until = commit.Id };

    foreach (var reference in result)
    {
        f.Since = reference;
        var c = repo.Commits.QueryBy(f).Count();

        if (c >= max)
        {
            continue;
        }

        max = c;
        cl = reference;
    }

    return cl;
}

Note 1: Depending on your tag naming scheme you may be able to skip this last phase (by selecting the oldest tag without performing any count, for instance: v0.17 has huge chances to be "closer" to the commit than v0.21).

Note 2: This method isn't the most effective one as it will perform a huge number of revwalks. However, it should help you until git-describe is natively implemented in libgit2.

nulltoken
  • 64,429
  • 20
  • 138
  • 130