撤销Git中的行尾更改

21
我的问题类似于Git - unchange line endings in already committed file,尽管被接受的答案似乎大多是胡言乱语,在我的情况下并没有什么帮助。 我有几个相当大的提交。 在我的编辑器和git配置之间,我提交了许多行结束符更改。例如,我更改了文件中的一行,但我的提交会更改文件中成千上万的其他行的行结尾符。 我还没有推送提交。 在推送之前如何删除行结尾符更改? 我试过...
git rebase [last_good_commit]

但它只是说了这样的话

current branch *** is up to date. 

可能是如何撤销最后一次Git提交?的重复问题。 - Mathew
@MatW,不是重复。我有几个提交记录想要更改。 - Paul Draper
你可以进行交互式的变基,并编辑每个提交。 - poke
啊,抱歉。那么我会选择@poke的建议。 - Mathew
1
你的提交有多大?你的更改是否有助于简单地执行硬重置并重新创建你的工作? - mjswensen
2个回答

19

只需强制将尚未推送的提交进行变基,并告诉它在补丁中应用空格修复:

git rebase --whitespace=fix -f <last_good_commit> 

重新设置基础的内部工作原理是通过应用补丁进行的,方便的是,git-apply支持使用--whitespace选项修复空格。


1
只修复换行符而不是所有空格的更改,这是否可行? - A Jar of Clay
core.whitespace 控制了哪些是错误,所以我认为(未经测试)您可以添加 -c core.whitespace=blank-at-eol 来限制正在修复的内容。这将防止修复 space-before-tab,但仍然会修复 所有 尾随空格,而不仅仅是行末的 CR。 - Matthijs Kooijman
请注意,如果后续提交对同一文件进行更改,则此方法似乎会失败 - 对于第一个提交,空格被修复,但这似乎会导致第二个提交失败。我尝试添加--ignore-whitespace,但是它(如果我正确理解文档的话)会抱怨混合应用和合并选项。我有另一种方法可以在这种情况下进行一些手动工作,并将其单独回答分享。 - Matthijs Kooijman

1

我成功应用的一个技巧是使用git rebasedos2unix来自动修复这个问题。

要做到这一点,运行一个交互式的rebase:

git rebase -i [last_good_commit]

这将使您进入一个编辑器,其中列出了所有要重新应用的提交。例如:

pick ad31eff3 Some commit message
pick 138fab7d Some other commit message

现在将每一行修改为exec git checkout xxx . && dos2unix *.txt && git commit -a -C xxx,其中两个xxx都是相关提交的哈希值,并且*.txt列出了需要修正行末的所有文件。

对于上面的示例,变成了:

exec git checkout ad31eff3 . && dos2unix *.txt && git commit -a -C ad31eff3
exec git checkout 138fab7d . && dos2unix *.txt && git commit -a -C 138fab7d

保存文件并退出编辑器:Git将依次处理每个命令,您应该得到一个修改后的历史记录,其中行尾永远不会出错。

使用正则表达式生成命令文件

这是一个正则表达式(实际上是一个vim命令 - 根据您的编辑器语言适应它),可用于将具有pick行的rebase命令文件转换为此修复程序的正确命令:

:%s/pick \([^ ]*\) .*/exec git checkout \1 . \&\& dos2unix *.txt \&\& exec git commit -a -C \1

这是如何工作的?

对于每个提交,此技巧运行以下命令:

git checkout xxx .

这将更新当前工作副本的内容,使其与原始提交(具有不正确的行尾)完全匹配,而不会触及分支头(当前提交)或索引。

dos2unix *.txt

这将修复工作副本中的所有行尾,使用Unix LF替换dos CRLF(如果需要其他转换,可以在此处使用unix2dos或其他命令)。

git commit -a -C xxx

这将提交所有更改(来自原始提交,但换行符已更改),重用原始提交的提交消息。

这里的技巧是对于每个提交,第一个命令会切换回原始文件。通常情况下,在重新定义基础时,您只需选择提交即可,但在这种情况下不起作用 - 一旦您在第一个提交中修复了行尾,后续提交可能将不再适用,留下您来解决混乱。在这种情况下,例如第二个提交,使用的git checkout将只使用原始提交(树)中的文件,因此它完全忽略(因此也还原)为第一个提交修复的行尾。但没关系 - dos2unix命令将愉快地为每个提交再次修复它们,最终结果是一个干净的历史记录,您无法告诉行尾曾经错误过。

聪明的读者会注意到,我们实际上并没有真正使用任何重新定义基础功能,因为所有提交都是使用显式的git commit命令创建的,而不是让重新定义基础处理此问题。因此,这只是使用重新定义基础重置到好的提交,生成要应用的提交列表并按顺序执行命令(并进行一些有用的检查,例如工作副本是否干净以及命令是否失败)。

将其用于其他情况

这种方法也适用于其他自动可重复修复的问题,例如代码格式调整。我最初 发现这种方法 是为了在新的代码库上运行 astyle 而不必创建一个大规模的修复提交。


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