我看到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
,而对于-text
,eol
属性变得不相关)。
因此,到目前为止,我们所拥有的是:文件是文本,当且仅当Git猜测它是文本,并且eol属性未指定 如果未指定 这并没有说明如果 如果Git决定内容是文本,则在签入时其行尾将转换为LF。当使用CRLF提交文件时,不会进行任何转换。 这只谈论签入。文档没有这么说,但实际上是在 Git在 只有两种内置的LF/CRLF转换: ;在";将CRLF转换为仅LF的转换:这种情况仅在 一个";在离开的路上";仅将LF转换为CRLF的转换:这发生在 这里发生的事情是,Git怀疑在未来的某个时候,你会有LF到CRLF的转换。我认为git config core.autocrlf false # I guess git attributes overrides this anyway?
text
属性专门覆盖此属性。gitattributes文档在一定程度上说:text
属性,Git将使用core.autocrlf
配置变量,以确定文件应该转换。text
属性被指定(实际上是auto
)会发生什么,但我们可以追溯到text=auto
:git add
期间,Git可能会将CRLF变成LF。git add t.txt # wait what? warning: LF will be replaced by CRLF in t.txt???
git add
期间发现任何可疑情况时会发出这些警告(除非通过其他配置抑制这些警告)。这些警告是,或者至少包括你所看到的,我有时称之为措辞不当(因为没有更好的术语)。不过,我没有更好的方式来表达它们,不要太冗长以至于有问题。警告:verbose=此处有问题的描述
git add
期间发生,然后仅在需要或似乎需要时发生。git checkout
、git reset --hard
、git restore
(如果使用显式或隐含的--worktree
运行)和其他类似操作期间。但是,就像正在进行的CRLF到LF转换一样,只有在需要或似乎需要的情况下才会发生。!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.autocrlf
、core.eol
以及.git/info/attributes
和.gitattributes
中的各种属性)。
注意:git ls-files --eol
从Git2.8开始就能够告诉你更多关于这里发生的事情。它将单独:
- 检查索引中的内容
- 检查工作树中的内容;以及
- 查看应用哪些属性
到当前索引中的每个文件。