如何从git diff读取输出?

pos*_*eid 254 git diff git-diff

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

git diff origin/master
Run Code Online (Sandbox Code Playgroud)

Jak*_*ski 465

让我们看看git历史中的示例高级差异(在git.git存储库中的commit 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] == '-') {
Run Code Online (Sandbox Code Playgroud)

让我们逐行分析这个补丁.

  • 第一行

    diff --git a/builtin-http-fetch.c b/http-fetch.c
    是表单中的"git diff"标题diff --git a/file1 b/file2.该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意味着它是普通文件,而不是如符号链接,并且它不具有可执行权限位),以及约缩短原像和postimage(文件中给出更改之前的版本)的哈希值(在更改后的文件版本).git am --3way如果无法自行应用补丁,则使用此行尝试执行3向合并.

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

    --- a/builtin-http-fetch.c
    +++ b/http-fetch.c
    diff -U结果相比,它在源(preimage)和目标(postimage)文件名之后没有from-file-modification-time和to-file-modification-time.如果创建了文件,则源为/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文件(如-pGNU diff中的选项),或其他类型文件的等效项(如果有).

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

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


    所以,例如,第一块

     #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;
    
    Run Code Online (Sandbox Code Playgroud)

    意味着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;
    
    Run Code Online (Sandbox Code Playgroud)

    在更改之后,现在'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;
    
    Run Code Online (Sandbox Code Playgroud)
  • 可能有

    \ No newline at end of file
    行存在(它不是示例差异).

正如Donal Fellows所说,最好在实际例子中练习阅读差异,在那里你知道你改变了什么.

参考文献:

  • 这是一个非常出色且全面的答案。几个月前我就投票了,但我一直在重新阅读它以巩固我的理解。我想查询一句话:*“如果没有显示行数,则意味着它是0。”*如果更改的行数为零,我会认为根本不会有任何行数大块头。使用 [GNU diff 的统一格式](https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html),*“如果一个块仅包含一行,则仅显示其起始行号” *。我想 git 的 diff 也是如此。 (2认同)

Cir*_*四事件 59

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

这部分花了一些时间来理解,所以我创建了一个最小的例子.

@@ -1,2 +3,4 @@统一差异的格式基本相同.

例如:

diff -u <(seq 16) <(seq 16 | grep -Ev '^(2|3|14|15)$')
Run Code Online (Sandbox Code Playgroud)

这里我们删除了第2,3,14和15行.输出:

@@ -1,6 +1,4 @@
 1
-2
-3
 4
 5
 6
@@ -11,6 +9,4 @@
 11
 12
 13
-14
-15
 16
Run Code Online (Sandbox Code Playgroud)

diff -u 手段:

+ 对于第二个大块是类似的:

Hunk标头

根据您的git版本和配置,您还可以在该行旁边获取代码11行,例如@@:

@@ -4,7 +4,6 @@ func1() {
Run Code Online (Sandbox Code Playgroud)

这也可以通过func1() {plain 的旗帜获得-p.

示例:旧文件:

func1() {
    1;
    2;
    3;
    4;
    5;
    6;
    7;
    8;
    9;
}
Run Code Online (Sandbox Code Playgroud)

如果我们删除行diff,diff会显示:

@@ -4,7 +4,6 @@ func1() {
     3;
     4;
     5;
-    6;
     7;
     8;
     9;
Run Code Online (Sandbox Code Playgroud)

请注意,这不是正确的行6:它跳过行func11.

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

选择标题的算法如何正常工作在下面讨论:git diff hunk标题中的摘录来自哪里?

  • 这适用于仍然不太了解的人.在`@@ -1,6 +1,4 @@`请不要将`-1`读为'减1'或'+ 1`作为'加1',而不是将其读作`1行到6'.旧的(第一个)文件.注意这里` - 暗示"旧"不是减去.顺便说一下,谢谢你澄清......哈希. (10认同)

iru*_*yak 22

这是一个简单的例子.

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
Run Code Online (Sandbox Code Playgroud)

这是一个解释(详见此处).

  • --git 不是命令,这意味着它是diff的一个git版本(不是unix)
  • a/ b/是目录,它们不是真实的.当我们处理同一个文件时,这只是一个方便(在我的情况下,a /在索引中,而b /在工作目录中)
  • 10ff2df..84d4fa2 是这两个文件的blob ID
  • 100644 是"模式位",表示这是一个常规文件(不可执行,不是符号链接)
  • --- a/file +++ b/file减号表示a /版本中的行但b /版本中缺少; 和加号显示a /但缺少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
Run Code Online (Sandbox Code Playgroud)


Don*_*ows 14

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

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

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

  • +1:关于练习的建议是非常好的 - 可能比试图阅读文档要快得多. (4认同)

ste*_*anB 6

在我的Mac上:

info diff然后选择:Output formats- > Context- > Unified format- > Detailed Unified:

或者在同一部分的相同路径上的在线人类差异 gnu:

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

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

统一输出格式以两行标题开头,如下所示:

 --- FROM-FILE FROM-FILE-MODIFICATION-TIME
 +++ TO-FILE TO-FILE-MODIFICATION-TIME
Run Code Online (Sandbox Code Playgroud)

时间戳看起来像"2002-02-21 23:30:39.942229878 -0800"以表示日期,小数秒的时间和时区.

您可以使用`--label = LABEL'选项更改标题的内容; 请参阅*备注替代名称::.

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

 @@ FROM-FILE-RANGE TO-FILE-RANGE @@
  LINE-FROM-EITHER-FILE
  LINE-FROM-EITHER-FILE...
Run Code Online (Sandbox Code Playgroud)

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

`+'这里添加了一行到第一个文件.

` - '从第一个文件中删除了一行.