web-dev-qa-db-ja.com

Gitのリベース全体の違いを比較する

ブランチfoomasterにリベースし、競合があるとします。競合の解決中に、追加の変更を導入したり、変更を失ったりして(競合の解決に適切なものを除く)、誤ってfooのコンテンツを損傷しないようにしたいと思います。私はこれを次の方法で行いました:

diff -u <(git diff `git merge-base master foo@{1}` foo@{1}) \
        <(git diff `git merge-base master foo    ` foo    )

(更新:または、今思い出した...の同等のgit-diff構文:)

diff -u <(git diff master...foo@{1}) <(git diff master...foo) | mate

これは、master..fooをパッチと見なすに発生したすべての変更を示しています。これは、最小限であることを確認したいものです。ただし、呼び出しは複雑であり、出力の解釈は完全に簡単ではありません。

このタスクを実行するためのより良い方法はありますか—同じ情報を提供しますが、より良い方法または形式を使用します—または、上記をスクリプトでラップする必要がありますか?

31
Kevin Reid

私たちが本当に示したいのは、(a)から(b)にマージしたかのように生成される競合結合差分です。ここで、(a)は以前は(upstream-old)に基づいており、(b)は現在(upstream-new)に基づいています。

まっすぐな差分だけは必要ありません。

代わりに、基本的にマージを実行できますが、結果のツリーを強制的に$ b ^ {tree}にします。これは、必要なものの正しい「終点」であることがすでにわかっています。

多かれ少なかれ、私たちが持っていると仮定しましょう

newcommit -> the new version of the series
oldcommit -> the old version of the series
upstream -> the (new) version of the base of the series

を介してマージを生成できます

git commit-tree newcommit^{tree} -p oldcommit -p upstream -m "message"

次に、「git show」で結果を表示します。これにより、必要なすべてのビットを競合解決として表示する結合​​diff形式が生成され、実際には競合解決の一部ではなかった変更が自動的に無視されます。

これは単に変更を修正する場合にも機能し、生成されたマージコミットに正確な作成者とコミットのタイムスタンプがあり、複数の呼び出しで一貫していることを確認するためにもう少し作業を行うことができます(are緩い参照をオブジェクトデータベースに保存する)。

残念ながら、実際にツリーを生成せずに、「gitdiff」を同じ方法でdiffする方法をまだ理解できていません。その点に到達するためにどの引数を渡さなければならないかわかりません。

6
Jake

マスターとリベースされたfooの違い:

git diff master..foo

vs.

マスターから分岐した後のリベース前のfooの違い(3つのドットに注意):

git diff master...foo@{1}

3
user1338062

git-cherryは、あるブランチで別のブランチにないコミットを検索します。あなたはそれを次のように使うでしょう:

git cherry -v OLDTIP TIP UPSTREAM

つまり、OLDTIPにあるが、UPSTREAM..TIPにはないコミットを探します。パッチの署名を調べてパッチが含まれているかどうかを確認するため、リベース中にパッチが追加、削除、または変更された場合は、リストに表示されます。変更せずに適用されたものは表示されません。

OLDTIPに戻ってgit rebase -i TIPを実行すると、同じロジックを使用してコミットのリストにデータが入力されるため、同様の効果が得られます。

2
Thomas Leonard

これはリベースであり、マージではないため、マージの前にfooをそれ自体と比較することをお勧めします。正しく思い出せば、foo@{1}fooの現在のコミットの親を生成しますが、これはあなたが探しているものではないかもしれません。

次のようなことができると思います(git gcを実行していないと仮定します)。

リベース後のブランチfoo

$ git reflog

これにより、ブランチのHEADがどのように移動されたかがわかります。次のようなレコードが表示されます(インタラクティブにリベースするかどうかによって異なります)。

64d3c9e HEAD@{15}: rebase -i (finish): returning to refs/heads/master
64d3c9e HEAD@{16}: rebase -i (fixup): Fixes external dependencies
049169e HEAD@{17}: rebase -i (fixup): updating HEAD
d4c2e69 HEAD@{18}: rebase -i (pick): Fixes external dependencies
049169e HEAD@{19}: rebase -i (fixup): Imports futures
e490bed HEAD@{20}: rebase -i (fixup): updating HEAD
...
etc
...

マージ前のfooの最後のコミットを楽しみにしています。あなたが何をしたかに応じて、これは難しいかもしれませんし、そうでないかもしれません。そのため、do-itスクリプトを提供していません。

リベースの前にfooの最後のコミットがあるコミットIDを取得し、そのコミットIDと比較します。コミットIDが次のようになっているとします:XXXX

 git diff XXXX...foo

多分それはあなたが望むものです。

2
manu

少し異なるアプローチ:Gitは、マージの場合にこれを表示するための優れたサポートを提供します。つまり、「親2では説明できない親1と比較して何が変わったのですか?」

リベースのサポートを活用する方法はいくつかあります。

オプション1:

  1. (リベースではなく)最初に使い捨てマージを実行します。次に、通常のマージ表示に、マージの変更内容が表示されます。これがあなたが望んでいたものであることを確認してください。

  2. 戻ってリベースし、リベースされたヒントをマージ結果と比較します。これらは同一である必要があります。

オプション2(リベースがマージよりも簡単な場合):

  1. リベースを行います。
  2. グラフトを使用して、ローカル/一時的にマージのように見せます。

これを行うには、1行で.git/info/graftsを作成します。

TIP UPSTREAM OLDTIP

ここで、TIPはリベースされたブランチのコミットIDであり、他は2つの必要な親です(つまり、マージを行っていた場合にマージした2つのもの)。次に、それが実際のマージであるかのように調べます。

自動化についてはよくわかりません。 (右クリックメニューを使用して)適切なリビジョンを簡単に比較できるため、私はgitkを開いた状態でこのようなことを行う傾向があります。

2つのdiffを本当に比較したい場合は、interdiff(1)を確認することをお勧めしますが、異なるベースファイルにどれだけうまく対処できるかはわかりません。

1
Thomas Leonard

interdiffよりもさらに優れており、Git 2.19(2018年第3四半期)ではgit range-diffがあります。
Gitdiff-2つの互いに素なリビジョン範囲 」を参照してください

git range-diffドキュメントには、次の例 が含まれています。

リベースでマージの競合を解決する必要がある場合は、直後にリベースによって導入された変更を比較します

$ git range-diff @{u} @{1} @

git range-diffの一般的な出力は次のようになります。

------------
-:  ------- > 1:  0ddba11 Prepare for the inevitable!
1:  c0debee = 2:  cab005e Add a helpful message at the start
2:  f00dbal ! 3:  decafe1 Describe a bug
    @@ -1,3 +1,3 @@
     Author: A U Thor <[email protected]>

    -TODO: Describe a bug
    +Describe a bug
    @@ -324,5 +324,6
      This is expected.

    -+What is unexpected is that it will also crash.
    ++Unexpectedly, it also crashes. This is a bug, and the jury is
    ++still out there how to fix it best. See ticket #314 for details.

    Contact
3:  bedead < -:  ------- TO-UNDO
------------

この例では、3つの古いコミットと3つの新しいコミットがあり、開発者は次のことを行います。

  • 3番目を削除し、
  • 最初の2つの前に新しいものを追加し、
  • 2番目のコミットのコミットメッセージとその差分を変更しました。

出力が端末に送られるとき、通常のgit diffの出力と同じように、デフォルトで色分けされます。さらに、最初の行(コミットの追加)は緑、最後の行(コミットの削除)は赤、2番目の行(完全一致)はgit showの出力のコミットヘッダーのように黄色で、 3行目は、古いコミットを赤、新しいコミットを緑、残りをgit showのコミットヘッダーのように色付けします。


Git 2.20では、新しい種類の(範囲)差分の色がより適切にサポートされます

commit 2543a64commit 8d5ccb5commit 7648b79 (2018年8月17日)を参照してください、および commit 4441067commit f103a6fcommit 29ef759commit 017ac45commit 9d1e1​​6bcommit 84120cccommit c5e64cacommit 991eb4f (14 Aug 2018)by Stefan Beller(stefanbeller
Junio C Hamano --gitster- in commit 30035d1 、2018年9月17日)

range-diff:コンテキストとして特別な行をインデントする

範囲差分の色付けは、新しいファイルと古いファイルを+++---で示すなど、差分の特別な行に関しては少しあいまいです。最初の文字を取得して解釈するためです。通常の差分のように煩わしいように見えるその色付けは、DIFF_METAINFOを介して太字で色付けされています。

これらの行を空白でインデントすることにより、範囲diffシリーズ自体の例として、はるかに便利なコンテキストとして扱われます。

git range-diff pr-1/dscho/branch-diff-v3...pr-1/dscho/branch-diff-v4

(リポジトリから github.com/gitgitgadget/git

[...]
    + diff --git a/Documentation/git-range-diff.txt b/Documentation/git-range-diff.txt
    + new file mode 100644
    + --- /dev/null
    + +++ b/Documentation/git-range-diff.txt
    +@@
    ++git-range-diff(1)
[...]
    +
      diff --git a/Makefile b/Makefile
      --- a/Makefile
      +++ b/Makefile
[...]

Manページの新しいファイルを紹介する最初の行には「+」記号が色付けされ、残りの行は太字になります。

Makefileへの変更を示す後の行は、外側と内側の両方の差分でコンテキストとして扱われるため、これらの行は通常の色のままになります。

1
VonC