git - git checkout 分支最终修改文件
问题描述
我有一台 Mac,我在其上克隆了一个 repo。当我签出任何分支时,.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
and false
。
有没有什么办法解决这一问题?
解决方案
TL;博士
您可能希望.gitattributes
(not ~/.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 要做的是不对所有名为 的文件text=auto
应用属性,并且不对所有名为-crlf
. 由于可能没有具有这些有趣名称的文件,因此这不会将属性应用于任何文件 - 因此它没有效果。
(您可能想要* text=auto
和/或* -crlf
,但见下文。)
同样(但也许不是问题),这可能是错误的地方:
cat ~/.gitattributes
Git 在几个地方寻找属性文件。最重要的是.gitattributes
在存储库工作树的顶层,即.gitattributes
, 不是~/.gitattributes
。您可以让 Git 咨询core.attributesFile
,您可以将其设置为~/.gitattributes
. 现代 Git 会查找$XDG_CONFIG_HOME/git/attributes
or $HOME/.config/git/attributes
ifcore.attributesFile
未设置,因此除非您配置了全局core.attributesFile
设置,否则它不会查找$HOME/.gitattributes
.
意见:core.autocrlf
不是一个好主意
cat .gitconfig [core] autocrlf = true
如果这是来自您的主目录,这可能是问题的根源。
如文档中所述,设置core.autocrlf
为以true
这种方式运行:git config
将此变量设置为“true”与将
text
所有文件的属性设置为“auto”和core.eol
“crlf”相同。如果您想在工作目录中使用 CRLF 行结尾并且存储库具有 LF 行结尾,请设置为 true。该变量可以设置为input,在这种情况下不执行输出转换。
一般来说,我不喜欢text=auto
,因为这意味着 Git 必须猜测您的文件是否为文本。Git会猜错吗?有时,是的,它会的。如果你在 Linux 上,行尾转换默认为什么都不做,这是无害的,但在 Windows 和 MacOS 上,它就不是那么无害了。在这里,一个.gitattributes
文件特别列出了哪些文件的行尾是有用的。
由于您提到core.eol
设置core.autocrlf
为false
:
为设置了 text 属性的文件设置在工作目录中使用的行结束类型(当
core.autocrlf
为 false 时)。替代方案是lf、crlf和native,它们使用平台的本机行尾。默认值为原生。有关行尾转换的更多信息,请参阅 gitattributes(5)。我也尝试
autocrlf
过auto
andfalse
。
唯一有效的设置是true
、false
和input
。好吧,从技术上讲,将其分别设置为1
、0
和-1
也可以,因为如果它与这些单词之一不匹配,Git 会尝试将其转换为整数。将其设置为文字字符串auto
使 Git 将其视为一个整数,它转换为 value 0
,因此意味着false
. 这是默认设置,在这种情况下,只要文件中没有覆盖,core.eol
就会发挥作用。.gitattributes
除了一些没有在任何地方正确描述的关键项目外,其余大部分要了解的内容都隐藏在该gitattributes
文档中。这些需要明确定义 Git 的index和work-tree。
提交、索引和工作树
首先要意识到的是,任何已提交的文件都被冻结在该文件的已提交形式中的任何数据中,在该特定提交中永远如此。也就是说,每个提交都包含每个文件的快照——嗯,那个提交中的每个文件,但这似乎有点多余——以当谁制作快照时它在索引中的形式,制作快照(通过运行git commit
)。
如果提交的文件有一行以 CR-then-LF 结尾,然后是第二行以 CR-without-LF 结尾,然后是第三行以 LF-without-CR 结尾,则该文件的提交版本总是有那种形式。对此,任何人都无能为力。如果你有那个提交,你就有那个文件,这三个行结尾的顺序是这样的。您可以选择是否查看;但无论您做什么或不做什么,该文件都以该形式存在于该提交中。
可是等等!签出某个文件的行为——或者一个充满文件的提交——并不意味着将该文件按原样放入工作树中。让我们考虑一个文件,例如x.json
. 首先,该文件必须通过您的index。Git 的索引是一种特殊的数据结构(通常存储在一个大的平面文件中,尽管有时有一些优化会使用多个文件:平面文件有各种索引槽,部分通过名称找到x.json
,以及与内部的链接数据,实际上存储在其他地方:索引副本实际上只是指向真实数据的指针)。该索引还有两个名称,也许反映了它在 Git 中的重要性,或者也许是名称索引留下一些不足之处:它也称为staging area,有时也称为cache,具体取决于 Git 的谁/哪个部分正在执行调用。
无论如何x.json
,正如我们刚刚提到的,提交的内部是冻结的,但也是一种特殊的、压缩的(有时是高度压缩的)仅 Git 格式。Git 首先将其复制x.json
到您的索引中,解冻文件,但将其保留为特殊的仅 Git 格式。由于索引中的副本当前与提交中的副本匹配,因此它具有相同的行尾。但由于它没有被冻结,如果你愿意,你可以改变行尾。在我们到达那里之前,让我们看一下结帐的最后一步x.json
。
仅 Git 文件对 Git 以外的任何东西都无用。因此,Git 将解冻的、仅限 Git 的文件从索引复制到您的work-tree,创建一个实际的普通格式x.json
文件。正是这最后一步完成了行尾操作的第一遍,基于:
- 该文件是否已被检测为“文本”——非文本文件从未以这种方式进行操作——以及
- 文件中的
eol
设置(如果有)或由or.gitattributes
暗示的设置,或多或少按该顺序。core.eol
core.autocrlf
当然,您也可以从工作树复制到索引。此时, Git 将再次进行行尾操作,基于这两个相同的项目,加上input
设置,它告诉 Git 进行行尾转换,即使输出传递没有。
转换
实际上有两种可能的转换。第一个也是最常见的,与回车和换行有关。第二个是自提交 107642fe2661(首次出现在 Git 2.18.0 中)以来的新内容,与 Unicode 编码有关。
CRLF 转换
从索引复制到工作树时,如果您告诉 Git 这样做,Git 会将仅 LF 行结尾替换为 CRLF 行结尾。它不会用 LF 代替 CR-LF;它在这里只做 LF-to-CRLF。
从工作树复制到索引时,如果您告诉 Git 这样做,Git 会将 CRLF 行结尾替换为仅 LF 行结尾。它不会用 CRLF 代替 LF;它在这里只做 CRLF-to-LF。现代 Git 有一些额外的魔力:如果文件的索引副本有任何回车(\r
或^M
字符),Git 会禁止转换,除非它正在执行“重新规范化”操作(git add --renormalize
,加上带有重新规范化标志的合并或樱桃选择打开)。
Unicode 转换
正如我所提到的,这些在 Git 2.18 中是新的(对我来说仍然是新的)。.gitattributes
Git 现在将通过条目将 UTF-16 文件重新编码为 UTF-8,反之亦然,如果您告诉它这样做的话。与行尾转换一样,这些是定向的:git add
,它从工作树复制到索引,准备文件以存储在 Git 中,并将转换为UTF-8,而git checkout
or git checkout-index
(或各种其他情况,如git reset --hard
),从工作树的索引,将从UTF-8 转换。
根据任何iconv --list
印刷品,可能会提供额外的重新编码。这也在gitattributes 文档中有所描述。
更改text=
和转换绕过问题
Git 的默认设置是猜测某个文件是否为文本。如果文件是文本,它会被转换;如果不是,Git 将其视为神圣不可侵犯:索引中的任何内容都进入工作树,工作树中的任何内容都进入索引。所以* -text
告诉 Git:永远不要碰任何文件,并且没有问题。但是如果 Git 之前在想* text=auto
怎么办?
这个问题的答案是:这行不通。坏事发生!除了git add --renormalize
,Git 将相信索引和工作树匹配——因此如果文件是最近提取或添加的,则不会费心复制一种或另一种方式。
也就是说,特别要注意这一点:
$ 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
orgit checkout
步骤可能不会做任何事情,即使它们仅用于基于先前.gitattributes
设置(或缺少设置)进行匹配。您可以通过修改(或什至删除,如果您要重新提取)文件来强制提取或添加,以便工作树副本现在明显不同。但总的来说,让.gitattributes
更改生效既烦人又棘手,因为 Git 没有意识到修改.gitattributes
可以更改 Git 将执行的转换。
就此而言,core.eol
and也是如此core.autocrlf
:更改这些配置项会更改 Git 可能执行的转换,但 Git 不知道索引和工作树现在可能彼此不同步。索引的缓存方面在这里对您不利。
最后,请注意,如果 Git 看到git add
-ing 文件会更改行尾,Git 会告诉您某个文件正在或将被修改。有时 Git 会猜错,尤其是在更改.gitattributes
条目之后,将分类为文本的文件现在分类为二进制文件,反之亦然。在这些情况下,您git add
无论如何都必须访问该文件,以便 Git 更新索引副本,之后 Git 意识到更新的索引副本仍然与HEAD
提交匹配,并且文件根本没有被修改。
换句话说,有时修复.gitattributes
文件是不够的,即使文件不会改变。 另请注意,有时,如果条目丢失或不正确,文件的提交版本的行尾错误。.gitattributes
工作树副本很可能是正确的,但是在您进行新的提交之前,该提交具有从正确的索引副本构建的冻结副本,Git 将——这一次正确地——告诉你文件已被修改!
推荐阅读
- java - 如何在 Spring 中更改 Application.properties 的值
- gitlab - GitLab管道中`environment:url`使用的`CI_COMMIT_TAG`的字符串操作
- kubernetes - 在启动新 Pod 之前强制关闭 Kubernetes Pod,以防中断
- php - 使用对象作为键的多维地图
- python - TypeError:不支持的操作数类型 -=:python 上的 'slice' 和 'int'
- ios - 如何在ios swift中将新值添加到数组时实时更新tableview
- node.js - 使用快速身份验证对用户隐藏特定选项卡
- ms-access - 过滤查询 - 编辑结果
- css - 为什么 Css 文本转换大写不起作用?
- ios - 核心情节标题对齐和字体样式