我有一台Mac,可以在其上克隆存储库。当我结帐任何分支时,我所拥有的.json
文件最终显示为“已修改”。
这是我尝试过的事情清单。
git clone <blah>.git
git checkout release
git checkout -- $(git ls-files -m)
文件仍显示为已修改:
me@box> cat ~/.gitattributes
text=auto
-crlf
me@box> cat .gitconfig
[core]
autocrlf = true
我也尝试将autocrlf
与auto
和false
一起使用。
有什么办法可以解决这个问题?
答案 0 :(得分:2)
您可能希望.gitattributes
(而不是~/.gitattributes
)包含行* -text
或*.json -text
。您可能要从autocrlf = true
或.git/config
或$XDG_CONFIG_HOME/git/config
或$HOME/.config/git/config
中删除任何$HOME/.gitconfig
设置。您可能希望更好地控制其他文件,具体取决于您和其他人在哪些系统上使用这些存储库的方式。
一方面,这些行是错误的:
text=auto -crlf
.gitattributes
中的条目应采用文件名或模式的形式,其后是空格(通常是一个制表符,但也可以使用空格),然后是一个或多个属性。在这里,文件名/模式部分丢失了,尽管Git并不这样认为:这告诉Git要做的是将 no 属性应用于所有名为text=auto
的文件,然后应用所有名为-crlf
的文件的 no 属性。由于可能没有带有这些有趣名称的文件,因此这不会对任何文件应用任何属性,因此无效。
(您可能想要* text=auto
和/或* -crlf
,但请参阅下文。)
类似(但可能不是问题),这个可能可能是错误的位置:
cat ~/.gitattributes
Git在多个位置查找属性文件。最重要的一个是在存储库工作树的顶层的.gitattributes
,即.gitattributes
,而不是~/.gitattributes
。您可以 让Git咨询core.attributesFile
,您可以将其设置为~/.gitattributes
。如果未设置$XDG_CONFIG_HOME/git/attributes
,则Modern Git将寻找$HOME/.config/git/attributes
或core.attributesFile
,因此,除非您配置了全局core.attributesFile
设置,否则它将不会在$HOME/.gitattributes
中查找
core.autocrlf
不是一个好主意cat .gitconfig [core] autocrlf = true
如果这来自您的主目录,则可能是问题的根源。
将core.autocrlf
设置为true
就是这样,如the git config
documentation中所述:
将此变量设置为“ true”与设置
text
相同 所有文件的属性都设置为“自动”,core.eol
的属性设置为“ crlf”。调成 如果您要在工作中使用CRLF行结尾,则为true 目录,并且存储库具有LF行结尾。这个变量可以 设置为 input ,在这种情况下将不执行输出转换。
总的来说,我不喜欢text=auto
,因为这意味着Git必须猜测您的文件是否为文本。吉特会猜错吗?有时候,是的,它将。如果您使用的是Linux,则行尾转换默认为不执行任何操作,这是无害的,但是在Windows和MacOS上,它并不是那么无害。这里,.gitattributes
文件特别列出了哪些行结尾在哪些文件中有用。
同样值得一看的是core.eol
的描述,因为您提到将core.autocrlf
设置为false
:
设置行尾类型以在文件的工作目录中使用
core.autocrlf
为false时设置了text属性的属性。 其他选择是 lf , crlf 和 native ,它们使用平台的 本机行结束。默认值为本地。看到 gitattributes(5),以获取有关行尾转换的更多信息。我也尝试了
autocrlf
和auto
的{{1}}。
唯一有效的设置是false
,true
和false
。好吧,从技术上讲,分别将其设置为input
,1
和0
也是可行的,因为如果Git与这些单词之一不匹配,它将尝试将其转换为整数。将其设置为文字字符串-1
会使Git将其视为整数,该整数将转换为值auto
,因此意味着0
。这是默认设置,在这种情况下,false
会发挥作用提供。core.eol
文件中没有覆盖。
除了尚不能在任何地方正确描述的一些关键项目外,其余许多知识都埋藏在that gitattributes
documentation中。这些要求明确定义Git的 index 和 work-tree 。
首先要意识到的是,任何已提交的文件都被冻结为该文件的已提交形式的任何数据,永远以这种特定的提交方式被冻结。也就是说,每个提交都保存着每个文件的快照-好吧,该提交中的每个文件,但似乎有点多余-以创建快照的人在索引中具有的形式快照(通过运行.gitattributes
)。
如果已提交文件的行以CR-then-LF结尾,然后是第二行以CR-without-LF结尾,然后是第三行以LF-without-CR结尾,则该提交版本该文件的始终具有该格式。任何人对此都无能为力。如果具有该提交,则具有该文件,该文件的那三行以该顺序结尾。您可以选择是否签出;但是无论您执行或不执行该操作,该文件都以该提交形式存在。
但是等等! 签出某个文件(或为此提交一个完整的文件)的行为并不意味着将该文件原样放入工作树。让我们考虑一个诸如git commit
的文件。首先,文件必须通过您的 index 。 Git的索引是一种特殊的数据结构(通常存储在一个大的平面文件中,尽管有时有些优化有时会使用多个文件:该平面文件具有各种索引位置,部分使用x.json
之类的名称来查找,与内部数据的链接,这些链接实际上存储在其他位置:索引副本实际上只是指向实际数据的指针)。该索引还有两个名称,也许反映了它在Git中的重要性,或者也许 index 这个名称尚待改进:它也称为 staging area ,或者有时是缓存,具体取决于Git的谁/哪个部分正在执行调用。
无论如何,正如我们刚刚提到的,提交中的x.json
被冻结,但也采用特殊的压缩(有时是高度压缩)的仅Git格式。 Git首先将x.json
复制到索引中,以取消冻结文件,但将其保留为仅Git的特殊格式。由于索引中的副本当前与提交中的副本匹配,因此其行尾相同。但是由于未冻结它们,因此可以根据需要更改行尾。在到达那里之前,让我们看一下检出x.json
的最后一步。
仅Git的文件对除Git之外的任何东西均无用。因此,Git将未冻结的,仅Git的文件从索引复制到您的工作树中,从而创建实际的普通格式x.json
文件。这是最后一步,它基于以下内容进行了行尾操作的第一遍:
x.json
文件中的eol
设置(如果有)或.gitattributes
或core.eol
所隐含的设置,按此顺序或多或少。当然,您也可以从工作树复制到索引。此时 ,Git将基于这两个项目再次进行行尾操作,并加上core.autocrlf
设置,该设置将告诉Git进行行尾转换。即使 output 传递没有通过。
实际上有两种可能的转换。首先,也是最常见的,与回车符和换行符有关。第二个是commit 107642fe2661之后的新内容(首次出现在Git 2.18.0中),与Unicode编码有关。
从索引复制到工作树时,如果您已告知Git,它将用CRLF行尾替换仅LF行尾。不会用LF代替CR-LF;它只在这里进行LF到CRLF。
从工作树复制到索引时,如果您已告知Git,则Git会将CRLF行尾替换为仅LF行尾。不会将CRLF替换为LF。它在这里执行ONYL CRLF-to-LF。现代Git中还有一些额外的魔力:如果文件的 index 副本具有任何回车符(input
或\r
字符),则Git会禁止转换,除非它正在执行“重新规范化”操作(^M
,加上重新规范化标志处于打开状态的合并或选择)。
正如我提到的,这些是Git 2.18中的新功能(对我来说仍然是新的)。 Git现在将通过git add --renormalize
条目将UTF-16文件重新编码为UTF-8,反之亦然。与行尾转换一样,它们是有方向性的:.gitattributes
从工作树复制到索引,准备文件存储在Git中,并将转换为 UTF-8,而从索引复制到工作树的git add
或git checkout
(或git checkout-index
之类的各种其他情况)将转换 from UTF-8。
根据git reset --hard
的打印内容,可能还可以使用其他重新编码。 the gitattributes documentation也对此进行了描述。
iconv --list
和绕过转化的问题 Git的默认值是 guess ,无论某个文件是否是文本。如果文件是文本,它将被转换;如果不是,则Git将其视为神圣不可侵犯:索引中的所有内容都进入工作树,而工作树中的所有内容都进入索引。因此text=
告诉Git:完全不要触摸任何文件,并且没有问题。但是,如果Git先前在考虑* -text
,该怎么办?
这个问题的答案是:效果不好。坏事发生了!除了* text=auto
之外,Git会相信索引和工作树是匹配的,因此,如果文件是最近提取或添加的,则不必费心以一种或另一种方式进行复制。>
尤其是要当心:
git add --renormalize
这:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git add file.ext # use whatever's in the work-tree
这里的$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git checkout -- file.ext # see what's actually in the commit
或git add
步骤可能什么也不做,前提是假设工作树和索引已经匹配,即使它们只是匹配基于先前的git checkout
设置(或缺乏设置)。您可以通过修改(甚至删除,如果要重新提取)文件来强制提取或添加文件,以使工作树副本现在明显不同。但是总的来说,使.gitattributes
更改生效是很烦人和棘手的,因为Git并没有意识到修改.gitattributes
可以更改Git将执行的转换。< / p>
就此而言,.gitattributes
和core.eol
也是如此:更改这些配置项会更改Git可能执行的转换,但是Git并不知道索引和工作树现在可能是彼此不同步。索引的 cache 方面在这里不利于您。
最后,请注意,如果Git看到core.autocrlf
-文件将更改行尾,则Git会告诉您某些文件已被修改或将被修改。有时,Git会猜错,尤其是在更改git add
条目之后,现在将分类为文本的文件分类为二进制,反之亦然。在这些情况下,您仍然必须.gitattributes
文件,以便Git更新索引副本,然后Git意识到更新的索引副本仍与git add
提交匹配,并且文件没有被修改毕竟。
换句话说,有时修复HEAD
文件是不够的,即使文件不会更改。。还要注意,有时,提交的版本如果.gitattributes
条目丢失或不正确,则该文件的行尾错误。工作树副本很可能是正确的,但是直到您进行 new 提交,该提交具有从正确的 index 副本构建的冻结副本之前,Git会(这次是正确的)告诉您文件已被修改!