为什么git使用不同的文本属性给出不同的diff结果?
我发现当我更改.gittributes中的text属性时,git会给我一个不同的diff结果。谁能给我解释一下吗 以下是我所做的:为什么git使用不同的文本属性给出不同的diff结果?,git,Git,我发现当我更改.gittributes中的text属性时,git会给我一个不同的diff结果。谁能给我解释一下吗 以下是我所做的: 添加一个.gittributes文件,其内容为*-text 添加另一个包含某些内容的文本文件 承诺 然后我添加了一行“ddd”和git diff,结果与预期一致 diff --git a/abc.txt b/abc.txt index aa3b7ba..911ddef 100644 --- a/abc.txt +++ b/abc.txt @@ -2,3 +2,5 @
*-text
git diff
,结果与预期一致
diff --git a/abc.txt b/abc.txt
index aa3b7ba..911ddef 100644
--- a/abc.txt
+++ b/abc.txt
@@ -2,3 +2,5 @@ aaa
bbb
ccc
+ddd^M
+
但当我再次将.gittributes更改为*text
和diff时,git给了我以下信息:
diff --git a/abc.txt b/abc.txt
index aa3b7ba..9a3ed4f 100644
--- a/abc.txt
+++ b/abc.txt
@@ -1,4 +1,6 @@
-aaa
-bbb
-ccc
+aaa
+bbb
+ccc
+ddd
+
据我所知,text属性仅用于eol规范化。为什么它会影响差异结果?这一切变得有点复杂,因为这里有许多运动部件。首先,让我们讨论一下git diff和“树”。然后让我们看看Git可以做什么样的线端修改,以及Git什么时候做。然后,让我们具体看看
.gittributes
中的*-text
和*text
是什么意思。最后,让我们一起考虑所有的代码:<代码> git diff > /p>
git diff
命令比较两个“树”
默认情况下,有一些特定的模式用于比较文件,但我们不打算在这里讨论那些运行git diff
比较两个git称之为树的模式。树是文件的集合,其中每个文件都有一个名称:A.txt
,abc.txt
,dir/c.txt
,dir/sub/d.txt
,等等(但本例到此为止)。此树的顶层是包含a.txt
、abc.txt
、子目录/文件夹dir
,Git称之为子树的目录/文件夹(使用您喜欢的术语)。名为dir
的子树包含c.txt
和另一个子树sub
,最后一个子树包含d.txt
Git想要两棵这样的树。一个通常是提交,另一个通常也是第二个(可能不同)提交。这种git diff
比较两个提交树的内容
但是,默认情况下,git diff
以索引作为第一棵树开始。您的索引(Git称之为索引,有时称之为暂存区域或缓存)是Git主要用于构建下一次提交的特殊实体。索引也有一系列子任务,这就是为什么它有这三个不同的名称。(我们将在这个答案的末尾看到一个额外的任务。)索引从当前提交中所有内容的副本开始:您在上运行的提交git checkout
。因此,至少在最初,索引与当前提交匹配
你也有一个工作树。工作树非常简单:它是你工作的地方。Git需要有一个工作树,因为Git存储的所有文件,无论是提交还是索引,都是一种特殊的、高度压缩的、仅限Git的格式。(从技术上讲,这些是Git blob对象。)计算机上的大多数程序,包括您自己的文本编辑器和编译器等,都不能处理仅Git文件。这些程序需要文件具有正常的每日文件格式,因此Git会在工作树中将仅限Git的文件提取为正常格式
每次您git添加一个类似abc.txt
的文件时,git都会将该文件从您的工作树复制到特殊的git-only格式,并将特殊的blob hash ID填充到索引中。因此,如果您在工作树中更改了一个文件,然后git添加更改后的文件,git会将更改复制到存储库中(作为blob对象),并将新的哈希ID放入索引中,将以前的索引版本替换为从工作树复制的版本。请注意,索引中不断有一些版本的abc.txt
。首先,它具有当前提交的版本。然后,在git add abc.txt
之后,它就有了工作树的版本(尽管现在是只使用git的特殊格式)
无论如何,这就是我们需要知道的关于索引的大部分内容:作为Git“树”的一个变体,它包含将进入下一次提交的所有内容。最初,这与我们刚刚签出的提交中的所有内容相同
我们已经提到了工作树,它是一种普通的、不适合Git的形式。尽管如此,各种Git命令也可以将其作为树来使用,而Git diff
就是其中之一。Git将把每个目录/文件夹视为一个子树,而工作树本身就是顶层树。树中的每个文件都像一个Git blob对象,但每个文件都有自己的格式,在计算机上是正常的,而不是只使用Git的特殊格式
因此:在没有参数的情况下运行git diff
,将索引与工作树进行比较。在这两种情况下,Git使用它们就像它们是Git的内部“树”对象一样。不过,重要的是要记住Git正在比较什么:现在是索引和工作树。这一点在一瞬间变得更加重要
线端修改
Git的特殊的、内部的、仅限Git的格式是为了适合Git而设计的。它也是由Linus Torvalds设计的,所以正如您所预料的,它对Linux非常友好。因此,您可以说它更希望文本文件的行以纯换行符\n
字符结尾,而不是DOS/Windows样式的CRLF(或\r\n
)序列。这有点言过其实:Git真的一点都不在乎这件事。但是很多使用Git的人都很在意,不管出于什么原因,不管你喜欢与否,\n
-目前只是文本文件的正常Git内部格式。你不必用这个,但很多人都用这个
同时,你的工作树,在你的计算机上使用你的计算机的首选(“正常”)格式,很可能有文本文件有CR-LF(我将从这里开始拼写没有连字符)行结尾,如果y
current commit index work-tree
-------------- ------- ---------
a.txt a.txt a.txt
abc.txt abc.txt abc.txt
$ vis foo.txt
this file has\^M
crlf line endings\^M
$ git status
On branch master
nothing to commit, working tree clean
$ echo '* text' > .gitattributes
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitattributes
nothing added to commit but untracked files present (use "git add" to track)
$ touch foo.txt
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: foo.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitattributes
no changes added to commit (use "git add" and/or "git commit -a")
$ git diff | vis
warning: CRLF will be replaced by LF in foo.txt.
The file will have its original line endings in your working directory.
diff --git a/foo.txt b/foo.txt
index 257cbae..6bf00d0 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1,2 +1,2 @@
-this file has\^M
-crlf line endings\^M
+this file has
+crlf line endings
$ echo '* -text' > .gitattributes
$ git diff
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitattributes
nothing added to commit but untracked files present (use "git add" to track)