`git` 显示克隆后更改的文件,没有任何其他操作
`git` shows changed files after cloning, without any other actions
git clone git@github.com:erocarrera/pydot
(35a8d858b) 在具有 git config core.autocrlf input
的 Debian 中显示:
modified: test/graphs/b545.dot
modified: test/graphs/b993.dot
modified: test/graphs/cairo.dot
这些文件有 CRLF 行结尾,例如:
$ file test/graphs/cairo.dot
test/graphs/cairo.dot: UTF-8 Unicode text, with CRLF line terminators
.gitattributes
文件 contains:
*.py eol=lf
*.dot eol=lf
*.txt eol=lf
*.md eol=lf
*.yml eol=lf
*.png binary
*.ps binary
更改core.autocrlf
对这些文件的状态没有影响。删除 .gitattributes
也没有任何效果。使用 dos2unix
更改这些文件不会更改它们的状态(如预期的那样),并且使用 unix2dos
返回显示与 diff
与旧副本没有区别。 ls -lsa
的文件权限看起来没有变化。此外,据我所知,这些文件具有统一的行尾 vi -b
(因此 unix2dos
或 dos2unix
不应该从混合行尾转换为统一行尾,这可以解释这种奇怪的行为)。我正在使用 git
版本 2.11.0.
git
认为发生了什么变化?
有点相关:
- Git status shows files as changed even though contents are the same
- Files showing as modified directly after git clone
- Cloning a git repo, and it already has a dirty working directory... Whaaaaa?
在搜索多个讨论时,我没有找到解释此行为的答案。这个问题来自 pydot
# 163.
更详细:
git status
On branch master
Your branch is up-to-date with 'origin/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: test/graphs/b545.dot
modified: test/graphs/b993.dot
modified: test/graphs/cairo.dot
no changes added to commit (use "git add" and/or "git commit -a")
git diff test/graphs/b993.dot
warning: CRLF will be replaced by LF in test/graphs/b993.dot.
The file will have its original line endings in your working directory.
diff --git a/test/graphs/b993.dot b/test/graphs/b993.dot
index e87e112..8aa0872 100644
--- a/test/graphs/b993.dot
+++ b/test/graphs/b993.dot
@@ -1,10 +1,10 @@
-diGraph G{
-graph [charset="utf8"]
-1[label="Umlaut"];
-2[label="ü"];
-3[label="ä"];
-4[label="ö"];
-1->2;
-1->3;
-1->4;
-}
+diGraph G{
+graph [charset="utf8"]
+1[label="Umlaut"];
+2[label="ü"];
+3[label="ä"];
+4[label="ö"];
+1->2;
+1->3;
+1->4;
+}
更新:
出于好奇,我提交了其中一个文件,转储了 git log -1 -p > diff
,并且 vi -b diff
显示 git
规范化
1 commit 2021d6adc1bc8978fa08d729b3f4d565f9b89651
2 Author:
3 Date:
4
5 DRAFT: experiment to see what changed
6
7 diff --git a/test/graphs/b545.dot b/test/graphs/b545.dot
8 index ebd3e8f..2c33f91 100644
9 --- a/test/graphs/b545.dot
10 +++ b/test/graphs/b545.dot
11 @@ -1,9 +1,9 @@
12 -digraph g {^M
13 -^M
14 -"N11" ^M
15 - [^M
16 - shape = record^M
17 - label = "<p0>WFSt|1571 as Ref: 1338 D"^M
18 -]^M
19 -N11ne -> N11:p0^M
20 -}^M
21 +digraph g {
22 +
23 +"N11"
24 + [
25 + shape = record
26 + label = "<p0>WFSt|1571 as Ref: 1338 D"
27 +]
28 +N11ne -> N11:p0
29 +}
其他奇怪的观察结果:git checkout
克隆后的任何这些文件都没有任何效果。 在 上述提交之后,文件 b545.dot
在工作目录中继续具有 CLRF 行结尾。应用 dos2unix
后跟 unix2dos
并没有使 git
认为它已经改变(而在提交之前它确实发生了变化,可能是因为 committed 文件有 CLRF 行结尾)。
Changing core.autocrlf
has no effect on the status of these files
它应该,但只有在再次克隆之后:
git config --global core.autocrlf false
git clone git@github.com:erocarrera/pydot pydot2
cd pydot2
git status
这会在全局范围内停用 core.autocrlf
,但这只是为了测试。
这恰恰发生了因为那些文件是以CRLF结尾提交的,但是.gitattributes
文件说要以CRLF结尾提交它们仅 LF 结尾。
Git 可以并且将在两个地方进行 CRLF-vs-LF-only 转换:
从索引提取到工作树期间。存储在提交或索引中的文件总是假定处于 "clean" 状态,但是当从索引中提取该文件到工作树时,Git 应该应用由.gitattributes
以 "change LF-only to CRLF" 的形式,例如,也以 Git 所谓的 涂抹过滤器 .
[ 的形式=66=]
在将文件从工作树复制回索引期间。存储在工作树中的文件处于 "smudged" 状态,因此此时,Git 应该应用任何 "cleaning" 转换:例如,将 CR-LF 更改为 LF-only,并应用 clean 过滤器.
请注意,这些转换 可以 在两个点上发生。这并不意味着它们 将 出现在两个点,只是这两个可能的位置。正如 .gitattributes
文档所述,实际转换为:
eol=lf
:none 在索引 -> 工作树上; CR-LF 到 LF-only on work-tree -> index
eol=crlf
:索引 -> 工作树上的 LF-only 到 CR-LF; none 在工作树上 -> 索引
现在,实际位于存储库中的文件,存储在提交中,是纯只读的。它可以 永远不会 在该提交中更改。更准确地说,提交标识(通过哈希 ID)一棵树,该树标识(通过哈希 ID)具有任何内容的 blob。这些散列 ID 本身就是对象内容的密码校验和,因此它们自然都是只读的:如果我们尝试更改内容,我们得到的是一个新的、不同的对象,具有新的、不同的散列 ID。
因为 git checkout
实际上是通过将原始哈希 ID 从提交的树复制到索引来工作的,所以存储在索引中的文件版本必须与存储在提交中的文件版本相同。
因此,如果不知何故——不管如何——提交的文件的形式与.gitattributes
指示Git的方式不一致,这些文件将成为工作树中的 "dirty",而不管 您 没有对它们做任何事情!如果你要 git add
有问题的三个文件,那会将它们从工作树复制到索引,因此从它们的行尾删除回车 -returns 。因此,在 git status
术语中,它们已修改但尚未准备提交。
删除工作树版本中的回车 returns 使它们处于相同状态:它们根据索引中的内容进行了修改,因为 git add
现在将保留它们的 LF - 仅行结尾不变,生成索引中的新的不同文件。
一个更有趣的问题是:他们是如何进入错误状态的提交的?这不是我们可以回答的问题:只有那些做出这些提交的人可以产生那个答案。我们只能推测。实现此目的的一种方法是在没有 .gitattributes
生效的情况下添加和提交文件,然后将 .gitattributes
设置为生效而无需再次 git add
-ing 文件。这样,CR-LF 结尾进入某人的索引并因此进入该用户的提交,即使 .gitattributes
文件 now 说(但之前没有说)任何new git add
应该去掉马车 returns.
感谢@torek 的解释(与我的 一致)。
总而言之,非对称 git
配置导致 commit(checkout(Index))
不是恒等映射。在索引中使用 CRLF,此特定配置检查了 CRLF,但在输入转换生效后 (eol=lf
),git
将提交 LF 而不是 CRLF。
造成这种混淆的根本原因是比较:
- 我在工作目录中看到的文件,
- 已提交文件。
这不显示文件是否已更改。应该比较的是 git
在应用输入转换后将提交 与已经提交的内容。显然,如果这两项不同,则文件 已 更改。
根据这一推理,可以声明存储库 "unstable",因为它认为自己在没有与世界交互的情况下被修改。这支持通过将提交的文件更改为 LF 或更改 .gitattributes
(我更喜欢提交 LF)来避免这种状态。
在这种情况下,git
会为工作目录中的 LF 和 CRLF 提交 LF,因此 dos2unix
和 unix2dos
不会对提交结果产生影响,因此两者都不会到文件的状态。
git clone git@github.com:erocarrera/pydot
(35a8d858b) 在具有 git config core.autocrlf input
的 Debian 中显示:
modified: test/graphs/b545.dot
modified: test/graphs/b993.dot
modified: test/graphs/cairo.dot
这些文件有 CRLF 行结尾,例如:
$ file test/graphs/cairo.dot
test/graphs/cairo.dot: UTF-8 Unicode text, with CRLF line terminators
.gitattributes
文件 contains:
*.py eol=lf
*.dot eol=lf
*.txt eol=lf
*.md eol=lf
*.yml eol=lf
*.png binary
*.ps binary
更改core.autocrlf
对这些文件的状态没有影响。删除 .gitattributes
也没有任何效果。使用 dos2unix
更改这些文件不会更改它们的状态(如预期的那样),并且使用 unix2dos
返回显示与 diff
与旧副本没有区别。 ls -lsa
的文件权限看起来没有变化。此外,据我所知,这些文件具有统一的行尾 vi -b
(因此 unix2dos
或 dos2unix
不应该从混合行尾转换为统一行尾,这可以解释这种奇怪的行为)。我正在使用 git
版本 2.11.0.
git
认为发生了什么变化?
有点相关:
- Git status shows files as changed even though contents are the same
- Files showing as modified directly after git clone
- Cloning a git repo, and it already has a dirty working directory... Whaaaaa?
在搜索多个讨论时,我没有找到解释此行为的答案。这个问题来自 pydot
# 163.
更详细:
git status
On branch master
Your branch is up-to-date with 'origin/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: test/graphs/b545.dot
modified: test/graphs/b993.dot
modified: test/graphs/cairo.dot
no changes added to commit (use "git add" and/or "git commit -a")
git diff test/graphs/b993.dot
warning: CRLF will be replaced by LF in test/graphs/b993.dot.
The file will have its original line endings in your working directory.
diff --git a/test/graphs/b993.dot b/test/graphs/b993.dot
index e87e112..8aa0872 100644
--- a/test/graphs/b993.dot
+++ b/test/graphs/b993.dot
@@ -1,10 +1,10 @@
-diGraph G{
-graph [charset="utf8"]
-1[label="Umlaut"];
-2[label="ü"];
-3[label="ä"];
-4[label="ö"];
-1->2;
-1->3;
-1->4;
-}
+diGraph G{
+graph [charset="utf8"]
+1[label="Umlaut"];
+2[label="ü"];
+3[label="ä"];
+4[label="ö"];
+1->2;
+1->3;
+1->4;
+}
更新:
出于好奇,我提交了其中一个文件,转储了 git log -1 -p > diff
,并且 vi -b diff
显示 git
规范化
1 commit 2021d6adc1bc8978fa08d729b3f4d565f9b89651
2 Author:
3 Date:
4
5 DRAFT: experiment to see what changed
6
7 diff --git a/test/graphs/b545.dot b/test/graphs/b545.dot
8 index ebd3e8f..2c33f91 100644
9 --- a/test/graphs/b545.dot
10 +++ b/test/graphs/b545.dot
11 @@ -1,9 +1,9 @@
12 -digraph g {^M
13 -^M
14 -"N11" ^M
15 - [^M
16 - shape = record^M
17 - label = "<p0>WFSt|1571 as Ref: 1338 D"^M
18 -]^M
19 -N11ne -> N11:p0^M
20 -}^M
21 +digraph g {
22 +
23 +"N11"
24 + [
25 + shape = record
26 + label = "<p0>WFSt|1571 as Ref: 1338 D"
27 +]
28 +N11ne -> N11:p0
29 +}
其他奇怪的观察结果:git checkout
克隆后的任何这些文件都没有任何效果。 在 上述提交之后,文件 b545.dot
在工作目录中继续具有 CLRF 行结尾。应用 dos2unix
后跟 unix2dos
并没有使 git
认为它已经改变(而在提交之前它确实发生了变化,可能是因为 committed 文件有 CLRF 行结尾)。
Changing
core.autocrlf
has no effect on the status of these files
它应该,但只有在再次克隆之后:
git config --global core.autocrlf false
git clone git@github.com:erocarrera/pydot pydot2
cd pydot2
git status
这会在全局范围内停用 core.autocrlf
,但这只是为了测试。
这恰恰发生了因为那些文件是以CRLF结尾提交的,但是.gitattributes
文件说要以CRLF结尾提交它们仅 LF 结尾。
Git 可以并且将在两个地方进行 CRLF-vs-LF-only 转换:
从索引提取到工作树期间。存储在提交或索引中的文件总是假定处于 "clean" 状态,但是当从索引中提取该文件到工作树时,Git 应该应用由
[ 的形式=66=].gitattributes
以 "change LF-only to CRLF" 的形式,例如,也以 Git 所谓的 涂抹过滤器 .在将文件从工作树复制回索引期间。存储在工作树中的文件处于 "smudged" 状态,因此此时,Git 应该应用任何 "cleaning" 转换:例如,将 CR-LF 更改为 LF-only,并应用 clean 过滤器.
请注意,这些转换 可以 在两个点上发生。这并不意味着它们 将 出现在两个点,只是这两个可能的位置。正如 .gitattributes
文档所述,实际转换为:
eol=lf
:none 在索引 -> 工作树上; CR-LF 到 LF-only on work-tree -> indexeol=crlf
:索引 -> 工作树上的 LF-only 到 CR-LF; none 在工作树上 -> 索引
现在,实际位于存储库中的文件,存储在提交中,是纯只读的。它可以 永远不会 在该提交中更改。更准确地说,提交标识(通过哈希 ID)一棵树,该树标识(通过哈希 ID)具有任何内容的 blob。这些散列 ID 本身就是对象内容的密码校验和,因此它们自然都是只读的:如果我们尝试更改内容,我们得到的是一个新的、不同的对象,具有新的、不同的散列 ID。
因为 git checkout
实际上是通过将原始哈希 ID 从提交的树复制到索引来工作的,所以存储在索引中的文件版本必须与存储在提交中的文件版本相同。
因此,如果不知何故——不管如何——提交的文件的形式与.gitattributes
指示Git的方式不一致,这些文件将成为工作树中的 "dirty",而不管 您 没有对它们做任何事情!如果你要 git add
有问题的三个文件,那会将它们从工作树复制到索引,因此从它们的行尾删除回车 -returns 。因此,在 git status
术语中,它们已修改但尚未准备提交。
删除工作树版本中的回车 returns 使它们处于相同状态:它们根据索引中的内容进行了修改,因为 git add
现在将保留它们的 LF - 仅行结尾不变,生成索引中的新的不同文件。
一个更有趣的问题是:他们是如何进入错误状态的提交的?这不是我们可以回答的问题:只有那些做出这些提交的人可以产生那个答案。我们只能推测。实现此目的的一种方法是在没有 .gitattributes
生效的情况下添加和提交文件,然后将 .gitattributes
设置为生效而无需再次 git add
-ing 文件。这样,CR-LF 结尾进入某人的索引并因此进入该用户的提交,即使 .gitattributes
文件 now 说(但之前没有说)任何new git add
应该去掉马车 returns.
感谢@torek 的解释(与我的
总而言之,非对称 git
配置导致 commit(checkout(Index))
不是恒等映射。在索引中使用 CRLF,此特定配置检查了 CRLF,但在输入转换生效后 (eol=lf
),git
将提交 LF 而不是 CRLF。
造成这种混淆的根本原因是比较:
- 我在工作目录中看到的文件,
- 已提交文件。
这不显示文件是否已更改。应该比较的是 git
在应用输入转换后将提交 与已经提交的内容。显然,如果这两项不同,则文件 已 更改。
根据这一推理,可以声明存储库 "unstable",因为它认为自己在没有与世界交互的情况下被修改。这支持通过将提交的文件更改为 LF 或更改 .gitattributes
(我更喜欢提交 LF)来避免这种状态。
在这种情况下,git
会为工作目录中的 LF 和 CRLF 提交 LF,因此 dos2unix
和 unix2dos
不会对提交结果产生影响,因此两者都不会到文件的状态。