好的,我以为这是一个简单的 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 feature
和 git rebase master
。还好。现在我们有:
A--B--C------F--G (master)
\
D'--E' (feature)
问题:当我要备份与 git push origin feature
分支的新重新定位的 feature
时,推送被拒绝,因为树已因重新定位而改变。这只能用 git push --force origin feature
解决。
我讨厌在不确定自己需要的情况下使用 --force
。那么,我需要它吗?变基必然是否暗示下一个 push
应该是 --force
ful?
此功能分支不与任何其他开发人员共享,因此我对强制推送实际上没有问题,我不会丢失任何数据,这个问题更具概念性。
--force
不是怪物,它是一项功能。您可以在需要时使用它。
问题是 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
上重新设置功能分支并将它们强制推回远程存储库是可以的,只要您是唯一在该分支上工作的人。
而不是使用 -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 --force-if-includes
。它比 ----force-with-lease
安全得多,例如当 IDE 进行后台提取时。
我会改用“checkout -b”,这样更容易理解。
git checkout myFeature
git rebase master
git push origin --delete myFeature
git push origin myFeature
当您删除时,您会阻止推送包含不同 SHA ID 的现有分支。在这种情况下,我只删除远程分支。
push --force
具有相同的结果,因此只是绕过 git repo 防止 --force
的一种方法。因此,我认为这不是一个好主意 - 要么回购允许 push --force
,要么出于充分的理由禁用它。如果在远程存储库中禁用 --force
,Nabi 的答案更合适,因为它没有丢失其他开发人员提交或以其他方式导致问题的风险。
git push origin --delete
不同,git push --force
不会关闭合并请求。
对此的一种解决方案是执行 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 创建新的提交而不参考他们以前的版本,我什至不确定这个合并的结果是否足够。
merge -s ours
的全部意义在于它人为地添加了对先前版本的父引用。当然,历史看起来并不干净,但发问者似乎特别为不得不强制推动 feature
分支而烦恼,这解决了这个问题。如果你想变基,它或多或少是一个或另一个。 :) 更一般地说,我认为 msysgit 项目这样做很有趣....
ours
策略,但我认为它仅适用于冲突情况,通过使用我们分支中的更改自动解决它们。事实证明它的工作方式不同。如果您需要 rebase 版本(例如,repo 维护者将其干净地应用到 master
)但想要避免强制推送(如果许多其他 ppl 出于某种原因正在使用您的功能分支),那么以这种方式工作非常有用。
-X
参数给出。)
其他人已经回答了你的问题。如果你重新设置一个分支,你将需要强制推送该分支。
Rebase 和共享存储库通常无法相处。这是改写历史。如果其他人正在使用该分支或已从该分支分支,那么 rebase 将非常令人不快。
一般来说,rebase 非常适合本地分支管理。远程分支管理最适合显式合并 (--no-ff)。
我们还避免将 master 合并到功能分支中。相反,我们变基为 master 但使用新的分支名称(例如添加版本后缀)。这避免了在共享存储库中变基的问题。
这个分支上只有一个开发人员可能会也可能不会,即现在(在 rebase 之后)不与源/功能内联。
因此,我建议使用以下顺序:
git rebase master
git checkout -b feature_branch_2
git push origin feature_branch_2
是的,新分支,这应该在没有 --force 的情况下解决这个问题,我认为这通常是 git 的主要缺点。
我避免强制推送的方法是创建一个新分支并继续在该新分支上工作,并在稳定后删除重新定位的旧分支:
在本地重新设置签出的分支
从重新定位的分支分支到新分支
将该分支作为新分支推送到远程。并删除远程上的旧分支
feature
分支上的 git merge master
有什么问题?这将保留您的工作,同时将其与主线分支分开。
A--B--C------F--G
\ \
D--E------H
编辑:抱歉,没有阅读您的问题说明。执行 rebase
时需要用力。所有修改历史的命令都需要 --force
参数。这是防止您丢失工作的故障保险(旧的 D
和 E
会丢失)。
因此,您执行了一个 git rebase
,它使树看起来像(尽管部分隐藏,因为 D
和 E
不再位于命名分支中):
A--B--C------F--G
\ \
D--E D'--E'
因此,当尝试推送新的 feature
分支(其中包含 D'
和 E'
)时,您将丢失 D
和 E
。
对我来说,以下简单的步骤有效:
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
以下对我有用:
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
的别名,这是问题尽可能避免的问题。
在最新的 master 之上获取 master 和 rebase 特性分支的新变化
git checkout master
git pull
git checkout feature
git pull --rebase origin master
git push origin feature
由于 OP 确实理解了这个问题,所以只是寻找一个更好的解决方案......
这作为一种练习怎么样?
拥有实际的特性开发分支(你永远不会变基和强制推送,所以你的特性开发人员不会讨厌你)。在这里,通过合并定期从 main 中获取这些更改。梅西耶的历史,是的,但生活很轻松,没有人会打扰他的工作。
有第二个特性开发分支,一个特性团队成员定期推送所有特性提交,实际上是重新设置,实际上是强制的。所以几乎完全基于最近的一次主提交。功能完成后,将该分支推送到 master 之上。
此方法可能已经有一个模式名称。
我会做如下
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(具有旧主控头)。如果您在解决冲突时犯了错误,这将允许您稍后进行比较。
不定期副业成功案例分享
git pull feature-branch
,则此拉取将生成新的合并提交(通过合并功能分支的远程和本地版本)。因此,要么在变基后得到不必要的合并,要么使用--force
推送。push --force
不是问题)来保持提交历史线性,根本没有任何合并提交。--force-with-lease
是一个不错的选择