git diffからの出力を読み取る方法は?


271

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

git diff origin/master

1
別のテキストエディターを使用することにより、行番号の@ ... @範囲表記が明らかになりました。
ポーズID

どのテキストエディター?
Jus12

回答:


489

gitの履歴からの高度な差分の例を見てみましょう(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.cb / http-fetch.c
    フォームの「git diff」ヘッダーdiff --git a/file1 b/file2です。a/およびb/名前変更/コピーが(私たちの場合のように)関与しなければ、ファイル名は同じです。これ--gitは、diffが「git」diff形式であることを意味します。

  • 次は、1つ以上の拡張ヘッダー行です。最初の3つ

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

    拡張diffヘッダーの最後の行である
    インデックスf3e63d7..e8f44ba 100644
    与えられたファイルのモード(100644symlinkではなく、通常のファイルであり、実行許可ビットがないことを意味します)、およびプリイメージ(与えられた変更前のファイルのバージョン)とポストイメージ(変更後のファイルのバージョン)。この行は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です。構成変数をtrueに
    設定diff.mnemonicPrefixするa/b/、この2行のヘッダーのとの代わり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>です。start-lineとnumber-of-linesはどちらも、それぞれプリイメージとポストイメージのハンクの位置と長さを表します。行数が示されていない場合は、0であることを意味します。

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

  • 次に、ファイルが異なる場所について説明します。両方のファイルに共通の行は、空白文字で始まります。2つのファイル間で実際に異なる行の左側の印刷列には、以下の標識文字の1つがあります。

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


    たとえば、最初のチャンク

     #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_fetchがに置き換えられmain、その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の例にはありません)。

ドナル・フェローと述べ、それはあなたが変更したものを知っている実際の例、上の差分を読んで練習するのが最善です。

参照:


1
@Geremia:Gitは類似性ベースのヒューリスティックを名前変更の検出に使用します...でのコードの移動とコピーの検出にも使用しますgit blame -C -C。それはGit設計の決定です。git diff形式は、類似性(または非類似性)インデックスをユーザーに表示するだけです。
JakubNarębski16年

1
@Geremia:より正確に[header]は、ハンクの前にある関数の開始と最も近い先行です。ほとんどの場合、この行には、diffのチャンクがある関数の名前が含まれています。これは、diffgitattributeをdiffドライバーに設定し、xfuncname構成変数を含むdiffドライバーで構成できます。
JakubNarębski16年

1
@AnthonyGeoghegan:行が削除される(ポストイメージの行数が0である)か、追加される(プリイメージの行数が0である)。
JakubNarębski16年

1
@KasunSiyambalapitiya:(コンテキストdiff形式^ [1]とは対照的に)Gitが使用する統合diff形式は、変更された行と削除された行と追加された行を区別しません。[1]:gnu.org/software/diffutils/manual/html_node/Context-Format.html
JakubNarębski16年

1
@JakubNarębski:デフォルトの行数は0ではなく1です。それはそれと同じくらい簡単です。実際には、表示するコンテキストがないため、単一行ファイルの場合は「-1」または「+1」としてのみ表示されます。
Guido Flohr 2017年

68

@@ -1,2 +3,4 @@ 差分の一部

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

形式は基本的には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,42番目のファイルのこの部分は1行目から始まり、合計4行を示します。したがって、1行目から4行目までを示しています。

    + 「新しい」を意味します。

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

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@ 2番目のハンクも同様です。

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

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

    11
    12
    13
    16
    

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

ハンクヘッダー

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

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

これ-pはplain のフラグで取得することもできdiffます。

例:古いファイル:

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

lineを削除する6と、diffに次のように表示されます。

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

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

この素晴らしい機能は、多くの場合、各ハンクがどの関数またはクラスに属しているかを正確に伝えます。これは、diffを解釈するのに非常に役立ちます。

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


11
これはまだ完全に理解していない人のためのものです。で @@ -1,6 +1,4 @@plsは読んでいない-1として、minus oneまたは+1としてplus oneの代わりとしてこれを読んでline 1 to 6、古い(最初の)ファイルインチ ここで- implies "old"はマイナスではないことに注意してください 。ところで、説明してくれてありがとう... haash。
dkjain

これから@@ -1,8 +1,9 @@は実際に起こったことを解釈することが可能です。たとえば、1)1行が追加されている2)1行が変更されており、1行が追加されている、などです。または、git diff correcltyがコードで変更された行を特定するときにそれらを取得する方法が必要であるため、別の方法からのものですか?私は本当にこれを整理する必要があるので助けてください
Kasun Siyambalapitiya '28

これは正しくないため、誤解を招く可能性があることに注意してください。上記の回答のこのステートメントは、「+1,4この部分は2番目のファイルの1行目から4行目までに相当すると述べています」。これは、+1,4非偶発的なコンテキスト行を参照する可能性があるためです。むしろ、どのような「+1,4」実際に意味するということである「がある4ファイルの『バージョン』の行(つまり、コンテキスト行が)」。の意味を理解することが重要である+-<whitespace>それが兄貴の解釈に適用されるように、これらの行の先頭に。より視覚的な例:youtube.com/watch
v=1tqMjJeyKpw

23

これが簡単な例です。

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

ここに説明があります(詳細はこちらをご覧ください)。

  • --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つの変更がある場合、次のような2つのエントリが表示され@@ -1,5 +1,5 @@ます。ファイル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

ありがとう。「100644はモードビットであり、これが通常のファイルであることを示します(実行可能ファイルではなく、シンボリックリンクではありません)」。「モードビット」はLinuxの概念ですか、それともGitの概念ですか?
Tim

@Tim gitに固有ではありません。右側の3桁(644)は8進数で読み取られ(値はそれぞれ1、2、4の実行、書き込み、読み取りのアクセス許可)、この順序で所有者(ユーザー)、グループ、その他のアクセス許可に対応します。つまり、644シンボリックu=rw,og=rに書かれていれば、誰にでも読むことができますが、所有者だけが書き込めます。左側の他の数字は、シンボリックリンクのように、他の情報をエンコードします。値はgithub.com/git/git/blob/…で確認できます。この位置の最初の1は「通常のファイル」です。
Patrick Mevzek

15

既定の出力形式(元々はdiff、より詳細な情報を探したいかのように知られているプログラムから取得される)は、「統一された差分」と呼ばれます。それは本質的に4つの異なるタイプのラインを含みます:

  • 単一のスペースで始まるコンテキスト行、
  • 挿入された行を示す挿入行。 +
  • で始まる削除行 -および
  • これが話しているファイル、diffを生成するために使用されたオプション、ファイルのアクセス許可が変更されたかどうかなど、より高いレベルの事柄を説明するメタデータ行

変更内容を正確に把握しているファイルの2つのバージョン間の差分を読む練習をすることをお勧めします。そのように見ると、何が起こっているのかがわかります。


5
+1:実践についての提案は非常に良いものです。おそらく、ドキュメントを執着的に読むよりもはるかに速いでしょう。
Cascabel

6

私のMacの場合:

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

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

ファイル: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つのファイル間で実際に異なる行の左側の印刷列には、以下の標識文字の1つがあります。

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

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


1
バージョン管理システムでは意味がないため、gitは「XXX-FILE-MODIFICATION-TIME」の部分を出力しないことに注意してください。ファイルシステム上のファイルを比較するために、タイムスタンプは「貧乏人」バージョン管理として機能することができます。
JakubNarębski10年

3

実際の差分、またはgitが出力する追加のヘッダー情報など、diffのどの部分が混乱しているのか、質問からはわかりません。念のため、ここにヘッダーの概要を示します。

最初の行は次のようなものですdiff --git a/path/to/file b/path/to/file-明らかにそれは、diffのこのセクションが何のファイルであるかをあなたに告げるだけです。あなたはブールのconfig変数を設定した場合diff.mnemonic prefixaおよびは、bのようなより説明的な文字に変更されますcと、w(コミットおよび作業ツリー)の。

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

最後に、のような行がありindex 789bd4..0afb621 100644ます。おそらく気にしないでしょうが、これらの6桁の16進数は、このファイルの新旧のblobのSHA1ハッシュの省略形です(blobは、ファイルのコンテンツのような生データを格納するgitオブジェクトです)。そしてもちろん、これ100644はファイルのモードです。最後の3桁は明らかにアクセス許可です。最初の3つは追加のファイルメタデータ情報を提供します(それについて説明するSO投稿)。

その後、(クラシックと同じようにdiff -U)標準の統一されたdiff出力に進みます。これは、ハンクに分割されます。ハンクは、変更とそのコンテキストを含むファイルのセクションです。各塊は、一対の先行され---且つ+++当該ファイルを表す行、その後、実際の差分は、(デフォルト)のいずれかの側のコンテキストの三行である-+除去/追加された行を示す行。


++ index行。確認git hash-object ./file
Ciro Santilli郝海东冠状病六四事件法轮功
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.