Есть ли инструмент, который может показать мне, в какие ветки была объединена конкретная ветка? Например, если «A» был объединен с «B» и «C», но не «D», как мне вывести «B» и «C»? Мне нужны только названия веток.
Визуализировать зависимость ветки git
Ответы (2)
Если вы имеете в виду графический инструмент, gitk
может быть хорошей отправной точкой.
В качестве альтернативы вы можете предпочесть CLI, который очень полезен для некоторых нужд, попробуйте:
git log --oneline --decorate --simplify-by-decoration --graph --all
И более конкретно ответить на вопрос «Как узнать, какие ветви объединены в ветвь A?»
git branch --contains A
git branch --contains A
, но это показывает, что сначала A был объединен с B, а затем B с C? (в настоящее время он будет показывать как B, так и C отдельно)
- person Ivan; 05.06.2019
gitk
на самом деле очень полезен, если вы можете пройти через довольно устаревший интерфейс. Вы пробовали это и исключили по какой-то конкретной причине?
- person RomainValeri; 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 sup >
То, что вы спрашиваете, немного сложнее: вы хотите, чтобы для каждого имени ветки не только определялось, происходит ли фиксация подсказки этой ветки перед фиксацией подсказки некоторой выбранной ветки (как это делает 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.