功能分支rebase后Git推送被拒绝


Git push rejected after feature branch rebase

好吧,我认为这是一个简单的git场景,我错过了什么?

我有一个master分支和一个feature分支。我在master上做了一些工作,在feature上做了一些工作,然后在master上做了一些工作。我最终得到这样的东西(词典顺序暗示了提交的顺序):

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

我没有问题git push origin master保持远程master更新,也没有git push origin feature(当在feature上时)维护我的feature工作的远程备份。到目前为止,我们很好。

但是现在我想在master上的F--G提交之上重新定义feature,所以我git checkout featuregit rebase master。还好。现在我们有:

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

问题:当我想用git push origin feature备份新的rebased feature分支时,推送被拒绝,因为树由于变基而发生了变化。这只能用git push --force origin feature解决。

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

这个功能分支不与任何其他开发者共享,所以我事实上没有问题,推力,我不会丢失任何数据,问题更具概念性。


问题是git push假设远程分支可以快速转发到本地分支,即本地和远程分支之间的所有区别都在本地有一些新的提交,如下所示:

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

执行git rebase提交时,D和E将应用于新基础并创建新提交。这意味着在rebase之后你会有类似的东西:

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

在那种情况下,远程分支无法快速转发到本地。虽然,理论上本地分支可以合并到远程(显然你不需要它),但由于git push只执行快进合并,它会引发错误。

--force选项所做的只是忽略远程分支的状态并将其设置为您正在推送的提交。所以git push --force origin feature-branch只需用本地feature-branch覆盖origin/feature-branch

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


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

1
--force-with-lease

为什么?因为它检查远程分支的更改,这绝对是个好主意。让我们想象一下,詹姆斯和丽莎正在研究同一个功能分支,丽莎已经推动了提交。詹姆斯现在重新调整他的当地分支,并在试图推动时被拒绝。当然,詹姆斯认为这是因为改变并使用--force并且会改写所有Lisa的变化。如果詹姆斯曾经使用过--force-with-lease,那么他会收到一个警告,告知其他人已做过提交。我不明白为什么有人会在推销一个rebase之后使用--force而不是--force-with-lease。


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

1
2
3
4
git checkout myFeature
git rebase master
git push origin --delete myFeature
git push origin myFeature

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


对此的一个解决方案是执行msysGit的rebasing合并脚本所做的事情 - 在rebase之后,将feature的旧头与-s ours合并。你最终得到了提交图:

1
2
3
4
5
6
7
A--B--C------F--G (master)
       \         \
        \         D'--E' (feature)
         \           /
          \       --
           \    /
            D--E (old-feature)

...而你推动feature将是一个快进。

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

1
2
3
4
5
git checkout feature
git branch old-feature
git rebase master
git merge -s ours old-feature
git push origin feature

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


可能是也可能不是这个分支上只有一个开发人员,现在(在rebase之后)不与原点/特征内联。

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

1
2
3
git rebase master
git checkout -b feature_branch_2
git push origin feature_branch_2

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


我避免强制推送的方法是创建一个新的分支并继续在新分支上工作,并在一些稳定之后,删除已重新分支的旧分支:

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


其他人已经回答了你的问题。如果你重新分支一个分支,你将需要强制推动该分支。

Rebase和共享存储库通常不相处。这是重写历史。如果其他人正在使用该分支或从该分支分支,那么rebase将是非常不愉快的。

一般而言,rebase适用于本地分支机构管理。远程分支管理最适合使用显式合并(--no-ff)。

我们还避免将master合并到功能分支中。相反,我们使用新的分支名称(例如添加版本后缀)来重新掌握。这避免了共享存储库中的重新定位问题。


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

1
2
3
A--B--C------F--G
       \         \
        D--E------H

编辑:啊抱歉没有看到你的问题陈述。执行rebase时需要强制。修改历史记录的所有命令都需要--force参数。这是一个防止您丢失工作的故障保护(旧的DE将丢失)。

所以你执行了一个git rebase,它使树看起来像(虽然部分隐藏为D并且E不再在命名分支中):

1
2
3
A--B--C------F--G
       \         \
        D--E      D'--E'

因此,当您尝试推送新的feature分支(其中包含D'E')时,您将丢失DE


以下适用于我:

git push -f origin branch_name

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

但是,如果您想避免这种情况,那么您可以执行以下操作:

1
2
3
git checkout master
git pull --rebase
git checkout -b new_branch_name

然后你可以挑选你所有的提交到新的分支。
git cherry-pick COMMIT ID
然后推动你的新分支。


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

1
2
3
4
5
6
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分支:

1
git push origin --delete myFeature

由于OP确实理解了这个问题,只需寻找更好的解决方案......

这个练习怎么样?

  • 拥有实际的功能开发分支(你永远不会改变和强制推送,所以你的同事功能开发人员不讨厌你)。在这里,通过合并定期从main获取这些更改。梅西耶的历史,是的,但生活很简单,没有人在他的工作中得到满足。

  • 有一个第二个功能开发分支,其中一个功能团队成员定期推送所有功能提交,确实是重新设置,确实是强制的。所以几乎干净地基于最近的主提交。功能完成后,将该分支推送到主服务器的顶部。

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