Визуализировать зависимость ветки git

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


person Ivan    schedule 04.06.2019    source источник


Ответы (2)


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

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

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

И более конкретно ответить на вопрос «Как узнать, какие ветви объединены в ветвь A?»

git branch --contains A
person RomainValeri    schedule 04.06.2019
comment
Спасибо, хотя я хочу знать только очень конкретную информацию о потомках ветки, как я описал. Если родительская ветка - A, я хочу быстро узнать, в какие ветки была объединена эта ветка, без необходимости сканировать дерево git. Как вы думаете, это возможно из командной строки? - person Ivan; 05.06.2019
comment
О, мальчик, это было так просто, спасибо! Пока я привлек ваше внимание, предположим, что A был объединен с B, а затем B был объединен с C, могу ли я получить аналогичный результат для git branch --contains A, но это показывает, что сначала A был объединен с B, а затем B с C? (в настоящее время он будет показывать как B, так и C отдельно) - person Ivan; 05.06.2019
comment
@Ivan Тогда, может быть, пора подумать об использовании инструмента. gitk на самом деле очень полезен, если вы можете пройти через довольно устаревший интерфейс. Вы пробовали это и исключили по какой-то конкретной причине? - person RomainValeri; 05.06.2019
comment
Не gitk, пробовал несколько других клиентов, но не нашел ни одного, который позволил бы мне сделать это легко (и при попытке gitk этого тоже недостаточно). Спасибо еще раз за помощь. - person Ivan; 05.06.2019

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

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

Сам график определяет фактическую «разветвленность» репозитория:

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

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

В этой части графика показаны две ветви, заканчивающиеся двумя подсказками 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 будет рассматривать как фиксацию подсказкой некоторой обратной цепочки. коммитов. Поскольку Git работает в обратном направлении, начиная с этой подсказки, Git будет проходить все этапы слияния, чтобы найти все коммиты, которых можно достичь, работая в обратном направлении. Поскольку у M есть два родителя C и B, Git посетит фиксацию C, затем A и так далее, а также B, а затем все коммиты слева от B.

Конечно, мы можем оставить метки, указывающие на A и / или B и / или C:

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

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

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

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

С git branch --contains anything that finds some commit 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, операция по преобразованию его в идентификатор хеша выбирает идентификатор хеша фиксации M. Затем операция по преобразованию refs/heads/feature в идентификатор хеша выбирает идентификатор хеша B, и git merge-base --is-ancestor отвечает на вопрос: является ли фиксация B предком фиксации M (и это так). 2

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

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

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

Я думаю, вы хотите, чтобы feature2 был упомянут как "после" feature1, потому что фиксация слияния, которая приводит к подсказке feature2, происходит после фиксации слияния, которая приводит к вершине feature1. 3 Но слияния не t всегда просто объединяйте два коммита. 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 Вам нужно будет написать свой собственный код для создания такого порядка. Обратите внимание, что это не так просто, как сравнение советов по разным ветвям; нам нужно знать, какие слияния, если они есть, приносят эти подсказки, и сравнивать слияния. Мы также должны учитывать ветки, образованные за счет того, что их кончик встроен в подграф без объединения y:

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

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

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

person torek    schedule 05.06.2019
comment
Я знал основы этого, но ваше расширенное (и потрясающее) объяснение помогло мне понять намного лучше, особенно то, что ветки являются просто указателями. Придется пересмотреть свой рабочий процесс вокруг этого. Спасибо! - person Ivan; 05.06.2019