在之前进行了git合并操作后如何进行git rebase

101

我有如下情况:

  • 我从一个主仓库(X)创建了克隆(Y),因为有很多人在Y上工作,我们没有做任何rebase而只做了merge。当我们想要将Y交付(push)给X时,我们希望进行一次rebase,以使事情变得清晰明了。

问题是,当进行rebase时,我们被要求做之前已经完成的所有merge步骤。除了重新执行合并操作的解决方案之外,是否有其他解决方案?

我认为这应该很简单,因为我们已经解决了冲突的合并。


在“因为有很多人在Y上工作,我们没有进行任何rebase,只进行了合并”这句话中,你的意思是与上游合并,对吗? - Ciro Santilli OurBigBook.com
相关 https://dev59.com/xGkv5IYBdhLWcg3wbgXQ - somethingRandom
6个回答

178

git merge --squash 现在是我在大量工作和多次合并之后首选的变基方式(参见此答案)。如果你正在工作的分支叫做 my-branch,并且你想要从 main 进行变基,只需按照以下步骤操作:

git checkout my-branch
git branch -m my-branch-old       # rename from my-branch to my-brand-old
git checkout main
git checkout -b my-branch         # make new my-branch branch
git merge --squash my-branch-old  # squash and merge changes from old branch 
git commit

这比使用变基压缩要容易得多。它只是有效地工作了。 - Matt
是的,rabasing 简直是一个可怕的想法,感谢您提供的解决方案。 - Ini
1
如果分支跟踪远程分支,您可以添加 git branch --set-upstream-to origin/my-branch 并可选地添加 git push --force。如果没有这样做,新的 my-branch 将不会与远程分支链接。强制推送允许在远程上重写历史记录,只需在其历史记录中有压缩提交即可。也就是说,我的评论假设除了我自己之外没有其他人跟踪远程分支,以避免破坏历史记录。 - Steve B
这比不断地变基和解决冲突好多了。我从现在开始要这样做。谢谢你节省了我一个小时的时间:D - Aron Atilla Hegedus
多年来,我一直在与变基做斗争,而我想要的只是这个。谢谢你,善良的人。 - Michael Fulton
git checkout main -- 需要确保你在最新的分支上,如果主分支默认没有指向 HEAD 的话! - Sion C

88
重新设置基准点以获得“干净”的历史记录被过度评价了。如果想保留历史记录,最好的方法就是进行合并而不是重新设置基准点。这样,如果您需要返回到先前的版本,它与开发期间测试的版本完全相同。这也解决了之前已解决的合并冲突问题。
如果您不关心保留历史记录,可以从主分支创建一个新分支,检出它,然后执行git read-tree -u -m dev来更新您的工作树以匹配dev分支。然后您可以将所有内容提交为一个大型提交,并像平常一样将其合并到主分支中。

1
你在这里实际上做的是合并。新分支是不必要的。 - isak gilbert
3
只有当你使用 --squash 参数合并时才会相同。如果使用普通合并,如果在分支上有 N 个提交,则将向主分支添加 NN+1 个提交。建议使用上述的 merge --squash 命令,它将始终向主分支添加一个单独的提交。 - peterflynn
3
@ytpete,是的,我想上面所说的只是一种从dev合并到master的绕路方式。 "创建新分支" 只是指向与 master 相同的位置。 "将所有内容提交到一个大提交" 就是在执行 squash 操作。 - isak gilbert
6
合并提交在尝试查看单个分支的完整更改集时会产生噪音。当一个干净的历史记录使得这变得简单时,提取差异并查看它是一种痛苦。 - Ajax
11
我非常不同意“清白的”历史被高估这一说法。它并没有被高估,反而可以节省时间并使事情变得更加清晰明了。 - basickarl
显示剩余2条评论

15

两点说明:

  • 你可以在新获取的提交上反复将自己(尚未推送)的工作变基。
  • 如果您已经激活了 git rerere,则可以避免在变基过程中出现合并冲突,这对于这种情况非常有用。 http://git-scm.com/images/rerere2.png 更多信息请参见git rerere

@lulian:在这种情况下,如果您必须重新定位您的工作,我认为您将不得不再次解决那些合并冲突。 - VonC
@krlmlr 谢谢。我已经恢复了链接,添加了插图和该命令的 man 手册参考。 - VonC

10

您可以使用以下方式将分支中的所有更改合并到master分支的一个新提交中:

git diff master > my_branch.patch
git checkout master
patch -p1 < my_branch.patch

接着对文件进行分阶段操作并提交。


4

关于合并冲突的重放,您可以使用git rerere维护一个数据库来记录已经解决过的合并冲突,这样进行导入操作时如果出现相同的冲突,繁琐的部分将会自动完成。

https://hackernoon.com/fix-conflicts-only-once-with-git-rerere-7d116b2cec67

git config --global rerere.enabled true

需要注意的一点是,如果您错误地解决了某些问题,下一次也会自动产生问题,而您可能并没有意识到。

更加正式的文档可在此处找到:https://git-scm.com/docs/git-rerere


1

我发现@sky的回答很有帮助。然后我将其制作成了一个压缩重置函数。

假设您在your-branch上,这样做就是创建一个名为your-branch-tmp的分支,该分支是在主分支上执行merge --squash的结果。它还保留了所有提交消息并允许您进行编辑。重要的是,它也不会影响您当前的分支。

运行压缩重置后,如果您对其满意,则必须对tmp分支进行硬重置。然后实际上您已经完成了一个压缩重置。

function squash-rebase() {
  MESSAGES="$(git log $(git merge-base main HEAD)..HEAD --reverse --format=%B)"
  SRC_BRANCH="$(git_current_branch)"
  TMP_BRANCH="$(git_current_branch)-tmp"
  echo "creating temp branch $TMP_BRANCH from $(git_current_branch)"
  git checkout -b $TMP_BRANCH main  # create tmp branch from main and checkout
  git merge $SRC_BRANCH --squash
  git commit -m "$MESSAGES" -n
  git commit --amend -n # just so you can edit the messages
  git checkout $SRC_BRANCH
  echo "IMPORTANT: run git reset --hard $TMP_BRANCH to complete squash rebase."
  echo "First, though, you may run git diff $TMP_BRANCH to make sure no changes."
  echo "Finally, delete the tmp branch with git branch -D $TMP_BRANCH"
}

注意:假设您正在使用oh-my-zsh,否则您可能需要找到另一种方法来获取当前分支名称。

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