【问题标题】:Git recheckout files after change to .gitattributes更改为 .gitattributes 后 Git 重新签出文件
【发布时间】:2019-08-01 00:02:25
【问题描述】:

我有一个 repo,其中包含一个错误地以 LF 行结尾提交的文件,但它需要具有 CRLF 行结尾。为了解决这个问题,我添加了一个 .gitattributes 文件以在结帐时强制执行正确的行结尾,这似乎解决了签出新存储库时的问题,但是现有的签出拒绝更新文件,除非我删除文件然后重新签出.我在另一个问题中看到

git rm --cached -r .
git reset
git checkout .

将正确地将受影响文件的行尾从 CRLF 重置为 LF,但这似乎不适用于 LF 到 CRLF。如何让 Git 自动应用 .gitattributes 中的更改?

【问题讨论】:

    标签: git


    【解决方案1】:

    你可以使用这个序列:

    git rm -r .
    git checkout -- .
    

    请注意,这会破坏所有未提交的更改!

    但是,您所说的问题敲响了警钟。

    这些行尾修改(仅)在 index-to-work-tree 复制阶段执行。如果指示这样做,此阶段会将存储在索引中的仅 LF 行尾转换为存储在工作树中的 CR-LF 行尾。

    其他行尾修改(仅)在工作树到索引复制阶段执行。如果指示这样做,此阶段会将存储在工作树中的 CR-LF 行结尾转换为存储在索引中的仅 LF 行结尾。

    要理解所有这些,请注意:

    • 这个东西被不同地称为索引,或暂存区,或(很少)缓存,拥有您的所有文件。好吧,暂时,在git rm -r . 之后,它有 no 文件,但我们会将它们全部放回下一个命令中。一般来说,它有每个文件的副本,准备好进入下一次提交。

    • 工作树,或工作树或这个名称的一些变体,也拥有每个文件的副本。你的工作树也可以有额外的文件,Git 称之为 untracked 文件。部分或全部这些未跟踪的文件可能会被忽略。 (不会忽略跟踪的文件。当且仅当文件存在于索引中时才会跟踪文件。)

    • Git 从索引中的任何内容进行新的提交。工作树副本在这里并不重要。

    • 您无法查看索引中的内容——不能直接查看。存储在索引(和提交)中的文件采用特殊的、只读的、仅限 Git 的冻结格式。您可以使用git ls-files(添加--stage 以获取详细信息)获取索引中所有文件的列表,但这很少有用。

    您可以看到的是工作树中的文件。它们已从冷冻版本中提取出来,按原样重新水化,然后变成普通文件。您的所有计算机程序都可以处理它们,因为它们只是您计算机喜欢的任何格式的普通旧文件。你可以为他们做任何你喜欢的事情。

    Git 必须在索引(它们被冻干并准备提交的位置)和工作树之间复制文件。有时,复制的方向是从索引到工作树。这时,Git 可以将 LF-only 变成 CR-LF。其他时候,复制的方向是从工作树到索引。此时,Git 可以将 CR-LF 变成 LF-only。

    尽管选项很多,但这两个转换是 Git 唯一的行尾转换。各种选项都是告诉Git 的不同方式什么时候做,什么时候不做。

    这是您问题中的警钟:

    我有一个 repo,其中包含一个错误地以 LF 行结尾提交的文件。

    就 Git 而言,只有 LF 的行尾是好。它们是文件的自然状态。所以以这种方式提交文件不是错误——反正对 Git 来说也不是。

    如果您希望此类文件在它们是普通文件时具有 CR-LF 结尾(而不是冻干的仅 Git 内部 Git 文件),那也可以:您告诉 Git 请对 CR-执行 LF-only LF 在提取过程中 并且它做到了。您还可以在git add 期间告诉 Git 请 CR-LF 到 LF-only,它也这样做了,并且您的文件在冻干时继续是 LF-only,但 CR-LF 时可见、可用、可编辑等。

    如果您希望 Git 永远不会弄乱您的文件——让它们以 CR-LF 行结尾即使在冻干时——也可以。但 Git 就是要永远存储数据。这些冻结的副本无法更改。存储在所有现有提交中的所有冻干、只读、Git 化版本将永远是 LF-only。如果您将这些仅 LF 文件与冻干 CR-LF 文件进行比较,每一行都会有所不同。

    (是可以获取所有旧提交,将它们转换为具有 CR-LF 结尾的更新不同提交,并丢弃所有旧提交并强制每个人都可以从旧的提交切换到新的提交。有时这是要走的路。有时不是。你必须自己做出这个决定。)

    【讨论】:

    • 虽然我喜欢大量的文章,因为它可能会帮助可能缺少知识的其他人,但我很清楚其中的大部分内容(除了我的印象是 CRLF LF 转换(如果执行)是在提交的最终确定时完成的,而不是在它被移动到索引时完成的)。我一直在寻找一种应用这种差异的方法,而不必清除我的整个工作目录并重新签出每个文件。
    • 我不同意您的说法“就 Git 而言,仅 LF 行尾是好的。它们是文件的自然状态。所以以这种方式提交文件不是错误 - 不是无论如何,到 Git。” Git 将 CRLF 作为行尾处理没有问题,因此,它也将其视为文件的 good 和 自然 状态,因此也不是错误。这也是一个有争议的问题,因为我不 关心 Git 认为什么是好或坏,以 LF 行结尾提交的文件是无意的,并且在我的工作树中包含 LF 结尾会破坏事情,并且因此,是错误。
    • 如果 Git 不认为 LF-only 行尾是好的,为什么 Git 在填充到冻结文件时提供将 CR-LF 行尾转换为 LF-only 行尾的能力?出于同样的原因,你可以说 Git 认为 CR-LF 行结尾对 work-trees 有好处,因为 Git 提供了将工作树中的 LF-only 结尾转换为 CRLF 结尾的能力.但是 Git 不提供将仅 LF 的工作树文件在工作树到索引的方向上转换为 CRLF 文件的能力。
    • 请注意,这并不意味着 CRLF 结尾也是 bad。它只是说,对于存储在 inside Git 中的文件(与工作树中的文件相比),仅 LF 行结尾有一点偏好,因为这样做的能力是内置的.如果 Git 在进入的过程中获得将 LF-only 转换为 CR-LF 的能力,Git 将不再有这种轻微的偏好。
    • 我的观点是,行尾是什么,甚至行尾的存在,最终与 git 无关,因此当没有任何替代方案不好时,声明 LF 结尾是好的是没有意义的,并且仅仅因为 Git 可以处理它们而声明使用 LF 不是一个错误就更没有意义了,尽管它破坏了项目,该文件是使用 LF 而不是 CRLF 的一部分。我可以提交一个空文件或一个没有行尾的文件,或者一个包含 LF 和 CRLF 的文件,Git 会很好地处理它,因为它们是文件,而这正是 Git 所关心的。
    【解决方案2】:

    用Git 2.16+,试试:

    git add --renormalize .
    

    这假设你已经在你的.gitattributes中添加了

    *.myExtension text eol=crlf
    

    【讨论】:

    • 这是我尝试的第一件事。不幸的是,这似乎只是规范了行尾,使它们保持一致,因为所有的行尾都已经是 LF,这个操作什么都不做。
    【解决方案3】:

    如果您还没有提交新的.gitattributes,您将不想重置索引。使用新属性重做当前结帐但不删除点文件的最简单方法是

    git ls-files -z '[^.]*' | xargs -0 rm; git checkout -- .
    

    (这比简单且通常足够好的rm *;git checkout -- .更安全)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-03
      • 1970-01-01
      • 2013-09-15
      • 1970-01-01
      • 1970-01-01
      • 2020-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多