`git`显示克隆后更改的文件,没有任何其他操作

时间:2017-11-25 23:11:47

标签: git newline eol gitattributes core.autocrlf

带有git clone git@github.com:erocarrera/pydot的Debian中的

git config core.autocrlf input35a8d858b)显示:

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而言,文件具有统一的行结尾(因此不应该是unix2dosdos2unix从混合行转换为统一行结尾的情况,可以解释这种奇怪的行为)。我正在使用git版本2.11.0。

git认为改变了什么?

有点相关:

  1. Git status shows files as changed even though contents are the same
  2. Files showing as modified directly after git clone
  3. Cloning a git repo, and it already has a dirty working directory... Whaaaaa?
  4. 在我搜索几个讨论期间,我没有找到解释此行为的答案。此问题源自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 > diffvi -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认为它已更改(而在提交之前,可能是因为已提交的文件具有CLRF行结局)。

3 个答案:

答案 0 :(得分:3)

这恰好发生在,因为这些文件 是通过CRLF结尾提交的,但.gitattributes文件表示只使用LF结尾提交它们。

Git可以并且将在两个地方进行CRLF-vs-LF转换:

  • 从索引到工作树的提取过程中。存储在提交或索引中的文件始终假定为&#34; clean&#34;状态,但是当从索引中提取该文件到工作树时,Git应该以{#1}}的形式应用.gitattributes指示的任何转换,例如,改变LF-only到CRLF&#34;以及Git称之为涂抹过滤器的形式。

  • 在从工作树复制文件到索引期间。存储在工作树中的文件位于&#34;污迹&#34;在这一点上,Git应该应用任何&#34;清洁&#34;转换:例如,将CR-LF更改为仅LF,并应用清理过滤器

请注意,可以发生这两种转换。这并不意味着他们发生在两个点上,只是这些是两个可能的地方。正如.gitattributes文档说明的那样,实际转换是:

  • eol=lf:无索引 - &gt;工作树;仅在工作树上使用CR-LF到LF - &gt;索引
  • eol=crlf:LF-only to CR-LF on index - &gt;工作树;工作树上没有 - &gt;索引

现在,存储在提交中的存储库中实际存在的文件纯粹是只读的。它可以永远在提交内部进行更改。更确切地说,提交(通过散列ID)标识(通过散列ID)标识具有其具有的任何内容的blob的树。这些哈希ID本身就是对象内容的加密校验和,因此它们自然都是只读的:如果我们尝试更改内容,我们得到的是具有新的不同哈希ID的新的不同对象。

因为git checkout实际上是通过将提交树中的原始哈希ID复制到索引来实现的,所以索引中存储的文件版本必须与提交中存储的文件版本相同。

因此,如果以某种方式 - 无论如何 - 提交的文件的形式与.gitattributes指示Git要做的事情不一致,那么这些文件将会变得非常糟糕。 #34;在工作树中,无论没有对他们做任何事情!如果你有git add这三个文件,那么它们会将它们从工作树复制到索引,因此从它们的行结尾中删除回车符。因此,它们以git status的形式被修改但尚未进行提交。

在工作树版本中删除回车会使它们处于相同的状态:它们会根据索引中的内容进行修改,因为git add现在将保留其LF - 只有行结尾不变,产生索引中的新的不同文件。

一个更有趣的问题是:他们是如何进入处于错误状态的提交的?这不是我们可以回答的问题:只有那些提交这些提交的人才能产生这样的答案。我们只能推测。实现此目的的一种方法是在没有.gitattributes生效的情况下添加和提交文件,然后将.gitattributes设置为生效而不再使用git add文件。这样,CR-LF结尾进入某人的索引,因此进入该用户的提交,即使.gitattributes文件现在表示(但没有早先说)任何 new git add都应该去掉回车。

答案 1 :(得分:2)

  

更改core.autocrlf不会影响这些文件的状态

它应该,但只有在再次克隆之后:

git config --global core.autocrlf false

git clone git@github.com:erocarrera/pydot pydot2
cd pydot2
git status

这会在全球范围内停用core.autocrlf,但这只是为了测试。

答案 2 :(得分:1)

感谢@torek的解释(同意我的conjecture)。

总之,非对称git配置导致commit(checkout(Index))不是身份映射。在索引中使用CRLF时,此特定配置检出CRLF,但在生效的输入转换(eol=lf)之后,git将提交LF而不是CRLF。

这种混淆的根本原因是比较:

  • 文件我在工作目录中看到了
  • 已提交的文件。

这并不表示文件是否已更改。在将输入转换应用于已提交的内容之后,应该比较的是git 将提交的内容。显然,如果这两个项目不同,则文件更改。

根据这个推理,可以声明存储库&#34;不稳定&#34;,因为它认为自己在没有与世界交互的情况下被修改。这支持通过将提交的文件更改为LF或更改.gitattributes(我更喜欢提交LF)来避免此状态。

在这种情况下,git将为工作目录中的LF和CRLF提交LF,因此dos2unixunix2dos对提交结果没有影响,因此对于提交结果都没有影响档案的状态。