提交记录的重新排序

221

我目前正在一个分支上工作,想要将一些提交合并到其他分支:

    a-b-c-d-e-f-g (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)
   \
    x-x-x-x-x (branchB)

(字母表示提交,x 表示不相关的提交。)

然而,我注意到汇合一些提交是个好主意。我想要将提交 adeg “连接”成一个补丁,并将其提交到 master。提交 bf 应作为一个提交发送到 branchB。有没有一种好的 Git 的方法可以实现这个?


如果您使用基于IntelliJ的IDE,从这里交互式地重新设置基础是一个非常棒的功能。请查看此30秒演示 - Ricardo
5个回答

213

你需要的命令是git rebase,具体来说是-i/--interactive选项。

我假设你想要将提交c留在分支A上,并且你确实希望将其他提交移到其他分支,而不是进行合并,因为合并是直接的。让我们开始操作分支A。

git rebase -i <SHA1 of commit a>^ branchA

^表示前一个提交,因此此命令表示重新设置分支A,以"a"之前的提交为基础。Git会向您显示此范围内的提交列表。重新排序它们并告诉git压缩相应的提交:

pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f

现在历史记录应该看起来像这样:

    c - [a+d+e+g] - [b+f] (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)

现在,让我们获取分支B的最新压缩提交 b+f。

git checkout branchB
git cherry-pick branchA  # cherry-pick one commit, the tip of branchA

而对于主分支(master)的a+d+e+g同样适用:

git checkout master
git cherry-pick branchA^

最后,更新 branchA 指向 c:

git branch -f branchA branchA^^

我们现在应该有:

    c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
   /
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
   \
    x-x-x-x-x-[b+f] (branchB)
请注意,如果您有多个提交需要在分支之间移动,则可以再次使用rebase(非交互式):
# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB

最后,需要声明的是:重新排序提交记录可能会导致某些提交记录无法干净地应用。可能是因为你选择了错误的顺序(比如在对引入某个功能的提交进行补丁之前),这种情况下你需要中止 rebase (git rebase --abort)。否则,你需要聪明地解决冲突(就像处理合并冲突一样),添加修复,然后运行 git rebase --continue 继续操作。当发生冲突时,这些指示也会在错误消息中提供。


git branch -f branchA branchA^^” 后面的图示不对吗?也就是说,此时 branchA 不应该指向 c 吗?这样,在图示的第一行应该是 “c(branchA)”,而不是 “c - [a+d+e+g] - [b+f] (branchA)” 吧? - ntc2
@enoksrd:是的,有一个错误,但你的编辑中也有错误。我会在有机会的时候进行修正。 - Cascabel
2
为什么不直接在分支A上执行git reset --hard <c的sha1值>,而要执行git branch -f branchA branchA^^呢? - Mikhail Vasilyev
1
我之前一直忽略了一个事实,就是你可以在“交互模式”下重新排序提交记录,Git 会自动完成剩下的操作。 - young_souvlaki
你的图片和描述是我见过的交互式变基最好的! - Geoff Langenderfer

140
git rebase -i HEAD~3

3是需要重新排序的提交数(来源).

这将打开vi,列出从最旧(顶部)到最新(底部)的提交记录。 现在重新排序行,保存退出编辑器。

ddp将当前行向移动
ddkP将当前行向移动(来源)


3
如果你想使用除vi之外的其他编辑器,你可以在交互式地进行git rebase之前使用命令git config --global core.editor "/path/to/your/editor"(当然,global是可选的)。 - colidyre
2
顺便说一句,对于那些不知道如何使用vim的人来说,你不需要一直按ddpddkP,你只需按下ddp来剪切行,然后使用jk分别向下或向上移动即可。 - svelandiag
2
使用nano编辑器,您可以使用 Ctrl+K 剪切一行,并使用Ctrl+U将其粘贴回来。 - jezmck
3
对于 vscode 用户:您可以使用 GitLens 扩展,它提供了友好的 UI 来进行 rebase 操作 https://cmolina.dev/posts/2021/06/configure-git/#use-visual-studio-code-for-squashing - cmolina
2
好东西...但是那些使用ed的人怎么办? - Борат Сагдиев
显示剩余3条评论

59
git rebase --interactive 是你需要使用的命令。

例如:

当前状态如下:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git status
On branch master
nothing to commit, working tree clean
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
a6e3c6a (HEAD -> master) Fourth commit
9a24b81 Third commit
7bdfb68 Second commit
186d1e0 First commit

我想重新排列提交9a24b81(第三次提交)和7bdfb68(第二次提交)。为此,我首先找到要更改的第一个提交之前的提交。这是提交186d1e0(第一次提交)。

执行的命令是git rebase --interactive COMMIT-BEFORE-FIRST-COMMIT-WE-WANT-TO-CHANGE,在这种情况下:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git rebase --interactive 186d1e0

这将使用默认编辑器打开一个文件,其内容如下:

pick 7bdfb68 Second commit
pick 9a24b81 Third commit
pick a6e3c6a Fourth commit

# Rebase 186d1e0..a6e3c6a onto 186d1e0 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#

注意文件中提交的顺序与git log使用的相反。在git log中,最新提交位于顶部。而在此文件中,最新的提交位于底部。

正如文件底部的注释所解释的那样,我可以进行各种操作,如压缩、删除和重新排序提交。要重新排序提交,我编辑文件使其看起来像这样(不显示底部的注释):

pick 9a24b81 Third commit
pick 7bdfb68 Second commit
pick a6e3c6a Fourth commit

每行开头的 pick 命令表示“使用(即包含)该提交”,当我保存包含变基命令的文件并退出编辑器时,git 将执行这些命令并更新存储库和工作目录:

$ git rebase --interactive 186d1e0
Successfully rebased and updated refs/heads/master.
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
ba48fc9 (HEAD -> master) Fourth commit
119639c Second commit
9716581 Third commit
186d1e0 First commit

请注意重新编写的提交历史。

链接:


4

-4

git rebase 是你想要的。请查看 --interactive 选项。


9
请添加相关细节到您的链接中,以提供完整和自包含的答案。SO不赞成“仅链接回答”。 - SherylHohman

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