高级Git历史编辑:修改提交与交互式变基
使用 git commit --amend 和交互式变基清理本地历史,避免干扰共享分支。
高级Git历史编辑:修改提交与交互式变基
高级Git历史编辑能帮助你在提交成为共享历史的一部分之前清理它们。掌握修改提交和交互式变基后,你可以修复小错误、合并杂乱的提交,并为代码审查呈现更清晰的故事。
关键规则很简单:编辑本地历史是正常的,但重写共享历史需要谨慎。这些命令之所以强大,是因为它们会更改提交ID,而不仅仅是提交信息。
Git历史编辑实际改变了什么
每个Git提交的ID基于其内容、元数据、父提交、作者、日期和信息。如果你修改一个提交或在变基期间重写它,Git会创建一个具有新ID的新提交。
当提交仅存在于你的机器上时,这没问题。但当其他人已经拉取了旧提交时,这就存在风险。他们的分支可能仍指向旧历史,而你的分支指向重写后的历史。
历史编辑适用于:
- 修复最新提交信息中的拼写错误。
- 向最后一个本地提交添加遗漏的文件。
- 在发起拉取请求前压缩多个工作进展中的提交。
- 重新排序本地提交,使相关更改更易于审查。
- 将一个大的本地提交拆分为多个逻辑清晰的提交。
避免在以下分支上进行历史编辑:
- 主分支。
- 发布分支。
- 多人使用的共享功能分支。
- 任何带有部署标签或审计敏感提交的分支。
开始之前,检查你当前所在的分支以及已推送的内容:
git status
git branch --show-current
git log --oneline --decorate -5
如果不确定重写是否安全,创建一个备份分支:
git branch backup-before-rebase
如果变基出现问题,该分支能为你提供一个已知的恢复点。
修改最新提交
git commit --amend 会用新提交替换最近一次提交。这是在其他人依赖它之前修复最后一次提交的最快方法。
仅编辑提交信息:
git commit --amend
Git会打开编辑器显示当前信息。保存更正后的信息,Git会创建一个替换提交。
向最后一次提交添加遗漏的文件:
git add path/to/file
git commit --amend --no-edit
--no-edit 标志保留现有信息。当提交信息已经正确,而你只是忘记暂存一个更改时,这很有用。
以下是一个常见场景。你提交了一个Dockerfile更新,然后注意到忘记了相关的.dockerignore更改:
git add Dockerfile
git commit -m "Improve Docker build caching"
git add .dockerignore
git commit --amend --no-edit
现在分支上只有一个干净的提交,而不是第二个写着“忘记文件”的提交。这使审查更容易。
如果原始提交已经推送,远程分支仍然有旧的提交ID。推送修改后的提交需要强制更新:
git push --force-with-lease
优先使用 --force-with-lease 而不是 --force。如果自上次拉取后有人推送了新工作,它会拒绝覆盖远程分支。
使用交互式变基清理多个提交
交互式变基允许你一次编辑多个提交。你选择一个范围,然后Git会打开一个待办列表,你可以在其中选择、重写、压缩、修复、重新排序或停止提交。
编辑最后五个提交:
git rebase -i HEAD~5
你会看到类似这样的列表:
pick a1b2c3d Add deployment script
pick b2c3d4e Fix typo
pick c3d4e5f Add health check
pick d4e5f6a Update docs
pick e5f6a7b Adjust timeout
更改每行开头的命令来控制发生什么。常见选项有:
pick:保持提交不变。reword:保留更改但编辑信息。squash:将此提交合并到前一个提交并编辑合并后的信息。fixup:将此提交合并到前一个提交并丢弃此提交的信息。edit:在此提交处停止,以便更改其内容。drop:删除提交。
例如,如果“Fix typo”属于“Add deployment script”,更改第二行:
pick a1b2c3d Add deployment script
fixup b2c3d4e Fix typo
pick c3d4e5f Add health check
pick d4e5f6a Update docs
pick e5f6a7b Adjust timeout
保存并关闭编辑器。Git会重放提交并将拼写错误修复合并到较早的提交中。
交互式变基也有助于在拉取请求前改进提交信息。如果三个提交都写着“update”,使用 reword 将它们改为解释实际更改的信息。
如果出现冲突,像处理普通合并冲突一样解决它:
git status
git add resolved-file
git rebase --continue
如果你决定变基不值得:
git rebase --abort
这会将分支恢复到变基开始前的状态。
在变基期间拆分提交
有时一个提交包含两个不相关的更改。例如,你可能有一个提交同时更新了Kubernetes清单并修复了README部分。这些更改可能应该分开。
启动交互式变基:
git rebase -i HEAD~3
将 pick 改为 edit 用于你想要拆分的提交。当Git在该提交处停止时,重置它同时将更改保留在工作树中:
git reset HEAD^
现在暂存并提交第一个逻辑部分:
git add k8s/deployment.yaml
git commit -m "Update deployment health check"
然后暂存并提交第二部分:
git add README.md
git commit -m "Document health check behavior"
继续变基:
git rebase --continue
这将一个宽泛的提交变成了两个聚焦的提交。审查者可以理解每个更改,而无需在心理上分离不相关的工作。
何时在重写前寻求帮助
在重写其他人使用的任何分支之前,请寻求帮助。团队领导、发布工程师或仓库维护者可以告诉你该分支是否可以安全重写,以及团队如何处理强制推送。
如果变基影响到已经部署、标记或被事件报告引用的提交,请立即寻求帮助。在这些情况下,保留历史通常比让它看起来整洁更重要。
对于常规功能分支,历史编辑是正常的清理步骤。对最新提交使用 git commit --amend,对小的本地系列使用交互式变基,当你必须更新自己拥有的远程分支时使用 --force-with-lease。