ChatGPT解决这个技术问题 Extra ChatGPT

功能分支变基后 Git 推送被拒绝

git

好的,我以为这是一个简单的 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 上的 F--G 提交之上重新设置 feature,所以我要 git checkout featuregit rebase master。还好。现在我们有:

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

问题:当我要备份与 git push origin feature 分支的新重新定位的 feature 时,推送被拒绝,因为树已因重新定位而改变。这只能用 git push --force origin feature 解决。

我讨厌在不确定自己需要的情况下使用 --force。那么,我需要它吗?变基必然是否暗示下一个 push 应该是 --forceful?

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

--force 不是怪物,它是一项功能。您可以在需要时使用它。

s
slm

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

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

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

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 上重新设置功能分支并将它们强制推回远程存储库是可以的,只要您是唯一在该分支上工作的人。


老实说,将特性分支的原始版本拉入并合并到重新定位的版本中,有点消除了重新定位的整个想法。
也许我没有正确理解你,但是如果你拉特性分支,将它重新定位到新的主分支上,你不能不强制将它推回去,因为特性分支的远程版本不能快速转发到你的新分支(重新定位)功能分支的版本。这正是 OP 在他的问题中所描述的。如果在变基之后,但在推送之前,您执行 git pull feature-branch,则此拉取将生成新的合并提交(通过合并功能分支的远程和本地版本)。因此,要么在变基后得到不必要的合并,要么使用 --force 推送。
啊,我想我明白了。您描述的方法与 Mark Longair 的回答相同。但它确实会生成一个合并提交。在某些情况下它可能很有用,但我主要在我自己的功能分支中使用 rebase(因此 push --force 不是问题)来保持提交历史线性,根本没有任何合并提交。
“强制推送”的问题在于,您确实可以“松散东西”(之前的提交),这在任何版本控制系统中通常都不应该发生➪ 因此,至少一个“master-ish”分支应该有设置为 not accept force-pushes,以限制潜在的损坏。 (说出以下任何一项:脾气暴躁/被解雇的员工、自己的白痴、疲倦和过度劳累的“决定”……)。
@hardev 建议的 --force-with-lease 是一个不错的选择
d
djvg

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

--force-with-lease

为什么?因为它检查远程分支是否有更改,这绝对是个好主意。让我们假设 James 和 Lisa 正在开发同一个功能分支,并且 Lisa 已经推送了一个提交。 James 现在重新设置了他的本地分支并在尝试推送时被拒绝。当然,James 认为这是由于变基并使用 --force 并会重写 Lisa 的所有更改。如果 James 使用了 --force-with-lease,他会收到一个警告,说有其他人完成了提交。我不明白为什么有人会在 rebase 后推送时使用 --force 而不是 --force-with-lease


我认为接受的答案和这个答案都解决了这个问题。接受的答案解释了为什么你需要强制。这解释了为什么 --force-with-lease 解决了使用 --force 的问题
这个答案的问题是 --force-with-lease 派上用场的场景是重新设置超过 1 人正在处理的共享分支。没有人应该首先重新设置共享/协作分支的基础。变基的黄金法则:不要在公共/共享分支上使用变基
反正我觉得还是蛮有用的。也许有人不知道其他人推到了同一个分支。因此,使用 --force-with-lease 可以看到这一点,并可以选择恢复他的变基,例如进行合并。或应用另一种解决方案。
更好的是使用这两个标志的组合:--force-with-lease --force-if-includes。它比 ----force-with-lease 安全得多,例如当 IDE 进行后台提取时。
E
Eddy Hernandez

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

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

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


这很好用,特别是如果您的团队有一个拒绝所有 git push --force 命令的 git 钩子。
谢谢你,它运作良好。这是我阅读以更好地理解的更多详细信息。当您不想或不能进行强制推动时,这非常有用。 Deleting Remote BranchesRebasing
这与 push --force 具有相同的结果,因此只是绕过 git repo 防止 --force 的一种方法。因此,我认为这不是一个好主意 - 要么回购允许 push --force,要么出于充分的理由禁用它。如果在远程存储库中禁用 --force,Nabi 的答案更合适,因为它没有丢失其他开发人员提交或以其他方式导致问题的风险。
git push origin --delete 不同,git push --force 不会关闭合并请求。
M
Mark Longair

对此的一种解决方案是执行 msysGit 的 rebasing merge 脚本所做的事情 - 在变基之后,将 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

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


我相信使用 git rebase(而不是将 master 合并回您的功能分支)的最常见原因是制作干净的线性提交历史记录。随着你的方法提交历史变得更糟。并且由于 rebase 创建新的提交而不参考他们以前的版本,我什至不确定这个合并的结果是否足够。
@KL-7:merge -s ours 的全部意义在于它人为地添加了对先前版本的父引用。当然,历史看起来并不干净,但发问者似乎特别为不得不强制推动 feature 分支而烦恼,这解决了这个问题。如果你想变基,它或多或少是一个或另一个。 :) 更一般地说,我认为 msysgit 项目这样做很有趣....
@KL-7:顺便说一句,我 +1 了你的答案,这显然是正确的 - 我只是觉得这可能也很有趣。
这绝对是有趣的,至少对我来说。谢谢你。我以前见过 ours 策略,但我认为它仅适用于冲突情况,通过使用我们分支中的更改自动解决它们。事实证明它的工作方式不同。如果您需要 rebase 版本(例如,repo 维护者将其干净地应用到 master)但想要避免强制推送(如果许多其他 ppl 出于某种原因正在使用您的功能分支),那么以这种方式工作非常有用。
@KL-7 “我认为它仅适用于冲突情况,通过使用我们分支中的更改自动解决它们。”我知道这是一篇 非常 旧的帖子,但需要澄清一些。您所描述的将“我们的”选项与“递归”(默认)策略相匹配。 UI 中“我们的”这个词的重载是不幸的。 (策略选项使用 -X 参数给出。)
B
Bill Door

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

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

一般来说,rebase 非常适合本地分支管理。远程分支管理最适合显式合并 (--no-ff)。

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


你能添加一个例子吗?
J
JAR.JAR.beans

这个分支上只有一个开发人员可能会也可能不会,即现在(在 rebase 之后)不与源/功能内联。

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

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

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


很抱歉,但是:“继续生成分支”以避免强制覆盖现有的分支并不能帮助“孤独的功能开发人员”(可以覆盖)或多个在功能分支上工作的人(需要传达该分支“增量”并告诉搬过去,伙计们)。 — 它更像是版本控制系统中的手动版本控制(“thesis_00.doc, thesis_01.doc, ...”)...
另外,当您在一个分支名称上打开 github PR 时,这并没有帮助,您必须为您推送的新分支名称创建一个新 PR。
@frankee 根据我的经验,一半是真的。对于一个孤独的开发者来说,是的,只是强制推送很容易,但它是以后可能会咬你的习惯。 + 新开发者刚刚加入?或者可能是一些不使用 --hard reset 的 CI 系统?对于协作的团队,我认为传达新的分支名称很容易,这也可以很容易地编写脚本 + 对于团队,我建议在本地或分支准备合并时进行 rebase,而不是在日常工作中,额外的提交比处理变基/合并冲突更容易。
@gprasant 再次代表 PR,我认为重新设置基准是错误的,我实际上希望看到带有 PR 修复的单个提交。变基(壁球)应该仅在稍后作为合并到 master 的一部分并且当 PR 全部完成并准备好时发生(因此不需要打开新的 PR)。
N
Nabi

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

在本地重新设置签出的分支

从重新定位的分支分支到新分支

将该分支作为新分支推送到远程。并删除远程上的旧分支


为什么不喜欢这个选项?它绝对是最干净、最简单、最安全的。
因为我有大约 200 个系统跟踪分支名称,并且它必须是任务的特定名称,如果我开始每次推送都重命名分支,我会失去理智。
@TamirDaniely我没有尝试过,但是在推送和推送具有相同旧名称的新分支之前删除旧分支(从远程)是否可以解决您的问题?
@Nabi 这正是 --force-with-lease 所做的,除了它还验证没有不属于您的新提交。
B
Bouke

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


这没有什么问题,我知道它会起作用。这不是我需要的。就像我说的,这个问题是概念性的而不是实际的。
N
Neeraj Khede

对我来说,以下简单的步骤有效:

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

S
Sohair Ahmad

以下对我有用:

git push -f origin branch_name

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

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

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

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


-f--force 的别名,这是问题尽可能避免的问题。
a
algus

在最新的 master 之上获取 master 和 rebase 特性分支的新变化

git checkout master
git pull
git checkout feature
git pull --rebase origin master
git push origin feature

F
Frank Nocke

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

这作为一种练习怎么样?

拥有实际的特性开发分支(你永远不会变基和强制推送,所以你的特性开发人员不会讨厌你)。在这里,通过合并定期从 main 中获取这些更改。梅西耶的历史,是的,但生活很轻松,没有人会打扰他的工作。

有第二个特性开发分支,一个特性团队成员定期推送所有特性提交,实际上是重新设置,实际上是强制的。所以几乎完全基于最近的一次主提交。功能完成后,将该分支推送到 master 之上。

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


K
Karthik

我会做如下

rebase feature
git checkout -b feature2 origin/feature
git push -u origin feature2:feature2
Delete the old remote branch feature
git push -u origin feature:feature

现在遥控器将具有功能(基于最新主控)和功能2(具有旧主控头)。如果您在解决冲突时犯了错误,这将允许您稍后进行比较。