功能分支重新设置后,Git推送被拒绝


916

好的,我以为这是一个简单的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工作维护远程备份。到目前为止,我们还不错。

但是现在我想基于master featureF--G提交基础,所以我git checkout featuregit rebase master。还好。现在我们有:

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

问题:我想备份新的feature分支后git push origin feature,由于该树由于重新定基而发生更改,因此推送被拒绝。这只能通过解决git push --force origin feature

我讨厌--force不确定我是否需要它。所以,我需要吗?变基是否必然意味着下一个push应该满足--force

此功能分支未与任何其他开发人员共享,因此对于强制推送我实际上没有问题,我也不会丢失任何数据,这个问题更具概念性。

Answers:


681

问题在于,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使用local 覆盖feature-branch

在我看来,master只要您是该分支上唯一的成员,就可以对功能分支进行重新基础设置并将其强制推回到远程存储库中是可以的。


67
老实说,将要素分支的原始版本合并到经过重新调整的基础中完全消除了重新调整基础的整个想法。
2013年

24
也许我不太了解您,但是如果您拉出功能分支,将其重新设置到新的主分支上,就无法无力地将其推回,因为功能分支的远程版本无法快速转发到新的分支(基于)功能分支的版本。这正是OP在他的问题中所描述的。如果在重新设置基准之后但在推送之前执行git pull feature-branch,则此拉取将生成新的合并提交(通过合并功能分支的远程版本和本地版本)。因此,要么在重新定基后获得不必要的合并,要么用推送--force
KL-7

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

11
“强制推送”的麻烦在于,您确实可以“松散的东西”(事先提交),通常在任何版本控制系统中都永远不可能做到这一点➪因此,至少应有一个“主-ish”分支设置为不接受强制推动,以限制潜在的损坏。(列举以下任何一项:脾气暴躁/被解雇的员工,自己的愚蠢,疲倦和过度劳累的“决定” ...)。
Frank Nocke 2015年

13
--force-with-lease正如@hardev建议的那样,这是一个不错的选择
augustorsouza,2018年

464

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

--force-with-lease

为什么?因为它检查远程分支的更改,这绝对是个好主意。假设James和Lisa正在同一个功能分支上,而Lisa已推送了一次提交。詹姆斯现在将其本地分支机构重新设基,并在尝试推送时被拒绝。当然,詹姆斯认为这是由于改组并使用--force造成的,并将重写Lisa的所有更改。如果詹姆斯使用了“租用强制”,他将收到警告,提示其他人进行了提交。我看不出为什么有人在重新定级后进行推送时会使用--force而不是--force-with-lease。


33
很好的解释。git push --force-with-lease救了我一堆。
ckib16

5
这是一个有用的评论,但实际上并不是该问题的答案。
达林

4
这就是答案,重新掌握/开发会产生问题,这就是--force-with-lease存在的原因。
塔米尔·丹尼尔

3
这应该是公认的答案。确切地解决了所描述的问题-在此期间如果没有其他人犯规,则强制执行而不会强制执行。
Luzian

3
我认为既可以接受的答案,也可以解决这个问题。接受的答案说明了为什么需要强制。而这一次解释了为什么--force-with-lease地址使用的关注--force
杰夫Appareti

47

我会改用“ checkout -b”,这更容易理解。

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

删除时,您可以防止推送包含不同SHA ID的现有分支。在这种情况下,我仅删除远程分支。


6
这很好用,特别是如果您的团队有一个git hook拒绝所有git push --force命令。
瑞安·泰晤士

1
谢谢你,它运作良好。这是我阅读以了解更多信息的更多详细信息。当您不想或无法强行推动时,此功能非常有用。删除远程分支机构重新定基础
RajKon '16

5
这具有相同的结果push --force,因此,这只是绕过git repo预防的一种方法--force。因此,我认为这绝不是一个好主意-要么是回购许可push --force,要么是出于充分的原因,它禁用了它。如果--force在远程仓库上禁用了Nabi的答案,则它更合适,因为它不会丢失其他开发者的提交或引起问题的风险。
Logan Pickup

19

一种解决方案是执行msysGit的重定基合并脚本的工作-在重定基数后,将其合并到featurewith中-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

(未经测试,但我认为是对的...)


26
我相信使用git rebase(而不是master重新合并到功能分支中)的最常见原因是制作干净的线性提交历史记录。使用您的方法提交历史会变得更糟。而且由于重新定基会创建新的提交而不参考其先前版本,所以我什至不确定此合并的结果是否足够。
2012年

6
@ KL-7:的全部要点merge -s ours是,它人为地为以前的版本添加了父引用。当然,历史看起来并不干净,但发问者似乎尤其被困扰,因为他不得不强行推动feature分支机构的工作,而这种情况就绕开了。如果您想重新定基,则它或多或少。:)更笼统地说,我认为msysgit项目可以做到这一点很有趣....
Mark Longair 2012年

@ KL-7:顺便说一句,我+1了您的答案,这显然是正确的答案-我只是认为这也可能很有趣。
Mark Longair 2012年

至少对我来说,这绝对是有趣的。谢谢。我之前已经看过ours策略,但是我认为它仅适用于冲突情况,方法是使用分支中的更改自动解决它们。事实证明,它的工作原理有所不同。并以这种方式工作,如果您需要基于版本的版本(例如,用于repo维护者将其干净地应用于master),但又希望避免强行推送(如果由于某种原因,许多其他ppl使用了您的功能分支),则非常有用。
KL-7

15

在这个分支上可能只有一个开发人员,也可能不是,即现在(在重新设置基础之后)不与源/功能内联。

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

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

是的,新分支,这应该在没有--force的情况下解决此问题,我认为这通常是git的主要缺点。


3
抱歉地说:“保持生成分支”以避免强行覆盖现有分支并不能帮助“孤独的特征开发人员”(谁可以覆盖),也没有多人在功能分支上工作(需要传达该分支“增量”并告知过来)。—更像是在版本控制系统中的手动版本控制(“ thesis_00.doc,thesis_01.doc,...”)……
Frank Nocke 2015年

2
另外,当您在一个分支名称上打开github PR时,这无济于事,您必须为您推送的新分支名称创建一个新PR。
gprasant 2015年

1
@frankee根据我的经验,这是一半。是的,对于一个孤独的开发人员来说,仅仅强制推销就足够了,但这是以后可能会咬你的习惯。+一个新的开发人员刚刚加入?还是某些不使用--hard reset的CI系统?对于团队合作,我认为传达新的分支名称很容易,也可以很容易地编写脚本+对于团队来说,我建议在本地进行基础调整或分支准备合并时,而不是在日常工作中,额外的提交比处理重新设置/合并冲突少了很多麻烦。
JAR.JAR.beans 2015年

@gprasant for PR,再次,我认为这对于重新设置基准是错误的,我实际上想查看具有PR修复程序的单个提交。只有在合并到主数据库以及PR已全部准备就绪(因此不需要打开新的PR)时,才应在以后进行重新设置(压缩)。
2015年

13

我避免强行推入的方法是创建一个新分支,并继续对该新分支进行工作,并在保持一定稳定性之后,删除重新建立基础的旧分支:

  • 在本地重新建立已签出的分支
  • 从重新建立基础的分支到新分支
  • 将该分支作为新分支推送到远程。并删除远程上的旧分支

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

因为我有大约200个跟踪分支名称的系统,而且它必须是任务的特定名称,并且如果我开始进行分支重命名,那么每次推送都会使我大失所望。
塔米尔丹妮

@TamirDaniely我还没有尝试过,但是在以相同的旧名称推送新的分支之前删除旧的分支(从远程)是否可以解决您的问题?
纳比

2
@Nabi --force-with-lease就是这么做的,除了它还会验证没有不属于您的新提交。
塔米尔丹妮

12

别人已经回答了您的问题。如果您为分支设置了基准,则需要强制推送该分支。

变基和共享存储库通常不会相处。这是重写历史。如果其他人正在使用该分支或已从该分支分支,则重新设置基准将非常不愉快。

通常,对本地分支机构管理来说,改组效果很好。远程分支管理最适合与显式合并(--no-ff)一起使用。

我们还避免将master合并到功能分支中。取而代之的是,我们改用母版,但使用新的分支名称(例如,添加版本后缀)。这避免了在共享存储库中重新设置基准的问题。


5
你能补充一个例子吗?
Thermech 2014年

8

分支git merge master上的a有什么问题feature?这将保留您所做的工作,同时将其与主线分支分开。

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

编辑:抱歉没有阅读您的问题说明。进行练习时需要用力rebase。所有修改历史记录的命令都将需要该--force参数。这是一种故障保险,可以防止您丢失工作(旧的DE将会丢失的)。

因此,您执行了一个操作git rebase,使树看起来像(尽管部分隐藏为D并且E不再在命名分支中):

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

因此,当尝试推动新feature分支(使用D'E'在其中)时,您将失去DE


3
没什么错,我知道它会起作用。这不是我所需要的。就像我说的那样,这个问题更具概念性,而不是实际性。
Yuval Adam

4

对我来说,遵循简单的步骤即可:

1. git checkout myFeature
2. git rebase master
3. git push --force-with-lease
4. git branch -f master HEAD
5. git checkout master
6. git pull

完成上述所有操作后,我们还可以通过以下命令删除myFeature分支:

git push origin --delete myFeature

3

以下对我有用:

git push -f origin branch_name

并且不会删除我的任何代码。

但是,如果要避免这种情况,可以执行以下操作:

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

那么您就可以将所有提交樱桃挑选到新分支。 git cherry-pick COMMIT ID 然后推送您的新分支。


5
-f是的别名--force,如果可能的话,这就是问题要避免的别名。
epochengine

1

由于OP确实了解了问题,因此只寻找更好的解决方案...

作为实践呢?

  • 拥有实际的功能开发分支(您永远无需重新设置基础和强制推送,因此您的同行功能开发人员不会讨厌您)。在这里,请定期合并合并中的更改。梅西耶(Messier)的历史,是的,但是生活很轻松,没有人对他的工作有所了解。

  • 拥有第二个功能开发分支,其中一个功能团队成员定期将所有功能提交推送到,实际上是基于基础的,甚至是强制执行的。因此,几乎完全是基于最近的主提交。功能完成后,将该分支推入master顶部。

该方法可能已经有一个模式名称。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.