Answers:
让我们看一下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] == '-') {
让我们逐行分析此补丁。
第一行
差异--git a / builtin-http-fetch.cb / http-fetch.c是形式中的“ git diff”标头
diff --git a/file1 b/file2
。该a/
和b/
,除非重命名/复制参与(如在我们的例子中)的文件名是相同的。该--git
是意味着DIFF处于“混帐”的diff格式。接下来是一个或多个扩展标题行。前三个
相似指数95% 从Builtin-http-fetch.c重命名 重命名为http-fetch.c告诉我们,文件已从
builtin-http-fetch.c
更改为http-fetch.c
,并且这两个文件95%相同(用于检测此重命名)。索引f3e63d7..e8f44ba 100644告诉我们给定文件的模式(
100644
意味着它是普通文件,而不是例如symlink,并且它没有可执行权限位),以及关于preimage(给定更改之前文件的版本)和postimage(更改后的文件版本)。git am --3way
如果补丁本身无法应用,则使用此行尝试进行三向合并。接下来是两行统一的diff头
--- a / builtin-http-fetch.c +++ b / http-fetch.c相较于
diff -U
结果,它在源(原映像)和目标(后映像)文件名之后没有从文件修改时间或到文件修改时间。如果创建文件,则源为/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]
。从文件范围为形式-<start line>,<number of lines>
,至文件范围为+<start line>,<number of lines>
。起始线和行数分别指前图像和后图像中块的位置和长度。如果未显示行数,则表示为0。
可选的标头显示了每次更改发生的C函数(如果它是C文件)(例如-p
GNU 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;
表示已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中不是)。
正如Donal Fellows所说的,最好是在真实示例中练习阅读差异,在这些示例中您知道自己已更改了什么。
参考文献:
git blame -C -C
,这就是它的工作方式;这是Git的设计决策。git diff格式仅向用户显示相似性(或不相似性)索引。
[header]
是最接近的前导,例如在大块之前的函数的开头。在大多数情况下,此行包括diff块所在的函数的名称。这是可以配置的,其中diff
gitattribute设置为diff驱动程序,并且diff驱动程序包括xfuncname
配置变量。
@@ -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 @@
第二个大块是类似的:
在旧文件上,我们有6行,从旧文件的第11行开始:
11
12
13
14
15
16
在新文件上,我们有4行,从新文件的第9行开始:
11
12
13
16
请注意,该行11
是新文件的第9行,因为我们已经删除了前一个大块的2行:2和3。
大块头
根据您的git版本和配置,您还可以在该行旁边获得一个代码@@
行,例如func1() {
in:
@@ -4,7 +4,6 @@ func1() {
也可以使用-p
plain标志获得diff
。
示例:旧文件:
func1() {
1;
2;
3;
4;
5;
6;
7;
8;
9;
}
如果我们删除line 6
,则差异显示:
@@ -4,7 +4,6 @@ func1() {
3;
4;
5;
- 6;
7;
8;
9;
请注意,这不是正确的行func1
:它跳过了行1
和2
。
这个很棒的功能通常会准确告诉每个块属于哪个函数或类,这对于解释差异非常有用。
有关选择标头的算法的确切工作方式,请参见:git diff hunk标头中的摘录来自何处?
@@ -1,6 +1,4 @@
请不要读-1
的minus one
或+1
作为plus one
,而不是看这是line 1 to 6
在旧的(第一个)文件。注意这里 - implies "old"
不要减。顺便说一句,感谢您的澄清...哈希。
+1,4
说这对应于第二个文件的第1至4行 ”。这是因为+1,4
可能引用了非临时性上下文行。相反,“ +1,4
”的实际含义是“ 在该文件的 “ 版本 ”中有几4
行(即上下文行) ”。理解的含义是很重要的+
,-
和<whitespace>
那些行的开头,因为它适用于帅哥的解释。一个更直观的示例:youtube.com/watch?
这是简单的例子。
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
是这2个文件的Blob ID100644
是“模式位”,表示这是一个常规文件(不是可执行文件,不是符号链接)--- 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
644
)将以八进制读取(值:1、2、4分别为eXecute,Write和Read权限),并依次对应于所有者(用户),组,其他权限。简而言之,644
这意味着如果写成符号u=rw,og=r
,对所有人来说都是可读的,但只有所有者才能写。左边的其他数字编码其他信息,例如它是否是符号链接等。可以在github.com/git/git/blob/…中看到值,该位置的前1个是“常规文件”。
在我的Mac上:
info diff
然后选择:Output formats
-> Context
-> Unified format
-> Detailed Unified
:
或在gnu上的在线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选项更改标题的内容。请参阅* Note备用名称::。
接下来是一个或多个差异。每个块显示一个文件不同的区域。统一格式的块看起来像这样:
@@ FROM-FILE-RANGE TO-FILE-RANGE @@ LINE-FROM-EITHER-FILE LINE-FROM-EITHER-FILE...
这两个文件共有的行以空格字符开头。这两个文件之间实际不同的行在左侧打印列中具有以下指示符之一:
`+'在第一个文件中添加了一行。
`-'这里从第一个文件中删除了一行。
从您的问题尚不清楚,您会发现diff的哪一部分令人困惑:实际上是diff,还是git打印出了额外的标头信息。以防万一,这里是标题的快速概述。
第一行是这样的diff --git a/path/to/file b/path/to/file
-显然,它只是告诉您diff的这一部分用于什么文件。如果设置boolean config变量diff.mnemonic prefix
,则a
和b
将更改为更具描述性的字母,如c
和w
(提交和工作树)。
接下来,有“模式行”-为您提供不涉及更改文件内容的任何更改的描述。这包括新/删除的文件,重命名/复制的文件以及权限更改。
最后,有一行类似index 789bd4..0afb621 100644
。您可能永远不会在意它,但是这些6位十六进制数字是此文件的旧Blob和新Blob的缩写SHA1哈希值(Blob是一个git对象,用于存储原始数据(如文件内容))。当然,这100644
是文件的模式-后三位显然是权限;前三个给出了额外的文件元数据信息(SO对此进行了描述)。
之后,您就可以进行标准的统一diff输出(就像classic一样diff -U
)。它分为多个块-块是文件的一部分,其中包含更改及其上下文。每个大块前面都带有一对---
和+++
线,它们代表所讨论的文件,然后,实际的差异(默认情况下)是(默认情况下)-
和+
线两边的三行上下文,这些行表示已删除/添加的行。
index
行。确认git hash-object ./file