Git
English ▾ 主題 ▾ 最新版本 ▾ git-merge-base 最後更新於 2.43.0

名稱

git-merge-base - 找出合併時盡可能好的共同祖先

概要

git merge-base [-a | --all] <commit> <commit>…​
git merge-base [-a | --all] --octopus <commit>…​
git merge-base --is-ancestor <commit> <commit>
git merge-base --independent <commit>…​
git merge-base --fork-point <ref> [<commit>]

描述

git merge-base 找出兩個提交之間最佳的共同祖先,以便在三向合併中使用。如果一個共同祖先是另一個共同祖先的祖先,則前者比後者更好。沒有任何更好的共同祖先的共同祖先是最佳共同祖先,即合併基礎。請注意,一對提交可能有多個合併基礎。

操作模式

在最常見的特殊情況下,僅在命令列上指定兩個提交意味著計算給定兩個提交之間的合併基礎。

更一般地說,在要計算合併基礎的兩個提交中,一個由命令列上的第一個提交引數指定;另一個提交是一個(可能假設的)提交,它是跨命令列上所有剩餘提交的合併。

因此,如果指定了兩個以上的提交,則合併基礎不一定包含在每個提交引數中。這與使用 --merge-base 選項的 git-show-branch[1] 不同。

--octopus

計算所有提供的提交的最佳共同祖先,以便準備進行 n 向合併。這模擬了 git show-branch --merge-base 的行為。

--independent

不列印合併基礎,而是列印具有相同祖先的所提供提交的最小子集。換句話說,在給定的提交中,列出那些無法從任何其他提交到達的提交。這模擬了 git show-branch --independent 的行為。

--is-ancestor

檢查第一個 <commit> 是否為第二個 <commit> 的祖先,如果為真,則以狀態 0 退出;如果為假,則以狀態 1 退出。錯誤會以非零狀態(非 1)發出訊號。

--fork-point

找出分支(或導致 <commit> 的任何歷史記錄)從另一個分支(或任何參考)<ref> 分叉的時間點。這不僅僅是尋找兩個提交的共同祖先,還考慮了 <ref> 的 reflog,以查看導致 <commit> 的歷史記錄是否從 <ref> 分支的較早版本分叉(請參閱下面對此模式的討論)。

選項

-a
--all

輸出提交的所有合併基礎,而不僅僅是一個。

討論

給定兩個提交 ABgit merge-base A B 將輸出一個可以從 AB 通過父關係到達的提交。

例如,使用此拓撲結構

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

AB 之間的合併基礎是 1

給定三個提交 ABCgit merge-base A B C 將計算 A 和一個假設的提交 M 之間的合併基礎,而 MBC 之間的合併。例如,使用此拓撲結構

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

git merge-base A B C 的結果是 1。這是因為 BC 之間具有合併提交 M 的等效拓撲結構是

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

git merge-base A M 的結果是 1。提交 2 也是 AM 之間的共同祖先,但是 1 是更好的共同祖先,因為 21 的祖先。因此,2 不是合併基礎。

git merge-base --octopus A B C 的結果是 2,因為 2 是所有提交的最佳共同祖先。

當歷史記錄涉及交叉合併時,兩個提交可能有多個最佳共同祖先。例如,使用此拓撲結構

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

12 都是 A 和 B 的合併基礎。沒有一個比另一個更好(兩者都是最佳合併基礎)。當未給出 --all 選項時,未指定輸出哪個最佳選項。

檢查兩個提交 A 和 B 之間「快速前進」的常用慣用語(或至少曾經是)是計算 A 和 B 之間的合併基礎,並檢查它是否與 A 相同,在這種情況下,A 是 B 的祖先。您會在舊腳本中經常看到這種慣用語。

A=$(git rev-parse --verify A)
if test "$A" = "$(git merge-base A B)"
then
	... A is an ancestor of B ...
fi

在現代 Git 中,您可以更直接地說

if git merge-base --is-ancestor A B
then
	... A is an ancestor of B ...
fi

取而代之。

關於分叉點模式的討論

在使用 git switch -c topic origin/master 建立的 topic 分支上工作後,遠端追蹤分支 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。假設您現在想將您在主題上所做的工作基於更新的 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(即您的儲存庫的 reflog 知道的遠端追蹤分支的舊提示),以查看您的主題分支是在哪個提交上建立的,並找到 B0,允許您僅重播主題上的提交,而不包括另一端稍後丟棄的提交。

因此

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

將找到 B0,並且

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

將在 B 之上重播 D0、D1 和 D,以建立這種形狀的新歷史記錄

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

一個注意事項是您儲存庫中較舊的 reflog 條目可能會被 git gc 過期。如果 B0 不再出現在遠端追蹤分支 origin/master 的 reflog 中,則 --fork-point 模式顯然無法找到它並失敗,避免給出隨機且無用的結果(例如 B0 的父節點,就像沒有 --fork-point 選項的相同命令一樣)。

此外,您使用 --fork-point 模式的遠端追蹤分支必須是您的主題從其提示分叉的分支。如果您從提示較舊的提交分叉,則此模式將找不到分叉點(想像一下,在上面的範例歷史記錄中,B0 不存在,origin/master 從 B1 開始,移動到 B2,然後是 B,並且當 origin/master 為 B1 時,您在 origin/master^ 分叉了您的主題;歷史記錄的形狀將與上面相同,沒有 B0,並且 B1 的父節點是 git merge-base origin/master topic 正確找到的,但 --fork-point 模式不會找到,因為它不是曾經位於 origin/master 的提示的提交之一)。

GIT

屬於 git[1] 套件的一部分

scroll-to-top