日本語 ▾ トピック ▾ 最新バージョン ▾ 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 は、3者間マージで使用する、2つのコミット間の最良の共通の祖先を見つけます。ある共通の祖先が別の共通の祖先の祖先である場合、前者は後者より優れています。より優れた共通の祖先を持たない共通の祖先は、最良の共通の祖先、すなわちマージベースです。1組のコミットに対して複数のマージベースが存在する可能性があることに注意してください。

操作モード

最も一般的な特殊なケースでは、コマンドラインで2つのコミットのみを指定すると、指定された2つのコミット間のマージベースを計算することを意味します。

より一般的には、マージベースを計算する2つのコミットのうち、1つはコマンドラインの最初のコミット引数で指定され、もう1つのコミットは、コマンドライン上の残りのすべてのコミットをマージした (おそらく仮定の) コミットです。

結果として、2つ以上のコミットが指定された場合、マージベースが各コミット引数に必ずしも含まれるわけではありません。これは、--merge-base オプションを指定して使用される git-show-branch[1] とは異なります。

--octopus

提供されたすべてのコミットの最良の共通の祖先を計算し、N方向マージの準備をします。これは git show-branch --merge-base の動作を模倣します。

--independent

マージベースを出力する代わりに、同じ祖先を持つ提供されたコミットの最小限のサブセットを出力します。言い換えれば、与えられたコミットの中で、他のどのコミットからも到達できないものをリストします。これは git show-branch --independent の動作を模倣します。

--is-ancestor

最初の <commit> が2番目の <commit> の祖先であるかどうかを確認し、真であればステータス0で、そうでなければステータス1で終了します。エラーは1以外の非ゼロステータスで通知されます。

--fork-point

ブランチ (または <commit> に至る任意の履歴) が別のブランチ (または任意の参照) <ref> から分岐した時点を見つけます。これは単に2つのコミットの共通の祖先を探すだけでなく、<ref> のリフロッグも考慮に入れ、<commit> に至る履歴がブランチ <ref> の以前の段階から分岐したかどうかを確認します (このモードの議論については下記を参照)。

オプション

-a
--all

コミットのすべてのマージベースを出力します。1つだけではありません。

議論

2つのコミット AB が与えられた場合、git merge-base A B は、親関係を通じて AB の両方から到達可能なコミットを出力します。

例えば、このトポロジーでは

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

AB の間のマージベースは 1 です。

3つのコミット ABC が与えられた場合、git merge-base A B C は、A と、B および C の間のマージである仮想的なコミット M とのマージベースを計算します。例えば、このトポロジーでは

       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 です。コミット 2AM の共通の祖先ですが、1 はより優れた共通の祖先です。なぜなら、21 の祖先だからです。したがって、2 はマージベースではありません。

git merge-base --octopus A B C の結果は 2 です。なぜなら、2 がすべてのコミットの最良の共通の祖先だからです。

履歴に交差マージが含まれる場合、2つのコミットに対して複数の最良の共通の祖先が存在する可能性があります。例えば、このトポロジーでは

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

12 の両方が A と B のマージベースです。どちらか一方が他方よりも優れているわけではありません(両方とも最良のマージベースです)。--all オプションが指定されていない場合、どの最良のマージベースが出力されるかは未指定です。

2つのコミット 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

代わりに。

fork-pointモードに関する議論

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 にあった頃にその上に開始され、その上に3つのコミット D0, D1, D を作成しました。今、topic で行った作業を更新された 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 (つまり、あなたのリポジトリのリフロッグが知っているリモート追跡ブランチの古いティップ) も考慮に入れて、あなたのトピックブランチがどのコミットの上に構築されたかを確認し、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 - updated)
	  \
	   D0---D1---D (topic - old)

注意点として、リポジトリ内の古いリフロッグエントリは git gc によって期限切れになることがあります。もし B0 がリモート追跡ブランチ origin/master のリフロッグに表示されなくなった場合、--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 のティップにかつてあったコミットの1つではないからです)。

GIT

git[1] スイートの一部

scroll-to-top