git checkout分支最终修改文件

时间:2018-11-21 01:40:18

标签: git

我有一台Mac,可以在其上克隆存储库。当我结帐任何分支时,我所拥有的.json文件最终显示为“已修改”。

这是我尝试过的事情清单。

  1. git clone <blah>.git
  2. git checkout release
  3. git checkout -- $(git ls-files -m)

文件仍显示为已修改:

me@box> cat ~/.gitattributes
text=auto
-crlf

me@box> cat .gitconfig
[core]
    autocrlf = true

我也尝试将autocrlfautofalse一起使用。

有什么办法可以解决这个问题?

1 个答案:

答案 0 :(得分:2)

TL; DR

可能希望.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/attributescore.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),以获取有关行尾转换的更多信息。

     

我也尝试了autocrlfauto的{​​{1}}。

唯一有效的设置是falsetruefalse。好吧,从技术上讲,分别将其设置为input10也是可行的,因为如果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设置(如果有)或.gitattributescore.eol所隐含的设置,按此顺序或多或少。

当然,您也可以从工作树复制到索引。此时 ,Git将基于这两个项目再次进行行尾操作,并加上core.autocrlf设置,该设置将告诉Git进行行尾转换。即使 output 传递没有通过。

转化

实际上有两种可能的转换。首先,也是最常见的,与回车符和换行符有关。第二个是commit 107642fe2661之后的新内容(首次出现在Git 2.18.0中),与Unicode编码有关。

CRLF转换

从索引复制到工作树时,如果您已告知Git,它将用CRLF行尾替换仅LF行尾。不会用LF代替CR-LF;它只在这里进行LF到CRLF。

从工作树复制到索引时,如果您已告知Git,则Git会将CRLF行尾替换为仅LF行尾。不会将CRLF替换为LF。它在这里执行ONYL CRLF-to-LF。现代Git中还有一些额外的魔力:如果文件的 index 副本具有任何回车符(input\r字符),则Git会禁止转换,除非它正在执行“重新规范化”操作(^M,加上重新规范化标志处于打开状态的合并或选择)。

Unicode转换

正如我提到的,这些是Git 2.18中的新功能(对我来说仍然是新的)。 Git现在将通过git add --renormalize条目将UTF-16文件重新编码为UTF-8,反之亦然。与行尾转换一样,它们是有方向性的:.gitattributes从工作树复制到索引,准备文件存储在Git中,并将转换为 UTF-8,而从索引复制到工作树的git addgit 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>

就此而言,.gitattributescore.eol也是如此:更改这些配置项会更改Git可能执行的转换,但是Git并不知道索引和工作树现在可能是彼此不同步。索引的 cache 方面在这里不利于您。

最后,请注意,如果Git看到core.autocrlf-文件更改行尾,则Git会告诉您某些文件已被修改或将被修改。有时,Git会猜错,尤其是在更改git add条目之后,现在将分类为文本的文件分类为二进制,反之亦然。在这些情况下,您仍然必须.gitattributes文件,以便Git更新索引副本,然后Git意识到更新的索引副本仍与git add提交匹配,并且文件没有被修改毕竟。

换句话说,有时修复HEAD文件是不够的,即使文件不会更改。还要注意,有时,提交的版本如果.gitattributes条目丢失或不正确,则该文件的行尾错误。工作树副本很可能是正确的,但是直到您进行 new 提交,该提交具有从正确的 index 副本构建的冻结副本之前,Git会(这次是正确的)告诉您文件已被修改!