git行尾行为与文档不匹配



我看到git用换行符做事,这似乎与我在这个网站和官方文档上看到的一切相矛盾,甚至与它自己的警告消息相矛盾。(或者我的阅读理解能力不好。)这是一个小复制品。

# repro.sh
git --version # 2.27.0.windows.1
mkdir empty
cd empty
echo '* text=auto !eol' > .gitattributes
echo hi > t.txt
git init
git config core.autocrlf false # I guess git attributes overrides this anyway?
git add t.txt # wait what?  warning: LF will be replaced by CRLF in t.txt???  I thought git likes LF?
git commit -m 'the plot thickens'
git cat-file -p `git rev-parse HEAD:t.txt` > temp.txt # get raw blob as its really stored, I hope
od -c t.txt # original file in working dir ends in LF
od -c temp.txt # file from git also ends in LF, despite git warning??
# end of script

这有道理吗?我认为git有时喜欢在";git add"并在结账时进行相反的操作,但我从未听说过它在git add上将普通LF变成CRLF,因为警告似乎有威胁。然后它就不起作用了。签入的文件正是我工作目录中的文件,正如cat文件所验证的那样。那么,为什么要发出警告呢?怎么回事?

消息本身似乎总是有点。。。错误的奇怪的措辞不当--我不知道该怎么称呼它。消息的意图是警告你,有些事情似乎不一致:你将来查看文件的方式可能与现在查看文件的方法不兼容。

考虑到这一点,让我们来了解具体情况:

echo '* text=auto !eol' > .gitattributes

首先,在text=auto上:这将text属性设置为字符串值auto,这告诉Git:请猜测每个文件是文本还是二进制。我个人认为这是个坏主意:你不想让Git猜测。你应该告诉它。Git的猜测通常都很好,但我不喜欢我的软件猜测那么多。:-)

在任何情况下,让我们转到!eol:这意味着将eol属性设置为未指定的状态。这可能不是你想要的。它开始时未指定,所以如果您不想指定它,可以不指定它。!前缀存在,因此您可以更正以前的一些设置:例如,如果默认值应为eol=lf,则可能有:

* eol=lf

但由于JPG文件不应该被屏蔽,因此我们可以覆盖*.jpg:

*.jpg !eol

(尽管*.jpg binary可能更好:它意味着-diff -merge -text,而对于-texteol属性变得不相关)。

因此,到目前为止,我们所拥有的是:文件是文本,当且仅当Git猜测它是文本,并且eol属性未指定

git config core.autocrlf false # I guess git attributes overrides this anyway?

text属性专门覆盖此属性。gitattributes文档在一定程度上说:

如果未指定text属性,Git将使用core.autocrlf配置变量,以确定文件应该转换。

这并没有说明如果text属性被指定(实际上是auto)会发生什么,但我们可以追溯到text=auto:

如果Git决定内容是文本,则在签入时其行尾将转换为LF。当使用CRLF提交文件时,不会进行任何转换。

这只谈论签入。文档没有这么说,但实际上是在git add期间,Git可能会将CRLF变成LF。

git add t.txt # wait what?  warning: LF will be replaced by CRLF in t.txt???

Git在git add期间发现任何可疑情况时会发出这些警告(除非通过其他配置抑制这些警告)。这些警告是,或者至少包括你所看到的,我有时称之为措辞不当(因为没有更好的术语)。不过,我没有更好的方式来表达它们,不要太冗长以至于有问题。

警告:verbose=此处有问题的描述

只有两种内置的LF/CRLF转换:

  • ;在";将CRLF转换为仅LF的转换:这种情况仅在git add期间发生,然后仅在需要或似乎需要时发生。

  • 一个";在离开的路上";仅将LF转换为CRLF的转换:这发生在git checkoutgit reset --hardgit restore(如果使用显式或隐含的--worktree运行)和其他类似操作期间。但是,就像正在进行的CRLF到LF转换一样,只有在需要或似乎需要的情况下才会发生。

这里发生的事情是,Git怀疑在未来的某个时候,你会有LF到CRLF的转换。我认为

你的设置现在没有这样配置,因为你有!eol并且在Linux上(你在Linux上?也许不是:你在版本字符串中说windows)。因此,也许您的设置现在是以这种方式配置的,因为您有!eol并且在Windows上。我不使用Windows,所以我不确定Windows上的默认是什么

同时,正如在索引和工作树中看到的,t.txt具有纯LF的行结尾。如果Git执行从LF到转换(从索引副本到工作树副本)的途中操作,工作树中的t.txt文件将突然出现CRLF行结尾。

这就是这个警告信息的意思。如果将来Git对文件进行文本转换,那么提取Git索引中当前内容的结果将与当前工作树中的实际文件不匹配。Git在这里可以做的一个转换是将LF仅转换为CRLF,而t.txt目前仅为LF。

走到最后几步

git commit -m 'the plot thickens'

这里的情节并没有真正变厚。所有转换都发生在此之前。commit命令只获取存储在Git索引中的t.txt文件(由于存储库是全新的,这是Git索引的唯一文件),并从中进行提交。

git cat-file -p `git rev-parse HEAD:t.txt` > temp.txt
# get raw blob as its really stored, I hope

确实如此。您同样可以从索引中获取:t.txt,或者使用git ls-files --stage来获取blob散列ID。

请注意,git commit步骤没有修改工作树副本。它仍然完好无损。要强制Git将索引副本提取回工作树,请首先删除工作树副本,然后使用任何将重新创建它的Git命令。这将运行提取步骤,根据您的各种配置的要求,该步骤将——或不会——将LF仅转换为CRLF:

rm t.txt
git checkout -- t.txt

您现在可以使用od或类似的工具来查看发生了什么。n变成rn了吗?这告诉Git如何解释该文件的当前设置(core.autocrlfcore.eol以及.git/info/attributes.gitattributes中的各种属性)。

注意:git ls-files --eol从Git2.8开始就能够告诉你更多关于这里发生的事情。它将单独:

  • 检查索引中的内容
  • 检查工作树中的内容;以及
  • 查看应用哪些属性

到当前索引中的每个文件。

最新更新