如何从git差异读取输出?

git-diff的手册页相当长,并且解释了许多对于初学者来说似乎不需要的情况。例如:

git diff origin/master
135733 次浏览

在我的mac上:

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

或gnu上的网络男人差异,按照相同的路径到相同的section:

文件:diff.info,节点:详细 统一,下:示例统一,上: 统一格式的< / p >

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

开始统一输出格式 带有两行标题,看起来 这样的:< / p >

 --- 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...

两个文件共用的行 以空格字符开始。的 这两条线实际上是不同的 两个文件包含以下内容之一 指示器字符在左侧打印 列:< / p > < p >“+” 在第一个文件中添加了一行 < p >“-”

.

.

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

  • 上下文行,以一个空格开头,
  • 显示已插入行的插入行,以+开头,
  • -开头的删除行和
  • 元数据行描述了更高层次的东西,比如这谈论的是哪个文件,用于生成差异的选项是什么,文件是否更改了其权限,等等。

我建议您练习阅读文件的两个版本之间的差异,以便您确切地知道所更改的内容。这样,当你看到它的时候,你就会知道发生了什么。

从你的问题中不清楚你觉得diff的哪一部分令人困惑:实际的diff,还是git打印的额外头信息。以防万一,这里有一个快速的标题概述。

第一行是类似diff --git a/path/to/file b/path/to/file的东西——显然它只是告诉你diff的这一部分是用于哪个文件的。如果你设置布尔配置变量diff.mnemonic prefixab将被更改为更有描述性的字母,如cw(提交和工作树)。

接下来,是“模式行”——这些行描述了不涉及更改文件内容的任何更改。这包括新建/删除文件,重命名/复制文件,以及权限更改。

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

在这之后,你就可以得到标准的统一差异输出(就像经典的diff -U一样)。它被分割成块——块是文件中包含更改及其上下文的部分。每个块之前都有一对---+++行,表示有问题的文件,然后实际的diff是(默认情况下)在-+行两侧的三行上下文,显示删除/添加的行。

让我们来看一个来自git历史的高级diff的例子(在在git中提交1088261f。git存储库中):

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] == '-') {

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

  • 第一行
    ——git a/builtin-http-fetch.c b/http-fetch.c
    是一个"git diff"diff --git a/file1 b/file2格式的头文件。a/b/文件名是相同的,除非涉及重命名/复制(就像我们的例子一样)。--git表示diff在"git"diff格式。

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

    相似度指数95%
    从builtin-http-fetch.c重命名
    rename to http-fetch.c
    告诉我们文件从builtin-http-fetch.c重命名为http-fetch.c,并且这两个文件95%相同(用于检测重命名)。

    扩展diff头中的最后一行,即
    指数f3e63d7 . .e8f44ba 100644
    告诉我们给定文件的模式(100644意味着它是普通文件而不是例如符号链接,并且它没有可执行权限位),以及关于preimage(给定更改前的文件版本)和postimage(更改后的文件版本)的缩短哈希。如果补丁本身不能应用,git am --3way将使用这一行来尝试进行3-way合并
  • 接下来是两行统一差异头

    ——a/builtin-http-fetch.c
    +++ b/http-fetch.c
    diff -U结果相比,它在源(preimage)和目标(postimage)文件名后没有from-file- modify -time和to-file- modify -time。如果文件已创建,则源为/dev/null;如果文件被删除,目标是/dev/null。如果你将diff.mnemonicPrefix配置变量设置为true,在这个两行标题中,你可以用c/i/w/o/作为前缀,分别与你比较的内容;看到/dev/null0 < / p >
  • 接下来是一个或多个差异;每个块显示文件不同的一个区域。统一格式块以__abc3或__abc4开头,格式为@@ from-file-range to-file-range @@ [header]。从文件范围的形式是-<start line>,<number of lines>,到文件范围的形式是+<start line>,<number of lines>。起始线和行数分别表示大块在原像和后像中的位置和长度。如果没有显示行数,则表示它是1。

如果是C文件(如GNU diff中的-p选项),则可选标头显示每次更改发生的C函数,如果有,则显示其他类型文件的等效函数。

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

  • '+'——在第一个文件中添加了一行。

  • '-'——从第一个文件中删除一行。


例如,第一个chunk

     #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;
  • 可能存在
    文件末尾没有换行符
    line(它不在示例diff中)。

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

引用:

这里有一个简单的例子。

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不是命令,这意味着它是diff的git版本(不是unix)
  • a/ b/是目录,它们不是实目录。这只是为了方便我们处理相同的文件(在我的例子中,a/在索引中,b/在工作目录中)
  • 10ff2df..84d4fa2是这两个文件的blob id
  • 100644是“模式位”,表示这是一个常规文件(不可执行,也不是符号链接)。
  • --- a/file +++ b/file负号表示a/ version中的行,但b/ version中没有;加号表示在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

@@ -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,4意味着第二个文件的这一部分从第1行开始,总共显示4行。因此它显示了第1行到第4行。

    +表示“新”。

    我们只有4行而不是6行,因为2行被删除了!新帅哥只是:

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@ for the second hunk is analogous:

  • on the old file, we have 6 lines, starting at line 11 of the old file:

    11
    12
    13
    14
    15
    16
    
  • on the new file, we have 4 lines, starting at line 9 of the new file:

    11
    12
    13
    16
    

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

大块头

根据你的git版本和配置,你也可以在@@行旁边得到一个代码行,例如:

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

这也可以通过普通diff-p标志获得。

例如:旧文件:

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

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

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

注意,这不是func1的正确行:它跳过了12行。

这个很棒的特性通常会准确地告诉每个块属于哪个函数或类,这对于解释差异非常有用。

如何选择头文件的算法将在:在git差异块头摘录从哪里来?