web-dev-qa-db-ja.com

マージコミットのgit show

マージコミットを実行してgit show #commitを実行すると、コミットログのみが表示され、実際の変更の差分は表示されません。

commit c0f50178901e09a1237f7b9d9173ec5d1c4936c
Merge: ed234b ded051
Author: abc
Date:   Mon Nov 21 15:56:33 2016 -0800

    Merge branch 'abc'

私は本当のコミットがマージログにあることを理解していますが、入力を保存したいのですが、差分を1つに表示する方法はありますか?

27
Sohaib Farooqui

TL; DR:git show -m c05f017またはgit show --first-parent c05f017、またはおそらくgit diff c05f017^ c05f017を使用します。


あなたの質問には根本的なエラーがあります:コミットは差分ではありません。コミットはスナップショットです。これは違いのない区別のように思えるかもしれません。一部のコミットでは、isです。ただし、マージコミットの場合、notです。

git show(またはgit log -p)がコミットを表示する場合as diff、それはコミットのスナップショットを他のものと比較するによって表示されます。 git diffコマンドは同じことを行います。1つのコミットを別のコミットと比較します。 (または、コミットをワークツリー、インデックスの内容、または他のいくつかの組み合わせと比較できます。)

通常のコミットの場合、比較対象は自明です:thisコミットのスナップショットとprevious(つまり、親)コミットのスナップショットを比較してください。これがgit showが行うことです(そしてgit log -pも):親コミットからこのコミットまでgit diffを実行します。

ただし、マージコミットには1つの親コミットしかありません。彼らはtwo親を持っています。1 これが、そもそも「マージコミット」となるものです。マージコミットの定義は、少なくとも2つの親を持つコミットです。


1マージコミットには、3つ以上の親を含めることができます。これらは「タコマージ」と呼ばれます。しかし、彼らは特別なことは何もせず、主に自慢するためのものです。 :-)ここでは無視できます。


2つの親がある場合、どちらをgit showと比較する必要がありますか?

git log -pがデフォルトで選択することは、まったく比較しないことです。さまざまなフラグを追加して、何かを表示することができます(以下を参照)。

git showがデフォルトで行うことは、より複雑です。 2つの親があるため、git showは最初に「最初の親」と比較します。2 次に、2番目の親と比較します。次に、この部分は非常に重要です。2つのdiffを結合し、いわゆる「結合diff」を生成します。

次のセクションでは、トリッキーですが非常に便利なGit構文の一部に注目してください。 c05f017のようなコミットIDがある場合、その後にキャレットまたは「帽子」文字^を追加して、親コミットに名前を付けることができます。オプションで別の番号を追加して、which parentを選択できます。通常の(マージではない)コミットの場合、1つしかないので、c05f017^the parentです。マージコミットの場合、c05f017^c05f017^1は両方とも最初の親を意味し、c05f017^22番目の親を意味します。


2最初の親というアイデアはGitで特に重要であるため、引用符で囲みます。これについては後で説明します。言い換えれば、Gitはどの親がfirstであるかを最も重視し、残りは単なる「残り」です。


結合された差分

結合されたdiff形式は ドキュメント で説明されていますが、最初にキービットが here で説明されているため、特にわかりにくくなっています。3

combined diffは、すべての親から変更されたファイルのみをリストすることに注意してください。

つまり、[〜#〜] m [〜#〜]はマージコミットであり、差分M ^ 1 vs [〜#〜] m [〜#〜]は、ファイルmainline.txtcommon.txtの両方が変更されたことを示します。さらに、M ^ 2[〜#〜] m [〜#〜]を比較すると、ファイルsidebranch.txtcommon.txtが両方とも変更されたと仮定します。結合されたdiffはonly common.txtを表示し、mainline.txtsidebranch.txtの両方をスキップします。これら2つのファイルはone parent各)。 (それでも、Gitはcommon.txtの差分の一部のみを表示する場合があります。)


3他のセクションを見続けていると、ドキュメントでこれを見つけるのに長い時間がかかりました。


差分の分割

-mオプション—mはおそらくmergeを表します。実際には、Gitにマージを「分割」します。つまり、各親に対するdiffを1つの大きなdiffに結合しようとする代わりに、each parentに対する差分を一度に1つずつ表示するだけです。

これは時々あなたが望むものです。望んでいない場合は、独自の明示的なgit diffを実行して、2つの親のいずれかと比較するだけです(または以下を参照)。

どちらの親と比較すべきですか?

通常、正しい答えは「最初の親」です。

「最初の親」という概念の鍵は、Gitがマージコミットを行うと、その時点でのブランチを最初の親として常に記録することです。もう1つのブランチが2番目の親になります。

つまり、developを使用しており、topicをマージする場合:

$ git checkout develop
$ git merge topic

Gitは、現在のブランチdevelopで2つの親を持つmerge commitの新しいコミットを作成します。マージコミットのfirst親は、ほんの少し前にdevelopの先端であったコミットになります。 second parentは、(まだ)topicの先端であるコミットになります。

あなたは通常、マージがもたらしたものに関心があるので、最初の親と比較するとそれが得られます。だから通常はそれがあなたが望むものです。このため、git showではgit show --first-parentを実行できます。これはコミットを「分割」し、git showは最初の親に対してのみ差分します。 (これは、コミットを2回分割するgit show -mとは少し異なります。最初の分割は最初の親と比較され、2番目の分割は2番目の親と比較されます。)

同様に、git log -p --first-parent ...を実行できますが、-mを追加してパッチとして変更を確認する必要があります。デフォルトではgit logはマージの差分の表示を完全にスキップするからです。 (内部的には、skipping-because-merge操作は、分割がnot-a-mergeのように振る舞う方法をオーバーライドします。)ここで、--first-parentフラグにはさらに重要な効果があります。ログ操作は見ません- anyサイドブランチのコミットのすべて、メイン(最初の親)行のコミットのみ。

76
torek

ここで述べた のように、これらの解決策には、次のような結合されたdiffの表示が含まれます。

git diff --cc $M $M^1 $M^2 $(git merge-base $M^1 $M^2)

しかし:mergeがrenamesを含む場合、「diff --cc」からの出力は元のパスを表示しませんでした。
Git 2.22(2019年第1四半期)の新しいオプションにより、元のツリーのパスが出力に追加されます。

git diff --cc --combined-all-paths $M $M^1 $M^2 $(git merge-base $M^1 $M^2)

logdiff-tree--combined-all-pathsオプションを追加

名前の変更またはコピーの検出がアクティブな場合でも、マージの結合diff形式は1つのファイル名のみをリストします。

たとえば、生のフォーマットでは次のように表示されます。

::100644 100644 100644 fabadb8 cc95eb0 4866510 MM describe.c
::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM   bar.sh
::100644 100644 100644 e07d6c5 9042e82 ee91881 RR   phooey.c

これは、最初の親にbar.shの元の名前が何であるかを知らせず、phooey.cの元の名前のどちらが親にあったかを知らせません。

対照的に、非マージコミットの場合、rawフォーマットは元のファイル名(およびブートするための名前変更スコア)を提供します。
マージコミットの元のファイル名も提供するには、--combined-all-pathsオプションを追加します(-cまたは--ccで使用する必要があります。またはコピー検出がアクティブになっている)、名前の変更が関係するときにタブ区切りのファイル名を印刷できるようにします。

これにより、上記の出力は次のように変換されます。

::100644 100644 100644 fabadb8 cc95eb0 4866510 MM desc.c  desc.c  desc.c
::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM   foo.sh  bar.sh  bar.sh
::100644 100644 100644 e07d6c5 9042e82 ee91881 RR   fooey.c fuey.c  phooey.c

さらに、パッチ形式では、これによりfrom/toヘッダーが変更されるため、「from」ヘッダーが1つだけではなく、親ごとに1つになります。
たとえば、代わりに

--- a/phooey.c
+++ b/phooey.c

私たちは見るだろう

--- a/fooey.c
--- a/fuey.c
+++ b/phooey.c
0
VonC