不需要交互式 rebase,可以更改 Git 提交消息。

4

我知道两种在git中更改提交信息的方法。

第一种是使用git amend,但只适用于最新的提交。因为我想直接更改旧的提交信息,所以这不是我要找的。

第二种是交互式rebase,如answer所描述的那样,它可以更改旧的提交信息。具体步骤如下:

git rebase -i HEAD~n

在我的特定情况下,我必须手动计算变量n的大小,然后浏览所有提交的列表,并将其中一个提交从pick更改为reword,最后输入新的提交消息并强制推送。

老实说,虽然这种方法可行,但操作非常复杂和繁琐。所以我的问题是,是否有更简单易用的选项(可能是别名形式),可以自动完成这个过程?

理想情况下,我希望有一个类似以下命令的方式:

git reword <hash> -m "New commit message"

然后只需强制推送。这是可能的吗?

编辑:我想摆脱交互性,因为我想从我的程序中以编程方式自动执行一些git命令。在该过程中手动与git进行交互有点违背自动化的目的。


使用“修正”作为一个标记 - undefined
2
你知道HEAD~n只是修订语法吗?你可以根据自己的需求指定修订版本,甚至可以只使用<hash> - undefined
@DanielA.White 不明白那样有什么帮助。就像我写的那样,我想修改旧的消息,而不仅仅是最后一条。 - undefined
不要使用HEAD~n,而是直接给出你想要更改的提交的父提交,例如 git rebase -i <hash>^ - undefined
显示剩余2条评论
3个回答

4

对于较新版本的Git来说,最简单的选项似乎是 git commit --fixup=reword:commit

--fixup=[(amend|reword):]<commit>

使用git rebase --autosquash应用时,创建一个新的提交来“修复”<commit>。[…] --fixup=reword:<commit>创建一个“amend!”提交,将<commit>的日志消息替换为自己的日志消息,但不对<commit>的内容进行任何更改。

[…]

--fixup=reword:<commit>--fixup=amend:<commit> --only的简写形式。它创建一个只有日志消息的“amend!”提交(忽略索引中的任何已暂存更改)。当通过git rebase --autosquash压缩时,它会替换<commit>的日志消息,而不做其他任何更改。

因此,您需要运行以下命令:

git commit --fixup=reword:commit-to-amend
git rebase -i --autosquash commit-to-amend^

但是这样做不能通过命令行参数指定提交信息,所以我们需要模仿Git的行为,通过构建autosquash所需的正确提交信息。
git config alias.reword2 '!f() {
  git commit --allow-empty --only -m "amend! $1

$2" &&
  GIT_SEQUENCE_EDITOR=: git rebase -i --autosquash "$1^";
}; f'

然后使用git reword commit-to-amend 'your new message'执行。

有没有关于Windows用户的一些建议? - undefined
@vanowm 安装并使用_Git Bash_ - 正如其名称所示,它可以运行bash脚本。 - undefined
谢谢,但似乎你提出的代码只是添加了一个新的提交和新的消息,而没有改变现有提交的消息…… - undefined
@vanowm 你不能改变一个已存在的提交。你可以创建一个新的提交来代替它。命令的第二部分(git rebase)将合并旧的和新的提交,创建一个新的提交。我在回答时尝试过,它是有效的。 - undefined
奇怪,对我来说它并没有删除原始提交... - undefined
谢谢 @knittl。如果有帮助的话,我在这里写了一篇关于这个解决方案的博客:https://peterevans.dev/posts/how-to-rewrite-git-commit-messages-non-interactively/ - undefined

3
我喜欢在我采取的步骤中明确说明,尤其是对于“破坏性”操作,比如变基。考虑到这一点,你想要的肯定可以通过利用GIT_SEQUENCE_EDITOR变量来实现。它定义了用于编辑交互式变基的“编辑器”。该编辑器不必是交互式编辑器,可以是任何修改文本文件的可执行文件,例如sed
GIT_SEQUENCE_EDITOR='sed -i "1s/^pick/reword/"' git rebase -i commit-you-want-to-amend^

当你在变基时,直到要修改的提交的父提交,那个提交将成为列表中的第一个,你可以使用简单的 sed 脚本轻松地将 pick 切换为 reword。
整个过程可以很容易地转换为一个别名:
git config --global alias.reword '!f() {
  GIT_SEQUENCE_EDITOR="sed -i 1s/^pick/reword/" git rebase -i "$1^";
}; f'

然后 git reword commit-you-want-to-amend
上述方法仍需要手动输入提交信息。如果你想避免这一步骤,可以使用 GIT_EDITOR 的巧妙技巧:
git config --global alias.reword '!f() {
  GIT_SEQUENCE_EDITOR="sed -i 1s/^pick/reword/" GIT_EDITOR="printf \"%s\n\" \"$2\" >" git rebase -i "$1^";
}; f'

请注意在`GIT_EDITOR`值中使用的标准输出重定向符号`>`。
然后调用命令`git reword commit-you-want-to-amend 'your new commit message'`。
第二个选择可以使用git replace,它保留了原始的提交记录,但使用Git的替换引用来显示不同的提交记录。

这在我的测试中有效,但有没有办法阻止钩子触发? - undefined
@vanowm指的是哪些钩子?使用rebase时,提交钩子不应该被运行。你有具体的例子吗?你可以提一个新问题,比如“如何在运行rebase时跳过预提交钩子”? - undefined

1
您可以直接引用目标提交之前的版本,而无需手动从HEAD向后计算提交。运行
$ git rebase -i <rev>^

其中,<rev> 是你想要修改的提交。

从那里开始,你仍然需要手动完成 rebase 操作(将目标行更改为 reword,然后运行 git rebase --continue)。你可以通过一些巧妙的 Shell 脚本和 GIT_SEQUENCE_EDITOR 的组合来进一步自动化这个过程,但我不确定是否推荐这样做。Rebase 是 Git 中非常具有侵入性的操作。在屏幕上短暂地闪现即将完全重写的历史,并有机会中止操作,这是一件好事,并且可能为你节省很多泪水和 git reflog 时光。


谢谢,这比之前好多了。不过,我仍在寻找一个非交互式的解决方案,因为我想基于脚本来完成(我已经编辑了我的问题以包含这个信息)。我会看一下GIT_SEQUENCE_EDITOR,但我真的不明白为什么我们可以在一行中重置硬件并强制推送而没有问题(这两个操作也是危险的),但不能像编辑提交消息这样简单的事情...(我知道它会重写历史,但这是一个如此常见的问题,应该有一个更容易使用的解决方案,这是我的观点) - undefined
1
@SampleTime 幸运的是,knittl来拯救这一天:)。虽然我要提出一个观点,即硬重置和强制推送比变基更不那么恶劣,因为这两种方法只是移动引用,并且很容易通过将引用移回来进行修复。而变基则同时移动引用并写入无限数量的新对象。 - undefined
1
@Brian61354270 只是与问题和答案有些关联,但是 reset --hard 绝对比 rebase(交互式或非交互式)更加“危险”。硬重置会撤销未提交的更改,并且这些更改是无法恢复的。 - undefined

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