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 は、3者マージで使用する2つのコミット間の最適な共通の祖先を検索します。ある共通の祖先が別の共通の祖先よりも優れているのは、後者が前者の祖先である場合です。より優れた共通の祖先を持たない共通の祖先は、最良の共通の祖先、つまりマージベースです。2つのコミットに対して複数のマージベースが存在する場合があることに注意してください。

動作モード

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

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

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

--octopus

n-way マージの準備として、提供されたすべてのコミットの最良の共通の祖先を計算します。これは 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 と、BC の間のマージである仮想コミット 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 の「fast-forward-ness」をチェックする一般的なイディオムは、(あるいは少なくともかつては)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 の3つのコミットを構築しました。ここで、topic で行った作業を更新された origin/master の上にリベースしたいと想像してください。

このような場合、git merge-base origin/master topic は上記の図の B0 の親を返しますが、B0^..D は B の上にリプレイしたいコミットの範囲では**ありません**(これはあなたが書いたものではない B0 を含んでいます。これは、他の側が tip を B0 から B1 に移動したときに破棄したコミットです)。

git merge-base --fork-point origin/master topic は、そのような場合に役立つように設計されています。これは B だけでなく、B0、B1、B2(つまり、あなたのリポジトリのリフロッグが知っているリモートトラッキングブランチの古い tip)も考慮に入れて、あなたの topic ブランチがどのコミットの上に構築されたかを確認し、B0 を見つけます。これにより、他の側が後で破棄したコミットを除いて、あなたの topic 上のコミットのみをリプレイすることができます。

したがって、

$ 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 モードは明らかにそれを見つけることができず、失敗します。これは、ランダムで役に立たない結果(--fork-point オプションなしの同じコマンドが返す B0 の親のようなもの)を与えるのを避けるためです。

また、--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