Русский ▾ Topics ▾ Latest version ▾ git-merge-base last updated in 2.43.0

НАЗВАНИЕ

git-merge-base - Поиск настолько хороших общих предков для слияния, на сколько возможно

ОБЗОР

git merge-base [-a | --all] <коммит> <коммит>…​
git merge-base [-a | --all] --octopus <коммит>…​
git merge-base --is-ancestor <коммит> <коммит>
git merge-base --independent <коммит>…​
git merge-base --fork-point <ссылка> [<коммит>]

ОПИСАНИЕ

git merge-base находит наилучшего(их) общего(их) предка(ов) между двумя коммитами для использования в трёхстороннем слиянии. Один общий предок «лучше» другого общего предка, если последний является предком первого. Общий предок, у которого нет никакого лучшего общего предка, является «наилучшим общим предком», т.е. «базой слияния» (merge base). Обратите внимание, что для пары коммитов может быть более одной базы слияния.

РЕЖИМЫ РАБОТЫ

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

В более общем смысле, среди двух коммитов, для которых вычисляется база слияния, один задаётся первым аргументом-коммитом в командной строке; другой коммит — это (возможно, гипотетический) коммит, который является слиянием всех остальных коммитов в командной строке.

Как следствие, «база слияния» не обязательно содержится в каждом из аргументов-коммитов, если указано более двух коммитов. Это отличается от git-show-branch[1] при использовании с параметром --merge-base.

--octopus

Вычисляет наилучших общих предков всех предоставленных коммитов, готовясь к n-стороннему слиянию. Это имитирует поведение git show-branch --merge-base.

--independent

Вместо вывода баз слияния выводит минимальное подмножество предоставленных коммитов с теми же предками. Другими словами, среди указанных коммитов перечисляет те, которые не могут быть достигнуты из какого-либо другого. Это имитирует поведение git show-branch --independent.

--is-ancestor

Проверяет, является ли первый <коммит> предком второго <коммита>, и завершается с кодом 0, если это так, или с кодом 1, если нет. Ошибки сигнализируются ненулевым кодом, отличным от 1.

--fork-point

Находит точку, в которой ветка (или любая история, ведущая к <коммиту>) ответвилась от другой ветки (или любой ссылки) <ссылка>. Это не просто ищет общего предка двух коммитов, но также учитывает журнал ссылок (reflog) <ссылка>, чтобы увидеть, ответвилась ли история, ведущая к <коммиту>, от более ранней инкарнации ветки <ссылка> (см. обсуждение этого режима ниже).

ПАРАМЕТРЫ

-a
--all

Выводит все базы слияния для коммитов, а не только одну.

ОБСУЖДЕНИЕ

Для двух коммитов A и B git merge-base A B выведет коммит, который достижим из обоих A и B через связь «родитель».

Например, при такой топологии:

	 o---o---o---B
	/
---o---1---o---o---o---A

базой слияния между A и B является 1.

Для трёх коммитов A, B и C git merge-base A B C вычислит базу слияния между A и гипотетическим коммитом M, который является слиянием между B и C. Например, при такой топологии:

       o---o---o---o---C
      /
     /   o---o---o---B
    /   /
---2---1---o---o---o---A

результатом git merge-base A B C является 1. Это потому, что эквивалентная топология с коммитом слияния M между B и C такова:

       o---o---o---o---o
      /                 \
     /   o---o---o---o---M
    /   /
---2---1---o---o---o---A

и результатом git merge-base A M является 1. Коммит 2 также является общим предком между A и M, но 1 — лучший общий предок, потому что 2 является предком 1. Следовательно, 2 не является базой слияния.

Результатом git merge-base --octopus A B C является 2, потому что 2 — наилучший общий предок всех коммитов.

Когда история включает перекрёстные слияния, для двух коммитов может быть более одного «наилучшего» общего предка. Например, при такой топологии:

---1---o---A
    \ /
     X
    / \
---2---o---o---B

и 1, и 2 являются базами слияния A и B. Ни одна из них не лучше другой (обе являются «наилучшими» базами слияния). Когда параметр --all не указан, не определено, какая из наилучших будет выведена.

Распространённая идиома для проверки «перематываемости» (fast-forward-ness) между двумя коммитами A и B заключается (или, по крайней мере, раньше заключалась) в вычислении базы слияния между A и B и проверке, совпадает ли она с A, и в этом случае A является предком B. Вы часто будете видеть эту идиому в старых сценариях.

A=$(git rev-parse --verify A)
if test "$A" = "$(git merge-base A B)"
then
	... A является предком B ...
fi

В современном git вы можете сказать это более прямым способом:

if git merge-base --is-ancestor A B
then
	... A является предком B ...
fi

вместо этого.

Обсуждение режима fork-point

После работы над веткой topic, созданной с помощью git switch -c topic origin/master, история отслеживаемой внешней ветки origin/master могла быть перемотана назад и перестроена, что привело к истории такой формы:

		 o---B2
		/
---o---o---B1--o---o---o---B (origin/master)
	\
	 B0
	  \
	   D0---D1---D (topic)

где origin/master раньше указывал на коммиты B0, B1, B2, а теперь указывает на B, и ваша ветка topic была начата поверх него, когда origin/master был на B0, и вы построили три коммита, D0, D1 и D, поверх него. Представьте, что теперь вы хотите переместить (rebase) работу, которую вы сделали в теме, поверх обновлённого origin/master.

В таком случае git merge-base origin/master topic вернул бы родителя B0 на рисунке выше, но B0^..D не является диапазоном коммитов, который вы хотели бы воспроизвести поверх B (он включает B0, который не является вашим изменением; это коммит, который другая сторона отбросила, когда переместила свою верхушку с B0 на B1).

git merge-base --fork-point origin/master topic предназначен для помощи в таком случае. Он учитывает не только B, но также B0, B1 и B2 (т.е. старые верхушки отслеживаемых внешних веток, о которых знает журнал ссылок вашего репозитория), чтобы увидеть, на каком коммите была основана ваша ветка topic, и находит B0, позволяя вам воспроизвести только коммиты вашей темы, исключая коммиты, которые другая сторона позже отбросила.

Следовательно

$ fork_point=$(git merge-base --fork-point origin/master topic)

найдёт B0, и

$ git rebase --onto origin/master $fork_point topic

воспроизведёт D0, D1 и D поверх B, создав новую историю такой формы:

		 o---B2
		/
---o---o---B1--o---o---o---B (origin/master)
	\                   \
	 B0                  D0'--D1'--D' (topic - обновлённая)
	  \
	   D0---D1---D (topic - старая)

Предостережение заключается в том, что более старые записи журнала ссылок в вашем репозитории могут быть устаревшими из-за git gc. Если B0 больше не появляется в журнале ссылок отслеживаемой внешней ветки origin/master, режим --fork-point, очевидно, не может его найти и завершается ошибкой, избегая выдачи случайного и бесполезного результата (например, родителя B0, который выдаёт та же команда без параметра --fork-point).

Кроме того, отслеживаемая внешняя ветка, с которой вы используете режим --fork-point, должна быть той, от которой ваша тема ответвилась от её верхушки. Если вы ответвились от более старого коммита, чем верхушка, этот режим не найдёт точку ответвления (представьте, что в приведённой выше примере истории B0 не существовало, origin/master начинался с B1, переместился на B2, а затем на B, и вы ответвили свою тему на origin/master^, когда origin/master был B1; форма истории была бы такой же, как выше, без B0, и родитель B1 — это то, что git merge-base origin/master topic правильно находит, но режим --fork-point не найдёт, потому что это не один из коммитов, которые раньше были на верхушке origin/master).

GIT

Является частью пакета git[1]