git-diff
的手册页很长,并解释了许多初学者似乎不需要的案例。例如:
git diff origin/master
答案 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%相同(用于检测此重命名)。 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;
的正确行:它跳过了行func1
和1
。
这个很棒的功能通常可以准确地告诉每个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输出。
比较档案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中,最后,实际修改了一些行:两条“ - ”行被改为看起来像下面的两条“+”行。
答案 6 :(得分:3)
从你的问题中不清楚你发现哪些差异令人困惑:实际的差异,或git打印的额外标题信息。以防万一,这里是标题的快速概述。
第一行类似于diff --git a/path/to/file b/path/to/file
- 显然它只是告诉你这个差异部分的文件是什么。如果设置布尔配置变量diff.mnemonic prefix
,则a
和b
将更改为更具描述性的字母,如c
和w
(提交和工作树)
接下来,有“模式行” - 行,为您提供任何不涉及更改文件内容的更改的说明。这包括新/已删除的文件,重命名/复制的文件以及权限更改。
最后,有一条像index 789bd4..0afb621 100644
这样的行。您可能永远不会关心它,但是这些6位十六进制数字是此文件的旧和新Blob的缩写SHA1哈希值(blob是存储原始数据(如文件内容)的git对象)。当然,100644
是文件的模式 - 后三位数显然是权限;前三个提供额外的文件元数据信息(SO post describing that)。
之后,您将进入标准的统一差异输出(就像经典的diff -U
)。它被分成了一堆 - 一个大块头是文件的一部分,包含变化及其背景。每个hunk前面都有一对---
和+++
行表示有问题的文件,然后实际的差异是(默认情况下)-
两侧的三行上下文+
行显示删除/添加的行。