使用 git rebase 编辑合并提交

在 Git 中,当我提交了例如 A - B - C并且我想编辑 B提交时,我

  • 使用 git rebase -i <A-commit-hash>,
  • 在列表中,我在 B提交前面写入 edit命令,
  • Git rebase 在 B提交后立即停止,因此我可以使用 git commit --amend修复任何我想要的东西,
  • 然后我继续使用 git rebase --continue

据我所知,这是最好的实践如何做到这一点。使用这种方法,我可以编辑过去的任何提交(只要它还没有被推到远程分支) ,而且使用 -p标志,我甚至可以保留合并。真是太好了。

我当前的问题是: 我在合并提交中的一行中犯了一个错误(输入错误)(当合并两个分支时解决了冲突)。

我想修复它,但我不知道如何使 git rebase停止在合并提交。git rebase -p -i <blah>列表忽略合并提交,所以我不能在它前面写 edit命令,让 git rebase停在那里让我编辑它。

有人帮忙吗? 我只是想修复合并提交中的这一行,同时保留它之后的所有提交(和合并)。

谢谢。

51890 次浏览

2020年最新答案:

可以通过 break命令(在 Git 2.20中添加)强制 git rebase -i在合并提交时停止。然后您可以通过 git commit --amend编辑所需的合并提交。

详细步骤:

  1. 运行 git rebase -i --rebase-merges $ancestor_of_merge_commit
  2. 在待办事项列表中找到要编辑的合并提交。
  3. 在只包含 break(或 b)的合并提交后插入新行。
  4. 保存更改并退出编辑器。Git rebase 将检出合并提交,打印如下内容,然后返回到提示符:
    Stopped at fb91fab (Merge branch 'foo' into bar)
    
  5. 根据需要使用 git commit --amend编辑合并提交。
  6. 编辑完合并提交后运行 git rebase --continue

如果在要编辑的提交之后没有任何合并提交,可以使用其他方法:

  1. 运行 git rebase -i $id_of_merge_commit
  2. 在 todo 列表的顶部插入一个只包含 break(或 b)的新行。
  3. 保存更改并退出编辑器。Git rebase 将检出合并提交,打印如下内容,然后返回到提示符:
    Stopped at fb91fab (Merge branch 'foo' into bar)
    
  4. 根据需要使用 git commit --amend编辑合并提交。
  5. 编辑完合并提交后运行 git rebase --continue

2012年的原答案(break之前) :

当涉及到合并时,Git 不能很容易地执行交互式 rebase。-p选项在内部使用 -i机制,因此将两者混合使用并不能真正起作用。

然而,git rebase只是一种自动化的方法,可以进行大量的挑选。您可以通过手动筛选来复制它的行为,以获得对过程的更多控制。它不太方便,更容易出现人为错误,但也有可能。

我建议这样做:

  1. 使用 git rebase到达提交 之后合并(合并的子级)
  2. 使用 git reset --hard HEAD^手动进行合并
  3. 使用 git commit --amend修复合并
  4. 使用 git cherry-pick在合并后返回到提交
  5. 使用 git rebase --continue来完成

以下是具体步骤:

  1. 请注意您要修改的合并提交的 SHA1 ID。
  2. 注意要修改的合并提交后提交的 SHA1 ID (合并提交的子级)。假设它是 facef00d
  3. 运行 git rebase -i deadbeef
  4. 选择 facef00d进行编辑。
  5. 当 rebase 返回编辑 facef00d的提示符时,运行 git reset --hard HEAD^。你现在应该在 deadbeef(git rev-parse HEAD应该打印 deadbeef)。
  6. 进行编辑,以修复不正确的合并冲突,并使用 git add阶段他们。
  7. 运行 git commit --amend将分段修复与错误的合并提交融合。结果现在将有一个不同的 SHA1(而不是 deadbeef)。
  8. 运行 git cherry-pick facef00dfacef00d所做的更改应用于固定的合并提交。
  9. 运行 git rebase --continue来完成。

可能更容易创建一个修复提交“ D”,然后使用“ git rebase -p -i <blah>”重新排序“ D”在“ B”之后,并将其压缩成“ B”。

pick A
pick B  <- merge commit to ammend
fixup D
pick C

现在有了 Git 2.22及以上版本的 --rebase-merges选项,这就容易多了。此选项保留合并拓扑,并使用交互式 rebase。

假设 B是合并提交修正,它将看起来像这样:

label onto


... some branch definitions ...


reset onto
merge -C B branch-name # Merge branch 'B' into whatever
pick C

你现在可以在 mergepick之间插入一个 b(或 break) :

merge -C B branch-name # Merge branch 'xyz' into whatever
break
pick C

在中断 commit --amend时进行合并,然后继续重建基础。

这也适用于修复提交。例如,假设对合并进行修复的提交是 D。您可以在合并提交之后立即将修复提交 D:

merge -C B branch-name # Merge branch 'xyz' into whatever
fixup D
pick C

参见 git-merge 手册页的 重新定基合并部分。

有时候你不应该使用 git rebase来编辑合并提交,另一种方法可能更有效。

我刚刚经历了一个这样的场景: 我有一个合并提交,它在一个非常古老的分支中进行合并,并且在合并提交中解决了许多冲突。在合并之后有一些额外的提交,然后发现在一个文件的冲突解决过程中,在大的合并提交中出现了错误。我按照接受的答案设置了交互式 rebase,但是当我的机器在2/59锁定时,我意识到它正在写一个 很多文件,因为它重新组织了我的文件系统以匹配旧的分支。几分钟后,我终止了它,中止了 rebase,然后不得不删除10万个文件才能回到我开始的地方。我没有使用 rebase,而是这样做了:

  1. 请注意在修复合并提交之后要重播的提交 ID。
  2. git reset --hard [merge-commit-id]
  3. 进行更改并简单地修改 HEAD 提交(即合并提交)。
  4. Cherry 在合并提交后按顺序挑选每个提交 ID,或者使用带有3个参数的 git rebase --onto来挑选整个范围(如果不止几个)

这是 很多更快。