Git:如何在所有修订版本的所有文件中重新规范化行尾?

49

我有一个现成的代码库,其中行尾标识已经混乱了。我想重新编写整个代码库,并一劳永逸地解决行尾标识问题。假设 Git 检测二进制文件的启发式算法能够正常工作,那么如何最简单地用规范化的行尾标识重新填充整个代码库呢?


1
我认为所有选项已经在https://dev59.com/jnNA5IYBdhLWcg3wUMDn#1060828中提供了。 - VonC
tree-filter 对我来说速度太慢了。即使逐个挑选更改也更快。 - user907059
1
慢?但这只是一次性操作,您不需要每天重复执行。在晚上启动它,第二天早上回来就可以了。在您的情况下,这难道不可能吗? - VonC
结果证明它很慢是因为它在Cygwin上运行。在Linux上就没问题了。 - user907059
1
在 Git 2.16(2018年第一季度)中,您将拥有 git add --renormalize .:请参见下面的我的答案 - VonC
有几个问题:首先,如果这个仓库有一个父仓库,你需要执行 git push -f 来传播你的更改,然后该仓库的任何子仓库都需要执行新的 git pull。其次,这将对任何尝试跨越你进行更改的提交的 git diff 造成困扰,除非使用 -w 参数。 - undefined
3个回答

58

自Git 2.16(2018年第一季度)以来,有另一种方法(而不是删除索引内容),这是一种新的、更安全的记录您正在更正行尾约定的方式:

git add --renormalize .

请参见提交 9472935(2017年11月16日),由Torsten Bögershausen (tboegi)提交。
(由Junio C Hamano -- gitster --提交 af6e0fe中合并,2017年11月27日)

add: 引入 "--renormalize"

Make it safer to normalize the line endings in a repository.
Files that had been committed with CRLF will be committed with LF.

The old way to normalize a repo was like this:

# Make sure that there are not untracked files
$ echo "* text=auto" >.gitattributes
$ git read-tree --empty
$ git add .
$ git commit -m "Introduce end-of-line normalization"

The user must make sure that there are no untracked files, otherwise they would have been added and tracked from now on.

The new "add --renormalize" does not add untracked files:

$ echo "* text=auto" >.gitattributes
$ git add --renormalize .
$ git commit -m "Introduce end-of-line normalization"

Note that "git add --renormalize <pathspec>" is the short form for "git add -u --renormalize <pathspec>".


注意:Git 2.21(2019年2月)修复了与此相关的错误:“git add --ignore-errors”未按照广告宣传的方式工作,而是作为“git add --renormalize”的意外同义词工作,已经得到修复。

请参见提交记录9e5da3d(2019年1月17日),作者为Jeff King(peff
(由Junio C Hamano -- gitster --提交记录1c41824中合并,2019年2月5日)

add:使用单独的ADD_CACHE_RENORMALIZE标志

提交 9472935add:引入“--renormalize”,2017年11月16日,Git 2.16)教给git addHASH_RENORMALIZE传递给add_to_index(),然后将该标志传递给index_path()
但是,add_to_index()index_path()所采取的标志是不同的命名空间。
我们不能在add_to_index()中采用HASH_*标志,因为它们与我们已经采用的ADD_CACHE_*标志重叠(在这种情况下,HASH_RENORMALIZEADD_CACHE_IGNORE_ERRORS冲突)。

我们可以通过添加新的ADD_CACHE_RENORMALIZE标志来解决这个问题,并在add_to_index()中使用它来设置HASH_RENORMALIZE
为了清楚地表明这两个标志来自不同的集合,让我们还将函数中的名称“newflags”更改为“hash_flags”。

另外: 请参见 提交记录 e2c2a37 (2019年2月7日),作者为Jeff King (peff)
(合并者为Junio C Hamano -- gitster,已合并至提交记录 9293bf6,2019年2月7日)

add_to_index(): 将遗忘的HASH_RENORMALIZE检查进行转换

提交9e5da3dadd: 使用单独的ADD_CACHE_RENORMALIZE标志,2019-01-17)切换我们在标志字段中使用HASH_RENORMALIZE为新的ADD_CACHE_RENORMALIZE标志。
但是,它忘记将一个HASH_RENORMALIZE检查转换为新标志,这完全破坏了"git add --renormalize"。

Git 2.37.3 (2022年第三季度), "git add --renormalize"(手册) 澄清了一个边角情况

请查看efaec7ce提交(2022年8月10日),作者为Philip Oakley (PhilipOakley)
(由Junio C Hamano -- gitster --58ded4a提交中合并,2022年8月18日)

doc add: renormalize is not idempotent for CRCRLF

签署者:Philip Oakley
评论者:Torsten Bögershausen

一个错误报告指出,一个包含/r/r/n的文件需要进行两次规范化。
这是设计上的要求。 单独的CR字符,没有与LF配对,将保持不变。 请注意文档中“clean”过滤器的这个限制。
Renormalize在9472935引入(add: introduce, 2017-11-16, Git v2.16.0-rc0 -- merge listed in batch #6)(add: introduce "--renormalize", Torsten Bögershausen, 2017-11-16)。

git add现在在其手册页面中包含以下内容:

此选项意味着-u
孤立的CR字符不受影响,因此虽然CRLF会被清理为LF,但CRCRLF序列仅部分地清理为CRLF。


3
@Pierre.Sassoulas,你随时可以升级Git:http://lifeonubuntu.com/upgrading-ubuntu-to-use-the-latest-git-version/。 - VonC
谢谢。顺便说一下,我更喜欢将文件保持原样,不进行任何自动转换(例如避免在Windows上将sh脚本转换为crlf或在Linux上将cmd脚本转换为lf),通过在我的gitattributes中指定* -text,然后删除并重新添加文件,以最终正确的行尾结束,这些行尾不应该被git修改。 - JustAMartin
@JustAMartin 同意,但是 git add --renormalize 不应该避免删除/添加步骤吗? - VonC
也许 renormalize 应该在新的 git 版本中也适用于“denornalization”(将 git stage 更新为当前工作副本中的文件 EOLs),但我还没有机会尝试它 - 下次会记住的。 - JustAMartin

9

如果您只想在设置了 core.autocrlftext=auto 后重新规范化当前提交,以便您可以在一个提交中进行所有的行结尾规范化,请运行以下命令:

git rm --cached -rf .
git add .

如果您也想规范化工作目录中的文件,请运行:

git checkout .

2

这可以不用 git 来使用。 然后,稍后 git commit 代码库。

for f in $(find ./ -type f ) ; do
    if grep -qP '\x00' $f ; then
       # file is binary
       continue    
    fi

    perl -pe 'BEGIN{ undef $/} s/\x0d\x0a/\x0a/g;s/\x0d/\x0a/g' -i $f
done

grep 假设任何包含空字符的内容都是二进制文件。

perl 用于原地编辑每个文件。首先,将 Windows 样式的换行符更改为 Unix 样式的换行符。然后将 Mac 样式的换行符更改为 Unix 样式的换行符。


“grep” 假设任何包含空字符的内容都是二进制文件。但更重要的是,它还假设任何不包含空字符的内容都是文本文件。这可能会损坏您目录中的某些二进制文件! - wovano
我写了一个名为nocr的简单脚本,几乎和你的一模一样,只是使用了tr而不是perl。它存在于我的路径中,我经常使用它。你的“这是一个二进制文件吗”的测试是一个很好的想法。@wovano:你说得对,这是危险的,但实际上没有一种不危险的方法来做这个。最终,为了安全起见,你将不得不以某种方式手动指定要处理的文件。也许可以添加一个子句,明确排除以.o,.a,.so,.dat,.bin结尾的任何文件,或者相反,限制为.c,.cpp,.java等文件。 - undefined
@EdwardFalk,.gitattributes文件(该文件在2011年还不存在,但自2018年起可用)是正确且安全的方法来解决这个问题。如果git已经实现了这个功能,就没有必要编写自定义脚本。另外,请注意这样的脚本需要定期运行,而.gitattributes解决方案可以一劳永逸地解决OP所要求的问题 :) - undefined
1
是的,我熟悉.gitattributes并在适用的地方使用它。我之所以提到我的nocr脚本,是因为它在git之外也有用途。但你说得对,如果你想修复git仓库中的换行符问题,那么这里列出的其他方法更好。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接