Break a previous commit into multiple commits

Break a previous commit into multiple commits

技术背景

在使用Git进行版本控制时,有时会遇到一个提交包含了多个不同功能或修改的情况。为了让提交历史更加清晰、易于维护和审查,需要将一个之前的提交拆分成多个独立的提交。

实现步骤

准备工作

开始操作前,确保工作目录是干净的,即git status应显示没有待修改、删除或添加的文件。

拆分最近的提交

如果要拆分最近的提交,可按以下步骤操作:

  1. 使用git reset HEAD~撤销最近的提交,将修改放回工作区。
  2. 按正常方式分别提交各个部分,生成所需数量的提交。

拆分较早的提交

这需要进行变基(rebasing)操作,即重写历史。指定正确的提交有以下几种选择:

  • 若要拆分的提交是倒数第3个,则使用git rebase -i HEAD~3
  • 若提交在提交树中较深,不想手动数,可以使用git rebase -i 123abcd~,其中123abcd是要拆分的提交的SHA1值。
  • 若要对整个当前分支进行变基,可使用git rebase -i
  • 若在不同的分支(如功能分支)上,并想将其合并到master分支,可使用git rebase -i master

当进入变基编辑界面后,找到要拆分的提交,将行首的pick替换为edit(简写为e),保存并退出。变基会在要编辑的提交之后停止,然后执行git reset HEAD~,再按正常方式分别提交各个部分,最后执行git rebase --continue完成变基。

保留提交作者信息

如果要保留提交的作者信息和日期,可参考相关链接

拆分单个文件中的更改

拆分文件到不同提交时,若要拆分单个文件中的更改,有两种选择:

  1. 使用git reset HEAD~后,通过git add -p逐个选择要包含在每个提交中的补丁。
  2. 编辑工作副本,移除不需要的更改,提交中间状态,然后恢复完整提交进行下一轮操作。

使用TortoiseGit拆分提交

在Windows上的最新版TortoiseGit中,可按以下步骤操作:

  1. 打开变基对话框并进行配置。
  2. 右键单击要拆分的提交,选择Edit
  3. 点击Start开始变基。
  4. 到达要拆分的提交时,勾选Edit/Split按钮,直接点击Amend,打开提交对话框。
  5. 取消选择要单独提交的文件。
  6. 编辑提交消息,然后点击commit
  7. 重复上述步骤,直到没有文件需要提交。

在IDE中拆分提交(以IntelliJ IDEA等为例)

  1. 在版本控制日志窗口中,选择要拆分的提交,右键单击并选择Interactively Rebase from Here
  2. 将需要拆分的提交标记为edit,点击Start Rebasing
  3. 当看到黄色标签表示HEAD已设置到该提交时,右键单击该提交,选择Undo Commit
  4. 此时这些提交会回到暂存区,可分别提交它们。所有更改提交完成后,旧的提交将变为无效。

不使用交互式变基拆分提交

可按以下步骤操作:

1
2
3
4
5
6
# 回退到上一个提交
git reset --hard <previous-commit> .
# 应用要拆分的提交的更改
git checkout <commit-you-want-to-split>
# 将更改从索引中移除
git reset

之后就可以像平常一样分别提交这些更改。

使用脚本拆分提交

可将以下脚本内容复制到名为git-split的文件中,放到$PATH包含的文件夹中,并使其可执行,然后使用git split运行该脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env zsh

# Use `git split` to split a commit into two commits.
#
# First, use `git checkout --patch HEAD~` to stage changes that you
# want to split from the HEAD commit into a separate commit. Then run
# `git split` (this script) and enter a commit message for the new commit.

set -e

git commit --fixup=HEAD --quiet
git revert --no-commit HEAD
git revert --quit
if ! git commit $@; then
git cherry-pick --no-commit HEAD
git reset --soft HEAD~
exit 0
fi
git -c sequence.editor=: rebase --autosquash --autostash --interactive HEAD~3

使用Sublime Merge拆分提交

  1. 在提交列表中选择要修改的提交,右键单击并选择Edit Commit > Edit Commit Contents
  2. 此时处于HEAD状态,提交内容在暂存区。
  3. 取消暂存要分离的文件、块或行,只保留第一个提交的内容。
  4. 提供提交消息,然后按Commit
  5. 根据需要暂存/提交,直到将提交拆分为所需的样子。
  6. 当没有未提交的更改时,按Continue Rebase

核心代码

拆分较早提交的示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
# 开始交互式变基
git rebase -i HEAD~3
# 在变基编辑界面将目标提交标记为edit
# 变基停止后,撤销提交
git reset HEAD~
# 添加并提交第一部分
git add ...
git commit -m "First part"
# 添加并提交第二部分
git add ...
git commit -m "Second part"
# 继续变基
git rebase --continue

不使用交互式变基拆分提交的示例代码

1
2
3
4
5
6
# 回退到上一个提交
git reset --hard <previous-commit> .
# 应用要拆分的提交的更改
git checkout <commit-you-want-to-split>
# 将更改从索引中移除
git reset

最佳实践

  • 在进行拆分提交操作前,建议先备份分支,以免操作失误导致历史记录丢失。
  • 拆分提交后,若分支已推送到远程仓库,需要使用git push --force强制推送,但要注意强制推送可能会影响其他协作者的工作。
  • 拆分提交时,尽量让每个提交的内容具有原子性,即每个提交只包含一个逻辑上独立的更改。

常见问题

变基过程中出现冲突怎么办?

当变基过程中出现冲突时,需要手动解决冲突。解决冲突后,使用git add将解决后的文件添加到暂存区,然后使用git rebase --continue继续变基。

拆分提交后提交历史变得混乱怎么办?

可以使用git log查看提交历史,确认是否符合预期。如果提交历史仍然混乱,可以考虑重新进行拆分操作或使用git reflog恢复到之前的状态。


Break a previous commit into multiple commits
https://119291.xyz/posts/break-a-previous-commit-into-multiple-commits/
作者
ww
发布于
2025年5月26日
许可协议