如何从git diff读取输出?

时间:2010-03-27 13:33:52

标签: git diff git-diff

git-diff的手册页很长,并解释了许多初学者似乎不需要的案例。例如:

git diff origin/master

7 个答案:

答案 0 :(得分:444)

让我们看看git历史中的示例高级差异(在commit 1088261f in git.git repository中):

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格式。

  • 接下来是一个或多个扩展标题行。前三个

    similarity index 95%
    rename from builtin-http-fetch.c
    rename to http-fetch.c
    告诉我们该文件已从builtin-http-fetch.c重命名为http-fetch.c,并且这两个文件的95%相同(用于检测此重命名)。

    扩展diff头中的最后一行,
    index f3e63d7..e8f44ba 100644
    告诉我们给定文件的模式(100644意味着它是普通文件而不是例如符号链接,并且它没有可执行权限位),以及缩短的preimage哈希值(给定更改前的文件版本)和postimage(更改后的文件版本)。如果无法自行应用补丁,git am --3way将使用此行尝试进行3向合并。

  • 接下来是两行统一差异标头

    --- a/builtin-http-fetch.c
    +++ b/http-fetch.c
    diff -U结果相比,它没有文件修改时间,也没有文件修改 - 源(preimage)和目标(postimage)文件名之后的时间。如果创建了文件,则源为/dev/null;如果文件被删除,则目标为/dev/null
    如果您将diff.mnemonicPrefix配置变量设置为true,则代替此前两个中的a/b/前缀 - 您可以使用行标题c/i/w/o/作为前缀,分别对应您所比较的内容;见git-config(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函数,如果它是C文件(如GNU diff中的-p选项),或其他类型文件的等效项(如果有)。

  • 接下来是文件不同的描述。两个文件共有的行以空格字符开头。两个文件之间实际不同的行在左侧打印列中具有以下指示符之一:

    • '+' - 此处在第一个文件中添加了一行。
    • ' - ' - 此处从第一个文件中删除了一行。

    所以,例如,第一个块

     #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;
    
  • 可能存在

    \ No newline at end of file
    行(它不在示例差异中)。

作为Donal Fellows said,最好在现实生活中练习阅读差异,在那里你知道你改变了什么。

<强>参考文献:

答案 1 :(得分:55)

@@ -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
    2
    3
    4
    5
    6
    

    -表示“旧”,因为我们通常将其称为diff -u old new

  • +1,4表示此部分对应于第二个文件的第1行到第4行。

    +表示“新”。

    我们只有4行代替6行,因为删除了2行!新的大块头只是:

    1
    4
    5
    6
    
第二个大块的

@@ -11,6 +9,4 @@类似:

    旧文件上的
  • ,我们有6行,从旧文件的第11行开始:

    11
    12
    13
    14
    15
    16
    
  • 在新文件中,我们有4行,从新文件的第9行开始:

    11
    12
    13
    16
    

    注意,行11是新文件的第9行,因为我们已经删除了前一个块上的2行:2和3。

Hunk标题

根据您的git版本和配置,您还可以获得@@行旁边的代码行,例如func1() { in:

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

这也可以通过普通-p的{​​{1}}标志获得。

示例:旧文件:

diff

如果我们删除行func1() { 1; 2; 3; 4; 5; 6; 7; 8; 9; } ,则差异显示:

6

请注意,这不是@@ -4,7 +4,6 @@ func1() { 3; 4; 5; - 6; 7; 8; 9; 的正确行:它跳过了行func11

这个很棒的功能通常可以准确地告诉每个hunk属于哪个函数或类,这对于解释差异非常有用。

如何精确选择标题的算法在Where does the excerpt in the git diff hunk header come from?

中讨论

答案 2 :(得分:21)

这是一个简单的例子。

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不是命令,这意味着它是diff(不是unix)的git版本
  • a/ b/是目录,它们不是真实的。当我们处理同一个文件时(在我的情况下,a /在索引中,而b /在工作目录中),这只是一个方便。
  • 10ff2df..84d4fa2是这两个文件的blob ID
  • 100644是“模式位”,表示这是一个常规文件(不是可执行文件而不是符号链接)
  • --- a/file +++ b/file减号表示a /版本中的行但b /版本中缺少;和加号表示/ /但在b /中存在缺少的行(在我的例子中---表示删除的行,+++表示在b /中添加行,这是工作目录中的文件)
  • @@ -1,5 +1,5 @@为了理解这一点,最好使用大文件;如果您在不同的地方进行了两项更改,则会获得两个条目,例如@@ -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

答案 3 :(得分:13)

默认输出格式(如果您想查找更多信息,最初来自称为diff的程序)称为“统一差异”。它基本上包含4种不同类型的行:

  • 上下文行,以单个空格开头,
  • 插入行,显示已插入的行,以+开头,
  • 删除行,以-
  • 开头
  • 描述更高级别内容的元数据行,比如这是关于哪个文件,用于生成差异的选项,文件是否更改了权限等等。

我建议你练习在文件的两个版本之间读取差异,在那里你确切知道你改变了什么。就像那样,你会发现当你看到它时会发生什么。

答案 4 :(得分:6)

在我的Mac上:

info diff然后选择:Output formats - &gt; Context - &gt; Unified format - &gt; Detailed Unified

或gnu上的online man diff遵循相同路径的相同路径:

  

文件:diff.info,节点:详细   统一,下一个:示例统一,向上:   统一格式

     

统一格式的详细说明   ......................................

     

统一输出格式启动   有两行标题,看起来   像这样:

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

时间戳看起来像`2002-02-21   23:30:39.942229878 -0800'表示   日期,时间与小数   秒和时区。

     

您可以更改标题的内容   使用`--label = LABEL'选项;看到   *注意替代名称::。

     

接下来是一个或多个帅哥   差异;每个大块显示一个区域   文件不同的地方。统一   格式帅哥看起来像这样:

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

两个文件共有的行   以空格字符开头。该   实际上不同的行   两个文件具有以下之一   左侧打印中的指示字符   柱:

     

'+”        这里在第一个文件中添加了一行。

     

' - ”        这里从第一个文件中删除了一行。

答案 5 :(得分:5)

在版本控制中,两个版本之间的差异在所谓的“差异”(或同义地,“补丁”)中呈现。让我们详细了解一下这种差异 - 并学习如何阅读它。

查看差异的输出。基于此输出,我们将了解git diff输出。

enter image description here

比较档案a / b

我们的差异比较了两个项目:项目A和项目B.在大多数情况下,A和B将是相同的文件,但是在不同的版本中。尽管不经常使用,但diff也可以将两个完全不相关的文件相互比较,以显示它们的不同之处。 为了弄清楚实际比较的内容,diff输出始终通过声明哪些文件由“A”和“B”表示来开始。

文件元数据

此处显示的文件元数据是一个非常技术性的信息,您可能在实践中不需要这些信息。前两个数字代表我们两个文件的哈希值(或简称为“ID”):Git不仅保存项目的每个版本,还保存每个文件的每个版本作为对象。这样的散列标识特定修订版本的文件对象。最后一个数字是内部文件模式标识符(100644只是“普通文件”,而100755指定可执行文件,120000表示符号链接)。

a / b的标记

在输出中进一步向下,实际变化将标记为来自A或B.为了区分它们,A和B每个都分配了一个符号:对于版本A,这是一个减号(“ - ” )签名和版本B,使用加号(“+”)。

<强>组块

diff不会从头到尾显示完整的文件:当只有2行更改时,您不希望看到10,000行文件中的所有内容。相反,它只显示实际修改过的那些部分。这样的部分称为“块”(或“块”)。除了实际更改的行之外,块还包含一些“上下文”:修改前后的一些(未更改的)行,以便您可以更好地了解发生更改的上下文。

块标题

这些块中的每一个都由一个标题前置,包含在两个@@标记之间。 Git使用标题告诉你哪些行受到了影响。在我们的例子中,第一个块表示以下行:

  • 从文件A(由“ - ”表示)中,从第no行开始提取6行。 34

  • 从文件B(用“+”表示),显示8行,也从行号开始。 34

结束对“@@”之后的文本旨在再次澄清上下文:Git尝试显示方法名称或其他上下文信息,从文件中取出该块的位置。但是,这在很大程度上取决于编程语言,并不适用于所有场景。

<强>更改

每个更改的行前面都带有“+”或“ - ”符号。如上所述,这些符号可帮助您了解版本A和B的完整外观:前面带有“ - ”符号的行来自A,而带有“+”符号的行来自B. 在大多数情况下,Git选择A和B的方式使您可以将A / - 视为“旧”内容,将B / +视为“新”内容。

让我们看一下我们的例子:

  • 更改#1包含前面带有“+”的两行。由于这些行中没有对应的A(没有带“ - ”的行),这意味着添加了这些行。

  • 改变#2正好相反:在A中,我们有两行标有“ - ”符号。但是,B没有等效(没有“+”行),这意味着它们被删除了。

  • 在变化#3中,最后,实际修改了一些行:两条“ - ”行被改为看起来像下面的两条“+”行。

Source

答案 6 :(得分:3)

从你的问题中不清楚你发现哪些差异令人困惑:实际的差异,或git打印的额外标题信息。以防万一,这里是标题的快速概述。

第一行类似于diff --git a/path/to/file b/path/to/file - 显然它只是告诉你这个差异部分的文件是什么。如果设置布尔配置变量diff.mnemonic prefix,则ab将更改为更具描述性的字母,如cw(提交和工作树)

接下来,有“模式行” - 行,为您提供任何不涉及更改文件内容的更改的说明。这包括新/已删除的文件,重命名/复制的文件以及权限更改。

最后,有一条像index 789bd4..0afb621 100644这样的行。您可能永远不会关心它,但是这些6位十六进制数字是此文件的旧和新Blob的缩写SHA1哈希值(blob是存储原始数据(如文件内容)的git对象)。当然,100644是文件的模式 - 后三位数显然是权限;前三个提供额外的文件元数据信息(SO post describing that)。

之后,您将进入标准的统一差异输出(就像经典的diff -U)。它被分成了一堆 - 一个大块头是文件的一部分,包含变化及其背景。每个hunk前面都有一对---+++行表示有问题的文件,然后实际的差异是(默认情况下)-两侧的三行上下文+行显示删除/添加的行。