複数のツリーをマージする、続き

残念なことに、多くのマージは自明ではありませんん。 追加や移動、削除されたファイルがある場合や、あるいは両方のブランチ で同じファイルが変更されている場合、"マージエントリ" を含んだ 索引ツリーが残されます。このような索引ツリーは tree オブジェクトには書き込むことができません。その結果を書き込む前に 他のツールを使用してコンフリクトを解消する必要があります。

このような索引状態は git ls-files —unmerged を 用いると確認できます。例えば:

$ git read-tree -m $orig HEAD $target
$ git ls-files --unmerged
100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1       hello.c
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2       hello.c
100644 cc44c73eb783565da5831b4d820c962954019b69 3       hello.c

git ls-files —unmerged の出力の各行は blob のモードビット、 blob の SHA-1、stage番号'、そしてファイル番号で始まります。 'stage番号 はそれがどの tree から来たかを表す git のやり方です: stage 1 は $orig ツリーに対応し、stege 2 は HEAD の tree stage 3 $target tree です。

既に自明のマージは git read-tree -m にて行なわれたことを 説明しました。例えば、ファイルが HEAD と $target` のどちらでも 変更されていない場合や、HEAD$target が同じ内容の場合は 明らかに最終的な結果は HEAD のものと同じです。 上記の例では hello.cHEAD で変更されていて $target`でも 別の変更が行なわれている場合です。 好きな3方向マージツールを実行してこれを解決することができます。 例えば `diff3`、`merge あるいは git 自身の merge-file などを使用し これら3つのstage 上のファイルを次のようにしてマージします。

$ git cat-file blob 263414f... >hello.c~1
$ git cat-file blob 06fa6a2... >hello.c~2
$ git cat-file blob cc44c73... >hello.c~3
$ git merge-file hello.c~2 hello.c~1 hello.c~3

これはマージ結果は hello.c~2 ファイルに保存し、 コンフリクトがあるときは、コンフリクトマーカが付けられます。 マージ結果を確認した後、このファイルの最終的なマージ結果を git に次のようにして伝えます:

$ mv -f hello.c~2 hello.c
$ git update-index hello.c

ファイルにマージ済みになっていない時は、git update-index は ファイルに解決済みのマークを付けます。

上記は下位レベルにおける git のマージ動作の解説で、 水面下でどのようなことが起きているかの概念を理解する手助けになります。 実際、誰も、git 自身でさえ、git cat-file を3回も実行しません。 git merge-index プログラムがあり、これが stage を一時ファイルとして 抽出し、"merge" スクリプトをコールします:

$ git merge-index git-merge-one-file hello.c

そして、上位レベルとして git merge -s resolve が実装されています。