web-dev-qa-db-ja.com

git diffからの出力の読み方

git-diffのmanページはかなり長く、初心者には必要ないと思われる多くのケースを説明しています。例えば:

git diff Origin/master
248
poseid

Git履歴から高度なdiffの例を見てみましょう( git.gitリポジトリの1088261fをコミット ):

diff --git a/builtin-http-fetch.c b/http-fetch.c
similarity index 95%
rename from builtin-http-fetch.c
rename to http-fetch.c
index f3e63d7..e8f44ba 100644
--- a/builtin-http-fetch.c
+++ b/http-fetch.c
@@ -1,8 +1,9 @@
 #include "cache.h"
 #include "walker.h"

-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
+int main(int argc, const char **argv)
 {
+       const char *prefix;
        struct walker *walker;
        int commits_on_stdin = 0;
        int commits;
@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
        int get_verbosely = 0;
        int get_recover = 0;

+       prefix = setup_git_directory();
+
        git_config(git_default_config, NULL);

        while (arg < argc && argv[arg][0] == '-') {

このパッチを行ごとに分析してみましょう。

  • 最初の行

    diff --git a/builtin-http-fetch.c b/http-fetch.c
    diff --git a/file1 b/file2という形式の「git diff」ヘッダーです。 a/b/のファイル名は、名前変更/コピーが関係しない限り同じです(この場合のように)。 --gitは、diffが「git」diff形式であることを意味します。
  • 次は1つ以上の拡張ヘッダー行です。最初の3つ

    類似性インデックス95%
     rename from builtin-http-fetch.c 
     rename to http-fetch.c
    ファイルの名前がbuiltin-http-fetch.cからhttp-fetch.cに変更されたこと、およびこれら2つのファイルが95%同一であること(この名前変更の検出に使用されたこと)を教えてください。

    拡張差分ヘッダーの最後の行。
    インデックスf3e63d7..e8f44ba 100644
    指定されたファイルのモード(100644は通常のファイルであり、シンボリックリンクではないこと、実行可能な許可ビットがないことを意味します)、およびプレイメージの短縮ハッシュ(指定された前のファイルのバージョン変更)およびpostimage(変更後のファイルのバージョン)。この行は、git am --3wayによって使用され、パッチを適用できない場合に3者間マージを試行します。

  • 次は2行の統合diffヘッダーです

    --- a/builtin-http-fetch.c 
     +++ b/http-fetch.c
    diff -Uの結果と比較すると、ソース(プリイメージ)および宛先(ポストイメージ)のファイル名の後のfrom-file-modification-timeもto-file-modification-timeもありません。ファイルが作成された場合、ソースは/dev/nullです。ファイルが削除された場合、ターゲットは/dev/nullです。
    この2行ヘッダーのdiff.mnemonicPrefixおよびa/プレフィックスの代わりにb/構成変数をtrueに設定すると、比較対象のプレフィックスとしてそれぞれc/i/w/およびo/を使用できます。 git-config(1) を参照してください
  • 次に、1つまたは複数の違いがあります。各ハンクには、ファイルが異なる1つの領域が表示されます。統一形式のハンクは、次のような行で始まります

    @@ -1,8 +1,9 @@
    または
    @@ -18,6 +19,8 @@ int cmd_http_fetch(int argc、const char ** argv、...
    形式は@@ from-file-range to-file-range @@ [header]です。 from-file-rangeの形式は-<start line>,<number of lines>で、to-file-rangeは+<start line>,<number of lines>です。開始行と行数は、それぞれプレイメージとポストイメージのハンクの位置と長さを指します。行数が表示されない場合、それは0であることを意味します。

    オプションのヘッダーは、Cファイル(GNU diffの-pオプションなど)、または他のタイプのファイルの同等のもの(存在する場合)の場合、各変更が発生するC関数を示します。

  • 次に、ファイルの違いについて説明します。両方のファイルに共通の行はスペース文字で始まります。 2つのファイル間で実際に異なる行には、左の印刷列に次のインジケータ文字のいずれかがあります。

    • '+'-最初のファイルに行が追加されました。
    • '-'-最初のファイルから行が削除されました。


    したがって、たとえば、最初のチャンク

     #include "cache.h"
     #include "walker.h"
    
    -int cmd_http_fetch(int argc, const char **argv, const char *prefix)
    +int main(int argc, const char **argv)
     {
    +       const char *prefix;
            struct walker *walker;
            int commits_on_stdin = 0;
            int commits;
    

    cmd_http_fetchmainに置き換えられ、const char *prefix;行が追加されたことを意味します。

    つまり、変更前の「builtin-http-fetch.c」ファイルの適切なフラグメントは次のようになりました。

    #include "cache.h"
    #include "walker.h"
    
    int cmd_http_fetch(int argc, const char **argv, const char *prefix)
    {
           struct walker *walker;
           int commits_on_stdin = 0;
           int commits;
    

    変更後、この「http-fetch.c」ファイルのこのフラグメントは、代わりに次のようになります。

    #include "cache.h"
    #include "walker.h"
    
    int main(int argc, const char **argv)
    {
           const char *prefix;
           struct walker *walker;
           int commits_on_stdin = 0;
           int commits;
    
  • あるかもしれません

    \ファイルの最後に改行なし
    行が存在します(例のdiffではありません)。

Donal Fellowsが言ったように 実際の例で差分を読む練習をするのが最善です。

参照:

455
Jakub Narębski

@@ -1,2 +3,4 @@ diffの一部

この部分を理解するにはしばらく時間がかかったので、最小限の例を作成しました。

形式は基本的にdiff -u統一差分と同じです。

例えば:

diff -u <(seq 16) <(seq 16 | grep -Ev '^(2|3|14|15)$')

ここで、2、3、14、15行目を削除しました。出力:

@@ -1,6 +1,4 @@
 1
-2
-3
 4
 5
 6
@@ -11,6 +9,4 @@
 11
 12
 13
-14
-15
 16

@@ -1,6 +1,4 @@の意味:

  • -1,6は、最初のファイルのこの部分が1行目から始まり、合計6行を表示することを意味します。したがって、1〜6行目が表示されます。

    1
    2
    3
    4
    5
    6
    

    -は「古い」という意味で、通常はdiff -u old newとして呼び出します。

  • +1,4は、2番目のファイルのこの部分が1行目から始まり、合計4行を表示することを意味します。したがって、1行目から4行目が表示されます。

    +は「新規」を意味します。

    2行が削除されたため、6行ではなく4行しかありません!新しいハンクは次のとおりです。

    1
    4
    5
    6
    

2番目のハンクの@@ -11,6 +9,4 @@は類似しています:

  • 古いファイルには、古いファイルの11行目から始まる6行があります。

    11
    12
    13
    14
    15
    16
    
  • 新しいファイルには、新しいファイルの9行目から始まる4行があります。

    11
    12
    13
    16
    

    11は新しいファイルの9行目であることに注意してください。これは、前のハンクの2行と2行がすでに削除されているためです。

ハンクヘッダー

Gitのバージョンと設定に応じて、@@行の隣にコード行を取得することもできます。 func1() {

@@ -4,7 +4,6 @@ func1() {

これは、プレーンdiff-pフラグでも取得できます。

例:古いファイル:

func1() {
    1;
    2;
    3;
    4;
    5;
    6;
    7;
    8;
    9;
}

6を削除すると、差分は次のように表示されます。

@@ -4,7 +4,6 @@ func1() {
     3;
     4;
     5;
-    6;
     7;
     8;
     9;

これはfunc1の正しい行ではないことに注意してください:1および2行をスキップしました。

この素晴らしい機能は、多くの場合、各ハンクがどの関数またはクラスに属しているかを正確に示します。これは、差分を解釈するのに非常に便利です。

ヘッダーを選択するアルゴリズムがどのように正確に機能するかについては、次で説明します。 git diff hunkヘッダーの抜粋はどこから来ますか?

以下に簡単な例を示します。

diff --git a/file b/file 
index 10ff2df..84d4fa2 100644
--- a/file
+++ b/file
@@ -1,5 +1,5 @@
 line1
 line2
-this line will be deleted
 line4
 line5
+this line is added

ここに説明があります(詳細を参照してください here )。

  • --gitはコマンドではありません。これはgitバージョンのdiff(unixではない)であることを意味します
  • a/ b/はディレクトリであり、実際のものではありません。同じファイルを処理する場合に便利です(私の場合、a /はインデックスにあり、b /は作業ディレクトリにあります)
  • 10ff2df..84d4fa2は、これら2つのファイルのblob IDです
  • 100644は「モードビット」であり、これが通常のファイルであることを示します(実行可能ファイルでもシンボリックリンクでもありません)
  • --- a/file +++ b/fileマイナス記号は、a /バージョンにはあるがb /バージョンにはない行を示します。プラス記号は、a /にはないがb /にある行を示します(私の場合は---は削除された行を意味し、+++はb /に追加された行を意味し、これは作業ディレクトリ内のファイルです)
  • @@ -1,5 +1,5 @@これを理解するには、大きなファイルで作業する方が良いです。異なる場所で2つの変更がある場合、@@ -1,5 +1,5 @@のような2つのエントリを取得します。ファイルline1 ... line100があり、line10を削除し、新しいline100を追加するとします-取得します:
@@ -7,7 +7,6 @@ line6
 line7
 line8
 line9
-this line10 to be deleted
 line11
 line12
 line13
@@ -98,3 +97,4 @@ line97
 line98
 line99
 line100
+this is new line100
21
irudyak

デフォルトの出力形式(元々diffとして知られているプログラムから来ています)は、「統合されたdiff」として知られています。基本的に4種類の行が含まれています。

  • 単一のスペースで始まるコンテキスト行
  • +で始まる挿入された行を示す挿入行
  • -で始まる削除行、および
  • これが話しているファイル、差分を生成するために使用されたオプション、ファイルがそのパーミッションを変更したかどうかなど、より高いレベルのことを記述するメタデータ行.

変更内容を正確に知っているファイルの2つのバージョン間の差分の読み取りを練習することをお勧めします。そのように、あなたはそれを見たときに何が起こっているかを認識するでしょう。

14
Donal Fellows

私のMacでは:

info diffを選択します:Output formats-> Context-> Unified format-> Detailed Unified

または オンラインman diff gnuで同じセクションへの同じパスをたどる:

ファイル:diff.info、ノード:詳細な統一、次:例の統一、上:統一形式

統一フォーマットの詳細な説明......................................

統一された出力形式は、次のような2行のヘッダーで始まります。

 --- FROM-FILE FROM-FILE-MODIFICATION-TIME
 +++ TO-FILE TO-FILE-MODIFICATION-TIME

タイムスタンプは「2002-02-21 23:30:39.942229878 -0800」のようになり、日付、秒の小数部の時刻、およびタイムゾーンを示します。

`--label = LABEL 'オプションを使用してヘッダーのコンテンツを変更できます。 * Note Alternate Names ::を参照してください。

次に、1つまたは複数の違いがあります。各ハンクには、ファイルが異なる1つの領域が表示されます。統一された形式のハンクは次のようになります。

 @@ FROM-FILE-RANGE TO-FILE-RANGE @@
  LINE-FROM-EITHER-FILE
  LINE-FROM-EITHER-FILE...

両方のファイルに共通の行はスペース文字で始まります。 2つのファイル間で実際に異なる行には、左の印刷列に次のインジケータ文字のいずれかがあります。

`+ '最初のファイルにここに行が追加されました。

`-'最初のファイルから行が削除されました。

6
stefanB

バージョン管理では、2つのバージョン間の違いは、 "diff"(または同義語で "patch")と呼ばれるもので示されます。そのような差分を詳細に見てみましょう-それを読む方法を学びましょう。

差分の出力を見てください。この出力に基づいて、git diff出力を理解します。

enter image description here

比較ファイルa/b

Diffは2つのアイテムを比較します:アイテムAとアイテムB。ほとんどの場合、AとBは同じファイルですが、バージョンが異なります。頻繁には使用されませんが、diffは2つの完全に無関係なファイルを互いに比較して、それらがどのように異なるかを示すこともできます。実際に比較されるものを明確にするために、diff出力は常に「A」と「B」で表されるファイルを宣言することから始まります。

ファイルのメタデータ

ここに表示されるファイルメタデータは非常に技術的な情報であり、おそらく実際には必要ないでしょう。最初の2つの数字は、2つのファイルのハッシュ(または単に「ID」)を表します。Gitは、プロジェクトだけでなく、各ファイルのすべてのバージョンをオブジェクトとして保存します。このようなハッシュは、特定のリビジョンのファイルオブジェクトを識別します。最後の番号は内部ファイルモード識別子です(100644は単なる「通常のファイル」ですが、100755は実行可能ファイルを指定し、120000はシンボリックリンクを表します)。

a/bのマーカー

出力のさらに下では、実際の変更はAまたはBからのものとしてマークされます。区別するために、AとBにはそれぞれ記号が割り当てられます。バージョンAの場合、これはマイナス( "-")記号であり、バージョンBの場合、プラス( "+")記号が使用されます。

チャンク

差分は最初から最後まで完全なファイルを表示しません。2行しか変更されていない場合、10,000行のファイルのすべてを表示したくないでしょう。代わりに、実際に変更された部分のみが表示されます。このような部分は「チャンク」(または「ハンク」)と呼ばれます。変更された実際の行に加えて、チャンクには少しの「コンテキスト」も含まれます。変更の前後の一部の(変更されていない)行により、変更がどのコンテキストで発生したかをよりよく理解できます。

チャンクヘッダー

これらの各チャンクには、2つの@@マーカーで囲まれたヘッダーが先頭に追加されます。 Gitはヘッダーを使用して、影響を受けた行を通知します。この場合、次の行は最初のチャンクで表されます。

  • ファイルA(「-」で表される)から、行番号から始まる6行が抽出されます。 34

  • ファイルB(「+」で表される)から、8行が表示されます。これも行番号から始まります。 34

「@@」の最後のペアの後のテキストは、コンテキストを再び明確にすることを目的としています。Gitは、メソッド名またはこのチャンクがファイル内のどこから取得されたかに関する他のコンテキスト情報を表示しようとします。ただし、これはプログラミング言語に大きく依存し、すべてのシナリオで機能するわけではありません。

変更点

変更された各行の先頭には、「+」または「-」記号が付けられます。説明したように、これらのシンボルは、バージョンAとバージョンBがどのように見えるかを理解するのに役立ちます。「-」記号が前に付いた行はAから、「+」記号が付いた行はBから来ます。ほとんどの場合、GitはA /-を「古い」コンテンツ、B/+を「新しい」コンテンツと考えることができるような方法でのAとB。

私たちの例を見てみましょう:

  • 変更#1には、先頭に「+」が付いた2行が含まれています。 Aにはこれらの行に対応するものが存在しないため(「-」が付いた行はない)、これはこれらの行が追加されたことを意味します。

  • 変更#2は正反対です。Aには、「-」記号でマークされた2行があります。ただし、Bには同等の(「+」行なし)がないため、削除されました。

  • 最後に、変更#3で、いくつかの行が実際に変更されました。2つの「-」行は、下の2つの「+」行のように変更されました。

ソース

5

あなたの質問から、diffのどの部分が紛らわしいかわかりません:実際のdiff、またはgitが出力する追加のヘッダー情報。念のため、ヘッダーの概要を簡単に説明します。

最初の行はdiff --git a/path/to/file b/path/to/fileのようなものです-明らかに、差分のこのセクションが何のファイルなのかを示しているだけです。ブール構成変数diff.mnemonic prefixを設定すると、aおよびbは、cおよびw(コミットおよび作業ツリー)などのよりわかりやすい文字に変更されます。 。

次に、「モード行」-ファイルの内容の変更を伴わない変更の説明を提供する行があります。これには、新規/削除されたファイル、名前変更/コピーされたファイル、および権限の変更が含まれます。

最後に、index 789bd4..0afb621 100644のような行があります。おそらく気にすることはないでしょうが、これらの6桁の16進数は、このファイルの古いブロブと新しいブロブの短縮SHA1ハッシュです(ブロブは、ファイルの内容のような生データを格納するgitオブジェクトです)。そしてもちろん、100644はファイルのモードです-最後の3桁は明らかにパーミッションです。最初の3つは、追加のファイルメタデータ情報を提供します( SOの説明 )。

その後、標準の統合diff出力に進みます(従来のdiff -Uと同様)。ハンクに分割されます-ハンクは、変更とそのコンテキストを含むファイルのセクションです。各ハンクの前に、問題のファイルを示す---行と+++行のペアがあり、実際の差分は(デフォルトでは)-の両側にある3行のコンテキストです。削除された/追加された行を示す+行。

3
Cascabel