Визуализировать git-ветвь зависимости - PullRequest
2 голосов
/ 05 июня 2019

Есть ли инструмент, который может показать мне, в какие ветви была объединена конкретная ветка? Например, если «A» объединен с «B» и «C», но не «D», как вывести «B» и «C»? Мне нужны только названия веток.

Ответы [ 2 ]

4 голосов
/ 05 июня 2019

Здесь важно понимать, что ветви имена на самом деле ничего не значат.

Имя ветви - это просто метка или указатель, указывающий на коммит. Думайте о них как об одной из тех желтых заметок или даже липких стрелок , которые вы можете вставить в коммиты. Это совершает , что имеет значение. Идентификацией коммита является его хеш-идентификатор. Коммиты являются постоянными 1 и не могут быть изменены, и каждый коммит записывает хэш-идентификатор своего непосредственного родителя (большинство коммитов) или родителей (2 или более для коммита слияния). Это то, что создает граф коммитов , который - за исключением того факта, что вы всегда можете добавить к нему, является таким же постоянным и неизменным, как и сами коммиты.

Сам график определяет фактическую "ветвистость" хранилища:

(older commits towards the left, newer ones towards the right)

          o--A
         /
...--o--o
         \
          o--o--B

Эта конкретная часть графика показывает две ветви, оканчивающиеся на два tip commit A и B. Добавление нового коммита после A дает нам C, который запоминает A в качестве родителя:

          o--A--C
         /
...--o--o
         \
          o--o--B

Если мы теперь добавим коммит слияния - назовем его M для "слияния" - чьи родители оба C и B получаем:

          o--A--C
         /       \
...--o--o         M
         \       /
          o--o--B

Для запоминания A и B или обоих C и B требуется две метки - две стрелки-липучки, но теперь, когда у нас есть M, мы можем избавиться от одной из эти метки и просто держите одну стрелку заметки, указывающую на M:

          o--A--C
         /       \
...--o--o         M   <-- branch
         \       /
          o--o--B

Так вот, что такое имя ветки и что оно делает: это указатель, указывающий на один единственный коммит, который Git будет считать tip коммитом некоторых обращенных назад цепочка коммитов. Поскольку Git работает в обратном направлении, начиная с этого коммитного наконечника, Git пересекает все ветви слияния, чтобы найти все коммиты, которые могут быть достигнуты при работе в обратном направлении. Поскольку у M есть два родителя C и B, Git посетит commit C, затем A и т. Д., А также B, а затем все коммиты слева от B.

Мы можем, конечно, пометить метки на A и / или B и / или C:

                .... <-- tag: v1.0
               .
              .
          o--A--C
         /       \
...--o--o         M   <-- branch
         \       /
          o--o--B   <-- feature

Как и на этом рисунке, ярлыки не обязательно должны содержать ответвления . Особенность имени ветки заключается в том, что при включении ветки, используя git checkout <em>name</em>, любой сделанный нами новый коммит автоматически обновит имя, чтобы указать на новый коммит. Новый коммит будет указывать на предыдущий коммит.

Это понятие стоит и за git branch --contains. Поскольку фиксация B достижима из фиксация M, начиная с M и возвращаясь назад на один шаг, git branch --contains <em>specifier-for-<code>B напечатает имя branch. Это имя указывает на M, а M достигает B. В общем, Git неоднократно задает и отвечает на вопрос:

  • является коммитом X предком коммита Y?

С git branch --contains <em>anything that finds some commit</em>, Git задает вопрос о каждом коммите с веткой-подсказкой для каждого имени ветки:

target=$(git rev-parse $argument) || exit 1

git for-each-ref --format='%(refname)' refs/heads |
    while read fullbranchname; do
        branchtip=$(git rev-parse $fullbranchname)
        if git merge-base --is-ancestor $branchtip $target; then
            echo "branch ${fullbranchname#refs/heads/} contains $argument"
        fi
    done

Если $argument является именем ветви, таким как branch, операция по преобразованию его в хеш-идентификатор выбирает хеш-идентификатор commit M. Затем операция по превращению refs/heads/feature в хеш-идентификатор выбирает хеш-идентификатор B, и git merge-base --is-ancestor отвечает на вопрос - это commit B предок commit M (и это ). 2

То, что вы спрашиваете, немного сложнее: вы хотите, чтобы для каждого имени ветви не только определялось , передается ли коммит-подсказка этой ветки перед коммитом-подсказкой какой-то выбранной ветки (как git branch --contains делает), но также для каждого такого имени ветви, которые являются более-предками-y, а какие - менее-предками-y . На этот вопрос можно ответить, но только при некоторых обстоятельствах .

Рассмотрим, например, этот график:

...--o--o--o--o---M1--M2-o   <-- master
      \     \    /   /
       \     o--o   /  <-- feature1
        \          /
         o--o--o--o   <-- feature2

я-йчернила, которые вы хотите, чтобы feature2 были упомянуты как "после" feature1, потому что коммит слияния, который вводит кончик feature2, идет после коммита слияния, который вводит кончик feature1. 3 Но слияния не всегда просто объединяют два коммитов. 4 Рассмотрим:

          o--o--o   <-- feature1
         /       \
...--o--o--o--o---M1--o   <-- master
         \       /
          o--o--o   <-- feature2

Теперь нет очевидного порядка: feature1 и feature2 оба были введены в одно и то же время одним коммитом.

(Конечно, если мы удалим одно из двух feature имен, это решит проблему. Если мы удалим оба имен, это еще более-решаемо! ?)


1 Вы можете удалить коммит, удалив весь доступ к коммит.Git начинается с известных имен - имен веток и тегов и всех других форм ссылок - и работает в обратном направлении по всему графику.Если эта обратная работа достигает коммита, коммит должен остаться.Если нет, коммит имеет право на удаление.Существуют и другие имена, помимо очевидных, которые обычно поддерживают коммиты в течение как минимум дополнительных 30 дней.

2 Мы можем исключить вызовы git rev-parse, потому что git merge-base принимаетназвание ветви вместо хеш-идентификатора.Когда ему дается имя, он просто находит хеш коммита, так же как и git rev-parse.Я положил их в первую очередь для иллюстрации.

3 Вам придется написать свой собственный код для создания такого рода порядка.Обратите внимание, что это не так просто, как сравнение коммитов из разных веток;нам нужно знать, какие слияния, если таковые имеются, ввести эти советы и сравнить слияния.Мы также должны учитывать ответвления, сформированные путем встраивания их наконечника в подграф non-merge-y:

             ..... <-- branch3
            .
...--o--o--o--o    <-- branch1
         .
          ........ <-- branch2

Здесь, я думаю, мы хотим заявить branch2 < branch3.

4 Даже в ситуации слияния пар в одно и то же время вы можете многократно объединять некоторые коммиты с какой-либо основной линией, что затрудняет вопрос (есть возможных заказов, но вынеобходимо выбрать алгоритм обхода графа, чтобы выбрать общий порядок здесь).По сути, помните, что когда вы работаете с DAG , вы имеете дело с poset .

3 голосов
/ 05 июня 2019

Если вы имеете в виду графический инструмент, gitk может показаться хорошей отправной точкой.

В качестве альтернативы, вы можете предпочесть CLI, который очень полезен для некоторых нужд, попробуйте:

git log --oneline --decorate --simplify-by-decoration --graph --all

А конкретнее задать вопрос "Как узнать, какие ветви объединены в ветвь А?"

git branch --contains A
...