фиксирует в Git форму направленного ациклического графа или DAG.
В любом графе у нас есть список смежности (поскольку граф определяется как G = ( V , E ), где V и E - наборы вершин и ребер соответственно): любая заданная вершина / узел в графе находится либо на расстоянии одного шага от другой вершины, через какое-либо ребро, либо дальше или не связана в все:
C--D
/
B------E G--H
\ /
A F
Узел A подключается к B (и наоборот), B подключается к C и E и так далее. G и H соединяются друг с другом, но не с остальной частью графа, поэтому этот граф состоит из двух непересекающихся подграфов. (Эти ребра не имеют направления.)
На графике , направленном , существует понятие предшественник и преемник , поскольку каждое соединение имеет стрелку:
A->B->C
|
v
D
Здесь A соединяется с B, а B соединяется с C и D. Таким образом, B является преемником A, а C и D являются преемниками B (но не связаны друг с другом). С точки зрения предшественников, A является предшественником B, а B является предшественником как C, так и D.
Если график не имеет циклов - это ациклический - мы можем выполнить операцию транзитивного замыкания, используя операции предшественника / преемника. График Git направлен и ациклически - это DAG - так что мы можем сделать это с Git.
Внутри Git стрелки на самом деле задом наперед (по причинам реализации), но мы все еще выполняем то же транзитивное замыкание. Когда мы делаем это, мы обнаруживаем, что A является предком B, C и D:
A <-B <-C
^
|
D
потому что мы можем начать с любого из этих коммитов и вернуться обратно к A. B является предком C и D. Противоположное отношение потомок: D является потомком A.
Здесь нужно быть немного осторожнее. Между C и D. нет отношений предков или потомков, то есть C не является предком D, но C также не является потомком D. Вы не можете просто сказать «не предок, следовательно, потомок»: эти двое могут просто не относиться друг к другу. Это также верно для некоторых коммитов, если DAG имеет непересекающиеся подграфы, что допускается:
A <-B <-C <-- master1
D <-E <-- master2
Хотя D является предком E, D не имеет отношения ни к одному из A, B или C.
В любом случае команда Git, которая проверяет «предок»:
git merge-base --is-ancestor
, который принимает два хеш-идентификатора коммита (или других спецификаторов коммита) и отвечает как истинный или ложный запрос (через состояние выхода с 0, означающим «да, является предком»), вопрос: является ли коммит, названный первым Аргумент предок коммита, названного вторым. То есть:
hash1=$(git rev-parse $thing1^{commit}) || die "$thing1 does not name a git commit"
hash2=$(git rev-parse $thing2^{commit}) || die "$thing2 does not name a git commit"
if git merge-base --is-ancestor $hash1 $hash2; then
echo "$thing1 is an ancestor of $thing2"
else
if git merge-base --is-ancestor $hash2 $hash1; then
echo "$thing1 is a descendant of $thing2"
else
echo "$thing1 and $thing2 are not relatable"
fi
fi
Ответ «не относящийся к делу» возникает потому, что, как мы видели выше, мы получаем только частичный заказ , а не общий заказ от нашего предшественника / понятие преемника.
(Для целей Git коммит является собственным предком, поэтому git merge-base --is-ancestor $hash1 $hash1
всегда верен.)