Здесь важно понимать, что ветви имена на самом деле ничего не значат.
Имя ветви - это просто метка или указатель, указывающий на коммит. Думайте о них как об одной из тех желтых заметок или даже липких стрелок , которые вы можете вставить в коммиты. Это совершает , что имеет значение. Идентификацией коммита является его хеш-идентификатор. Коммиты являются постоянными 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 .