在工作中进行分支合并的时候出现了这样的一个问题,正好在stackoverflow上已经有人提出了这样的疑问,正好简单做个小结。

原汁原味问答: https://stackoverflow.com/questions/8939977/git-push-rejected-after-feature-branch-rebase

问题点

在C点开出了feature分支,但是因为某些原因发了一个小版本,将这部分代码合到了master,也就是F,G,这时候需要做一次feature的rebase,当前的分支情况如下

A--B--C------F--G  (master)
       \    
        D--E  (feature)

这时候我们希望合并之后长这样:

A--B--C------F--G  (master)
                 \
                  D'--E'  (feature)

但是我们在本地合并之后无法推送到远程分支,被拒绝了,但是可以走git push --force强推解决问题,那么为什么不能推到远程分支呢?

解答

我们需要解答几个问题:

  1. git push的合并方式是什么样的?
  2. fast-forward是什么?
  3. 为什么git push会有fast-forward?

第一个问题:git push使用fast-forward进行合并,对于local(本地分支)和origin(远程分支)分支来说,区别应该只是local多了一些新的commit,所以push的结果是在远程的基础上往前移动指针,覆盖新的commit。

第二个问题:

参见:http://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E6%96%B0%E5%BB%BA%E4%B8%8E%E5%90%88%E5%B9%B6

在合并的时候,你应该注意到了“快进(fast-forward)”这个词。 由于你想要合并的分支 hotfix 所指向的提交 C4 是你所在的提交 C2 的直接后继, 因此 Git 会直接将指针向前移动。换句话说,当你试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候, 只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。

第三个问题:Git 为了保护远程分支不被覆盖,所以无法在不丢失提交的情况下对远程仓库进行更改,发生此情况时,推送会被拒绝。更多知识

所以最终我们知道:git push使用的是快速合并(fast-forward merge),所以无法解决本地与远程基不一致的合并,出现冲突则不允许提交,这时候的解决办法就是强推 git push -f

但是这显然也要考虑到是不是会有多人协作的问题,比如有人在你正好要强推之前提交了代码,你就会把他的代码覆盖掉,这时候别人的提交就会有问题!因此在下面有一个回答提到了一个安全的强制提交命令git push --force-with-lease,使用此参数推送,如果远端有其他人推送了新的提交,那么推送将被拒绝。