如何读取git diff的输出?
git-diff
的手册页相当长,并解释了许多初学者似乎不需要的情况。 例如:
git diff origin/master
让我们看看git历史logging中的示例高级diff( 在git.git仓库中提交1088261f ):
diff --git a/builtin-http-fetch.cb/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
是
diff --git a/file1 b/file2
格式的“git diff”头diff --git a/file1 b/file2
。a/
和b/
文件名是相同的,除非涉及重命名/复制(就像我们的情况一样)。 –--git
是指diff是以“git”diff格式。 -
接下来是一个或多个扩展标题行。 前三
相似指数95% 从builtin-http-fetch.c重命名 重命名为http-fetch.c
告诉我们这个文件是从
builtin-http-fetch.c
重命名为http-fetch.c
,并且这两个文件是95%相同的(用来检测这个重命名)。扩展差异标题中的最后一行,即
索引f3e63d7..e8f44ba 100644
告诉我们关于给定文件的模式(
100644
表示它是普通文件而不是例如符号链接,并且它没有可执行权限位),以及关于preimage(给定改变之前的文件的版本)的缩短的散列和postimage更改后的文件版本)。 如果补丁不能被应用,git am --3way
会使用这一行来尝试进行3路合并。 -
接下来是两行统一差异标题
--- a / builtin-http-fetch.c +++ b / http-fetch.c
与
diff -U
结果相比,source(preimage)和destination(postimage)文件名后面没有文件修改时间,也没有文件修改时间。 如果创build了文件,则源文件是/dev/null
; 如果文件被删除,目标是/dev/null
。
如果将diff.mnemonicPrefix
configurationvariables设置为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]
。 源文件范围的forms是-<start line>,<number of lines>
,并且文件范围是+<start line>,<number of lines>
。 起始行和行数都分别指前像和后像中的块的位置和长度。 如果未显示的行数表示它是0。可选的头部显示了C函数,其中每个变化发生,如果它是一个C文件(如GNU diff中的
-p
选项),或其他types的文件的等价物(如果有的话)。 -
接下来是文件不同的说明。 两个文件共同的行以空格字符开头。 两个文件之间实际上不同的行在左侧打印列中具有以下指示符字符之一:
- '+' – 一行被添加到第一个文件。
- ' – ' – 从第一个文件中删除了一行。
所以,例如,第一块
#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
replace,而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;
-
可能有
\文件结尾没有换行符
行存在(它不是在示例差异)。
正如Donal Fellows所说的那样 ,最好是练习阅读关于现实生活中的例子,在那里你知道你已经改变了什么。
参考文献:
- git-diff(1)联机帮助页 ,“使用-p生成补丁”
- (diff.info)详细的统一节点,“统一格式的详细描述”。
@@ -1,2 +3,4 @@差异的一部分
这部分花了我一段时间才明白,所以我创build了一个最小的例子。
格式与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 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。
哈克头
根据你的git版本和configuration,你也可以在@@
行旁边find代码行,例如func1() {
in:
@@ -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
的正确行:它跳过了第1
行和第2
。
这个令人敬畏的function通常精确地告诉每个大块属于哪个函数或类,这对解释diff是非常有用的。
如何select标题的algorithm是如何工作的: 在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
这里有一个解释(详情见这里 )。
-
--git
不是一个命令,这意味着它是一个git版本的diff(而不是unix) -
a/ b/
是目录,它们不是真实的。 这只是一个方便,当我们处理相同的文件(在我的情况下,一个/在索引和B /在工作目录) -
10ff2df..84d4fa2
是这两个文件的blob ID -
100644
是“模式位”,表示这是一个常规文件(不可执行,不是符号链接) -
--- a/file +++ b/file
减号显示a /版本中的行,但从b /版本中丢失; 和加号显示行中缺less一个/但是存在于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
默认的输出格式(最初来自一个称为diff
的程序,如果你想查找更多的信息)被称为“统一差异”。 它基本上包含4种不同types的行:
- 上下文行以单个空间开始,
- 显示插入行的插入行,以
+
开头, - 删除行,以
-
,和开始 - 描述更高级别的东西的元数据行,如正在讨论的文件,用于生成差异的选项,文件是否更改了权限等。
我build议你练习阅读文件的两个版本之间的差异,你确切地知道你改变了什么。 就像那样,当你看到它的时候,你会发现正在发生的事情。
在我的Mac上:
info diff
然后select: Output formats
– > Context
– > Unified format
– > Detailed Unified
:
或者在线人对gnu的相同path下的相同部分:
文件:diff.info,节点:详细统一,下一个:示例统一,上:统一格式
统一格式的详细说明………………………………..
统一的输出格式以双行标题开始,如下所示:
--- FROM-FILE FROM-FILE-MODIFICATION-TIME +++ TO-FILE TO-FILE-MODIFICATION-TIME
时间戳看起来像`2002-02-21 23:30:39.942229878 -0800'来指示date,小数秒的时间和时区。
你可以用`–label = LABEL'选项来改变标题的内容。 请参阅*备注名称::。
接下来是一个或多个区别的区别。 每个大块显示文件不同的一个区域。 统一格式的人像这样:
@@ FROM-FILE-RANGE TO-FILE-RANGE @@ LINE-FROM-EITHER-FILE LINE-FROM-EITHER-FILE...
两个文件共同的行以空格字符开头。 两个文件之间实际上不同的行在左侧打印列中具有以下指示符字符之一:
`+'在这里添加了一行到第一个文件。
` – '从第一个文件中删除了一行。
你的问题还不清楚,你会发现哪一部分比较混乱:真正的差异,或额外的头信息git打印。 为了以防万一,下面是对标题的简要概述。
第一行就像diff --git a/path/to/file b/path/to/file
– 显然它只是告诉你这个区段是什么文件。 如果你设置了布尔型configurationvariablesdiff.mnemonic prefix
,那么a
和b
就会变成更像c
和w
描述性字母(提交和工作树)。
接下来,有一些“模式行” – 这些行给你描述了任何不涉及改变文件内容的改变。 这包括新/删除的文件,重命名/复制的文件和权限更改。
最后,有一个像index 789bd4..0afb621 100644
。 你可能从来不关心它,但这些6位数的hex数字是这个文件的旧的和新的blob的缩写SHA1散列(blob是一个存储原始数据,如文件内容的git对象)。 当然, 100644
是文件的模式 – 最后三位显然是权限; 前三个给出额外的文件元数据信息( SO职位描述 )。
之后,你就可以使用标准的统一差异输出了(就像经典的diff -U
)。 它被分成了大块 – 大块是文件的一部分,其中包含更改和上下文。 每个大块前面都有一对表示相关文件的+
和+
行,然后实际差异是(默认情况下) -
和+
行两边的三行上下文显示删除/添加的行。