2012-01-20 87 views
547

好的,我认为这是一个简单的混帐方案,我错过了什么?功能分支rebase后Git推拒绝

我有一个master分支和一个feature分支。我在master上做了一些工作,一些在feature上,然后一些在master上。我结束了这样的事情(字典顺序意味着提交的顺序):

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

我不得不git push origin master没有问题,以保持远程master更新,也没有与git push origin feature(当feature)保持远程备份为我的feature工作。到目前为止,我们很好。

但现在我想在F--G上承诺对master进行rebase feature,所以我git checkout featuregit rebase master。还好。现在我们有:

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

问题:我要备份的新基featuregit push origin feature推支瞬间被拒绝自树已因改为垫底。这只能通过git push --force origin feature解决。

我讨厌使用--force而不确定我需要它。那么,我需要它吗?是否重新装订必然意味着下一个push应该是--force ful?

此功能分支不与任何其他开发人员共享,所以我没有问题事实上与力推,我不会失去任何数据,问题是更概念化。

+80

伟大的问题,一个很好的描述! – Nayan

+0

为了周期性地将主要的变化引入到你的特性分支(为了排除它们与正在开发的特性相冲突),是否可行的做法是:“同伴们,确保所有提交功能-1234中午,因为我会进行重新发展然后。 “(是的,手册,需要额外的沟通,但仍然...) –

回答

406

的问题是,git push假设远程分支可以快进到你的本地分支,那就是所有本地和远程分支机构之间的区别是在当地的在端部这样的一些新的提交:

Z--X--R   <- origin/some-branch (can be fast-forwarded to Y commit) 
     \   
     T--Y <- some-branch 

当您执行git rebase承诺d和E被应用到新基地和新提交的创建。这意味着你有类似的东西之后:

A--B--C------F--G--D'--E' <- feature-branch 
     \ 
     D--E    <- origin/feature-branch 

在这种情况下,远程分支不能被快速转发到本地。虽然,理论上本地分支可以合并到远程(显然你并不需要它),但是因为git push只执行快速合并它抛出错误。

什么--force选项不只是忽略远程分支的状态,并将其设置为提交你推了进去。所以git push --force origin feature-branch简单地覆盖origin/feature-branch与当地feature-branch

在我看来,只要你是唯一一个在该分支上工作的人,在master上重新布署功能分支并强制将它们推回到远程存储库是可以的。

+36

说实话,将原始版本的特性分支拉入并重新合并,消除了重新分配的整个想法。 –

+14

也许我没有正确理解你,但是如果你拉特性分支,将它重新绑定到新的主分支上,你不能无力地推回它,因为特性分支的远程版本不能被快速转发给你新的(重新发布的)版本的功能分支。这正是OP在他的问题中所描述的。如果在重新绑定之后,但在推送之前,您执行'git pull feature-branch',则该拉动将生成新的合并提交(通过合并远程和本地版本的功能分支)。所以要么在重新绑定后得到不必要的合并,要么用'--force'推送。 –

+5

啊,我想我明白了。您正在描述与Mark Longair的回答相同的方法。但它确实生成了合并提交。这在某些情况下可能会有用,但我主要在我自己的特性分支中使用rebase(因此'push --force'不是问题),以保持提交历史记录线性,而没有任何合并提交。 –

8

feature分支上的git merge master有什么问题?这将保留你的工作,同时保持它与主线分支分离。

A--B--C------F--G 
     \   \ 
     D--E------H 

编辑:不好意思啊没有看过您的问题声明。您执行rebase时需要强制执行。所有修改历史记录的命令都需要--force参数。这是一个故障安全防止你失去工作(旧的DE会丢失)。

所以你执行git rebase这使得树的样子(虽然部分隐藏的DE是在一个名为分支不再):

A--B--C------F--G 
     \   \ 
     D--E  D'--E' 

所以,试图推动新的feature分支(当其中D'E'在里面),你会失去DE

+3

这没什么错,我知道它会起作用。这不是我所需要的。就像我说的,这个问题比概念更概念化。 –

+0

对不起,请参阅我的编辑答案。 – bouke

19

解决这个问题的一个办法是做msysGit的rebasing merge脚本 - 在rebase之后,在feature的旧版本头合并-s ours。你最终提交图表:

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

...和你的feature推动将是一个快速前进。

换句话说,你可以这样做:

git checkout feature 
git branch old-feature 
git rebase master 
git merge -s ours old-feature 
git push origin feature 

(未测试,但我认为这是正确的......)

+18

我相信使用'git rebase'(而不是将'master'合并到你的特性分支中)的最常见原因是使得clean线性提交历史记录。随着你的方法承诺历史变得更糟。而且由于重新绑定创建了新的提交而没有提及它们以前的版本,我甚至不确定这种合并的结果是否足够。 –

+4

@ KL-7:“合并我们的”我们的整个重点是它人为地将父引用添加到以前的版本。当然,历史看起来并不干净,但是提问者似乎特别被强迫推特功能分支所困扰,并且这一点得到了解决。如果你想重组,那么它是或多或少的。 :)更一般地说,我认为有趣的是,msysgit项目这样做...... –

+0

@ KL-7:顺便说一句,我+你的答案,这显然是正确的 - 我只是认为这也许是有趣的。 –

11

其他的已经回答了你的问题。如果您重新设置分支,则需要强制推送该分支。

和衍合一个共享仓库一般不会相处。这是重写历史。如果其他人正在使用该分支或从该分支分支,则分叉将会非常不愉快。

一般而言,底垫可以很好地用于当地分支机构管理。远程分支管理通过显式合并(--no-ff)最适合。

我们还避免合并掌握到一个特性分支。相反,我们要重新掌握,但有一个新的分支名称(例如添加版本后缀)。这可以避免共享存储库中出现重新绑定的问题。

+4

请问您可以添加一个示例吗? – Thermech

14

它可能会或可能不会是只有一个在这个分支开发的情况下,也就是现在(底垫后)不与产地/功能内联。

因此我会建议使用以下顺序:

git rebase master 
git checkout -b feature_branch_2 
git push origin feature_branch_2 

呀,新的分支,这应该解决这个没有--force,我普遍认为是一个主要的git的缺点。

+2

对不起,但是:“继续生成分支”以避免强制覆盖现有分支并不能帮助“孤独的功能开发者”(可以覆盖),也没有多个人在功能分支上工作(需要传递该分支“增量”和告诉移动,乡亲)。 - 更像是在版本控制系统中手动版本控制(“thesis_00.doc,thesis_01.doc,...”)... –

+0

另外,当你在一个分支名称上打开github PR时,这没有帮助,你会必须为您推送的新分支名称创建一个新的PR。 – gprasant

+1

@frankee根据我的经验,一半是真实的。对于一个孤独的开发者来说,是的,仅仅推动就足够简单了,但是这种习惯可能会在以后引起你的注意。 +刚刚加入的新开发者?或者可能是一些不使用 - 硬重置的CI系统?对于一个团队协作,我认为沟通新的分支名称很简单,这也可以很容易地编写脚本+对于一个团队来说,我会建议在本地重新分配,或者当分支准备合并时,而不是在日常工作中,那么额外的提交就不会像处理因为rebase/merge冲突一样的麻烦了。 –

2

对我来说,以下工作:

git push -f origin branch_name

,它不会删除我的任何代码。

但是,如果你想避免这种情况,那么你可以做到以下几点:

git checkout master 
git pull --rebase 
git checkout -b new_branch_name 

那么你可以樱桃采摘所有提交到新的分支。 git cherry-pick COMMIT ID 然后推送你的新分支。

+2

'-f'是'--force'的别名,这就是问题在可能的情况下试图避免的问题。 –

+0

谢谢@PresidentEvil我已经更新了我的答案。 –

1

由于OP不明白的问题,只是寻找一个更好的解决方案...

这个怎么样的做法?

  • 对实际功能开发分支(如果你从来没有变基和力量推动,以便其他功能的开发人员不恨你)。在这里,定期从合并中获取主要的变化。 梅西尔的历史,是的,但生活很简单,没有人在他的工作中被中断。

  • 有第二个功能开发分支,其中一个功能团队成员regulary推动所有功能承诺,确实基期数,确实被迫的。所以几乎干净地基于最近的主提交。功能完成后,将该分支推送到主设备上。

此方法可能存在一个模式名称。

24

我会用“checkout -b”来代替,它更容易理解。

git checkout myFeature 
git rebase master 
git push origin --delete myFeature 
git push origin myFeature 

当您删除您时,阻止推入包含不同SHA ID的退出分支。 我在这种情况下只删除远程分支。

+2

这很好用,特别是如果你的团队有一个拒绝所有git push --force命令的git钩子。 –

+0

谢谢你,它运作良好。这里有更多的细节,我读了更好地理解。当你不想或不能做强制推送时,这非常有用。 [删除远程分支](https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches#Deleting-Remote-Branches)和[Rebasing](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) – RajKon

141

而不是使用-f或--force开发人员应该使用

--force-with-lease 

为什么呢?因为它检查远程分支的变化,这绝对是一个好主意。让我们想象一下James和Lisa正在制作相同的功能分支,Lisa推送了一个提交。詹姆斯现在重新设置他的本地分支,并在尝试推进时被拒绝。当然詹姆斯认为这是由于rebase和使用 - 力,并会重写所有Lisa的变化。如果詹姆斯使用“强制租赁”的方式,他会收到一个警告,说有人提交了一些提交。我不明白为什么有人会在推出rebase之后使用--force而不是--force-with-lease。

+6

很好的解释。 'git push --force-with-lease'为我节省了一大笔钱。 – ckib16

3

我避免力推的方法是在新的分支上建立一个新的分支,继续工作,经过一番稳定性,删除旧的分支,是重建基础:

  • 衍合签出的分支机构本地
  • 将分支从重新分支分支到新分支
  • 将分支作为新分支推入远程分支。并删除旧的分支在远程
+0

为什么不喜欢这个选项?它绝对是最干净,最简单,最安全的。 – cdmo