Узнать, к какой ветке принадлежит коммит в LibGit2Sharp?

Я перебираю коммиты в LibGit2Sharp:

Repository repo = new Repository("Z:/www/gg");

foreach (LibGit2Sharp.Commit commit in repo.Commits)
{
    ...
}

Я могу получить такие свойства, как Author и Message, но ничего не вижу о том, к какой ветке они принадлежат? В идеале я хотел бы иметь указатель на объект ветки, но в этом сценарии подойдет даже имя.

Вот что показывает отладчик:

введите здесь описание изображения

Это то, что я ищу:

введите здесь описание изображения

Поведение TortoiseGit при отображении наиболее подходящего имени ветки:

введите здесь описание изображения

Пример репозитория: https://docs.google.com/open?id=0B-3-X85VysdNcmZIaGVTSDZSenVGbTJxYlI2SUlsZw


person Tower    schedule 26.02.2012    source источник


Ответы (3)


В настоящее время нет встроенного способа имитировать git branch --contains <commit>.

Однако вы можете обойти это ограничение, явно просматривая каждую ветку и сравнивая каждую полученную фиксацию с искомой.

Следующий тест демонстрирует это

[Test]
public void CanSearchBranchesContainingASpecificCommit()
{
    using (var repo = new Repository(StandardTestRepoPath))
    {
        const string commitSha = "5b5b025afb0b4c913b4c338a42934a3863bf3644";
        IEnumerable<Branch> branches = ListBranchesContaininingCommit(repo, commitSha);

        branches.Count().ShouldEqual(6);
    }
}

private IEnumerable<Branch> ListBranchesContaininingCommit(Repository repo, string commitSha)
{
    foreach (var branch in repo.Branches)
    {
        var commits = repo.Commits.QueryBy(new CommitFilter { Since = branch }).Where(c => c.Sha == commitSha);

        if (!commits.Any())
        {
            continue;
        }

        yield return branch;
    }
}

Примечание. Этот код был успешно протестирован на текущей вершине ветки разработки. библиотеки LibGit2Sharp.

ОБНОВЛЕНИЕ:

После обсуждения в комментариях вот небольшое обновление, которое, я надеюсь, удовлетворит вашу просьбу.

Приведенный ниже код вернет все ветки, содержащие найденный коммит. Если коммит окажется верхушкой хотя бы одной ветки, вместо нее будут возвращены эти ветки.

[Test]
public void CanSearchBranchesContainingASpecificCommit()
{
    using (var repo = new Repository(StandardTestRepoPath))
    {
        const string commitSha = "5b5b025afb0b4c913b4c338a42934a3863bf3644";
        IEnumerable<Branch> branches = ListBranchesContaininingCommit(repo, commitSha);

        branches.Count().ShouldEqual(6);

        const string otherCommitSha = "4a202b346bb0fb0db7eff3cffeb3c70babbd2045";
        branches = ListBranchesContaininingCommit(repo, otherCommitSha);

        branches.Count().ShouldEqual(1); // origin/packed-test
    }
}

private IEnumerable<Branch> ListBranchesContaininingCommit(Repository repo, string commitSha)
{
    bool directBranchHasBeenFound = false;
    foreach (var branch in repo.Branches)
    {
        if (branch.Tip.Sha != commitSha)
        {
            continue;
        }

        directBranchHasBeenFound = true;
        yield return branch;
    }

    if (directBranchHasBeenFound)
    {
        yield break;
    }

    foreach (var branch in repo.Branches)
    {
        var commits = repo.Commits.QueryBy(new CommitFilter { Since = branch }).Where(c => c.Sha == commitSha);

        if (!commits.Any())
        {
            continue;
        }

        yield return branch;
    }
}
person nulltoken    schedule 26.02.2012
comment
Это прекрасно работает, но еще одна вещь: могу ли я как-то узнать, какая из этих многочисленных ветвей является самой последней, к которой она принадлежит? т.е. в моей ситуации мало смысла знать, принадлежал ли он ветке год назад, если он был только что объединен с master неделю назад. Я сомневаюсь, что могу просто полагаться на то, что он будет первым в наборе результатов... - person Tower; 26.02.2012
comment
Не могли бы вы привести несколько примеров или эквивалентную команду git? Я бы с удовольствием попытался ответить вам, но я не уверен, что ясно понимаю, что будет самой последней веткой. - person nulltoken; 26.02.2012
comment
Если вы ответите от master и создадите ветвь foo. Если вы сейчас фиксируете foo, то коммит является частью двух ветвей (foo и master). То, что я хочу, это просто foo, потому что это самое последнее. Если бы foo была веткой-сиротой (git checkout -b foo --orphan), то ваше решение вернуло бы только foo, и я мог бы ее использовать, но во многих случаях фиксация является частью одной или нескольких ветвей, и меня интересует только та, к которой она подключена в последний раз. . - person Tower; 26.02.2012
comment
Команда git log --all --decorate показывает это правильно. Это показывает, что фиксация является частью, например. (HEAD, foo), а не (HEAD, foo, master), хотя он и является частью master. - person Tower; 26.02.2012
comment
Чтобы быть уверенным, что я четко понял то, что вам нужно, можно ли это перефразировать следующим образом: ветка с самой последней подсказкой (т.е. коммит), которая содержит искомый коммит? - person nulltoken; 26.02.2012
comment
Не совсем. :) Добавил к вопросу картинку, которая должна прояснить. - person Tower; 26.02.2012
comment
Например, если вы посмотрите на TortoiseHg, вы увидите, что он делает то, что я хочу: tortoisehg.bitbucket.org /img/vt_history.png (показывает наиболее подходящую ветку для коммита, используя столбец сетки Branch). - person Tower; 26.02.2012
comment
Я далек от того, чтобы быть экспертом Mercurial, но IIRC ChangeSet содержит имя ветки, в которой он был зафиксирован (что может немного помочь TortoiseHg :)). На языке git ветка — это не что иное, как указатель на коммит. Коммит ничего не знает о ветках, к которым он принадлежит. Ветка могла даже быть отброшена после слияния коммита. Если бы у вас был пример, связанный с tortoiseGit, или образец репозитория Git (с ветками!), на который вы могли бы мне указать, это было бы здорово. - person nulltoken; 26.02.2012
comment
Я добавил скриншот того, что хочу, и именно это делает TortoiseGit. Я также связал небольшой пример репозитория, в котором нет ничего особенного (всего две ветки, демонстрирующие это). Я также хотел бы добавить, что git log --decorate --all --graph показывает это так, как я хочу. В основном ваш код отлично подходит для поиска правильных ветвей, но мне все еще нужно выбрать одну правильную ветвь, которая является наиболее подходящей ветвью. Могу ли я просто выбрать первый элемент branches.ElementAt(0)? - person Tower; 26.02.2012
comment
Посмотрите на скриншот TortoiseGit. Коммит 3572... говорит foo, а ваш код дает мне и master, и foo. Мне нужно выяснить, какой из них более актуален (технически коммит действительно есть в обоих). - person Tower; 26.02.2012
comment
давайте продолжим это обсуждение в чате - person nulltoken; 26.02.2012
comment
Это именно то, что я искал, очень признателен! :) - person Tower; 26.02.2012
comment
@nulltoken у меня работает очень медленно (первая версия ListBranchesContainingCommit) или требуется модификация libgit2? - person KindDragon; 16.03.2013
comment
@KindDragon К сожалению, в C# нет лучшей альтернативы. Это всего лишь обходной путь, и он использует revwalk для каждой ветки (что занимает очень много времени). Потребуется изменение в реализации libgit2 C revwalk, чтобы получить выгоду от лежачего полицейского. - person nulltoken; 16.03.2013
comment
Вы можете использовать FindCommonAncestor вместо Commits.QueryBy в libgit2sharp v0.9 и выше. - person KindDragon; 28.01.2014

Git не хранит информацию о ветках вместе с коммитами. Вам нужно будет просмотреть историю DAG и посмотреть, доступен ли коммит из ссылок.

В командной строке с обычным git вы должны запустить git branch --contains $SHA1

person knittl    schedule 26.02.2012
comment
Этот вызов командной строки - это именно то, что я хочу. Теперь, как мне это сделать с LibGit2Sharp? - person Tower; 26.02.2012

Как сказал Knittl, git не хранит эту информацию. Коммит — это фиксированное состояние репозитория с некоторыми метаданными. Поскольку фиксация неизменяема, а ветки, к которым она принадлежит, могут меняться, информация о ветвях не может храниться непосредственно в фиксации.

Итак, чтобы выяснить, действительно ли определенный коммит принадлежит какой-либо ветке, вам нужно просмотреть коммиты этой ветки и сравнить их с одним коммитом.

Чтобы сделать это быстрее, вы можете хранить все коммиты для каждой ветки в HashSet<T> следующим образом:

var branchCommits =
    repo.Branches.Select(
        b => new
             {
                 b.Name,
                 Commits = new HashSet<Commit>(b.Commits)
             })
        .ToArray();

foreach (Commit commit in branch.Commits)
{
    var commitBranches = branchCommits.Where(b => b.Commits.Contains(commit));

    …
}
person svick    schedule 26.02.2012
comment
Остерегайтесь, что это жадно загружает все коммиты каждой ветки в память (что в основном анализирует весь репозиторий). Чтобы быть немного более ресурсоемким, можно положиться на метод Commits.QueryBy(), который использует ходок. - person nulltoken; 26.02.2012