之前可能已经提出过这个问题,但是我正忙着寻找它。
经验丰富的git
用户经常会说“不要rebase
已公开曝光的资料库!”这是正确的。但我没有看到任何有关影响的信息。我知道这是我们不应该进入的情况,但是当它发生时我们该怎么做?
Original: A--B--C--D--E--F
My Repo: A--B--C--D
\
X--Y--Z
Rebased: A--B
\
L--M--N--O
所以我fetch
,这应该很简单,对吧?然后......然后会发生什么?如果我执行`pull --rebase
,它会在某些时候失败,因为C--D
丢失了。我将遇到哪些错误以及如何解决?
答案 0 :(得分:3)
在git-rebase
联机帮助页中有一个名为“从上游重拍中恢复”的部分可以很好地描述这一部分。
针对您的具体情况,让我们来看看会发生什么。如果你只是提取,那么所做的就是更新你的远程跟踪分支。这仍然可行 - 它会给你一个警告,但是:
From <url>:
+ abc123...def456 master -> origin/master (forced update)
拉取,无论是正常(合并)还是重新定位,都会在内部调用fetch。所以如果你还没有提取,你会看到“强制更新”警告。如果您已经提取,则之前已发出强制更新警告,并且现在没有强制更新警告。如果在它之后有一个大的差异报告作为合并的一部分,您也可能会错过强制更新警告。获取之后,git-pull然后根据请求调用merge或rebase。这就是真的可怕的地方。就Git而言,提交C
和D
现在只是本地提交。它们的出版有一点并不重要。所以Git会像往常一样合并/变换。
合并:
- A - B - L - M - N - O (origin/master)
\ \
C - D - X - Y - Z - Q (master)
rebase:
- A - B - L - M - N - O (origin/master) - C' - D' - X' - Y' - Z' (master)
现在,任何人都可能遇到冲突,如果删除提交C和D会使后来的提交不同。 合并冲突是你注意到的唯一其他错误。但如果它们是孤立的变化,那么一切都会变得“好”。如果用户没有注意到,则可能随后推送此历史记录,从而重新引入已删除的提交!有时这可能是相对温和的。也许L实际上是C,在提交消息中修改了拼写错误,而MNO与DEF相同,只是使用不同的父节点。所以重新引入C和D只会使历史变得混乱。但也许这是非常危险的:也许删除C修复了一些可怕的bug,它刚刚被重新引入。
这就是为什么重写已发布的历史记录是真的坏主意。最好的情况是最终会有一些重复的提交,最糟糕的情况是用户在不知不觉中破坏了所有内容。因此,如果它真的发生了,你要做的第一件事就是告诉每个人并指导他们如何恢复。接下来要警惕地监控随后发布的所有内容,以确保它不会重新引入旧的提交。如果你站在另一边,从公共回购中获取,希望你信任维护者足以永远不会改变稳定的分支,或告诉你他们是否这样做。如果您不信任它们,那么最好先获取merge / rebase,而不是拉动,以便您可以注意到“强制更新”消息,并且非常小心地进行。
如何恢复的细节各不相同;我推荐上述手册页。很难确切地告诉你需要做什么 - 这取决于C是否被重写为L(可能是D进入M)或者它们是完全新的提交,以及是否要重新绑定或合并。如果你只是想要改变,将XYZ重新定位到O就足够了。如果要合并,则将XYZ重命名为B或M,然后合并O。
答案 1 :(得分:3)
你应该这样做(如“How do I recover/resynchronise after someone pushes a rebase or a reset to a published branch?”):
git checkout yourBranch
git branch old-upstreamBranch D # BEFORE fetching!
E--F (origin/upstreamBranch)
/
A--B--C--D (old-upstreamBranch)
\
X--Y--Z (yourBranch)
git fetch
L--M--N--O (origin/upstreamBranch)
/
A--B--C--D (old-upstreamBranch)
\
X--Y--Z (yourBranch)
此时,你不能简单地在重写的upstreamBranch
之上修改你的分支:这将引入C和D,当upstreamBranch
被重写时,它们明确地被保留了。
您需要重新 yourBranch
,而不是之前的提交。
这就是你在获取之前添加old-upstreamBranch
标记的原因:
git rebase --onto origin/upstreamBranch old-upstreamBranch yourBranch
X--Y--Z (yourBranch)
/
L--M--N--O (origin/upstreamBranch)
/
A--B--C--D (old-upstreamBranch)
git branch -D old-upstreamBranch
X--Y--Z (yourBranch)
/
L--M--N--O (origin/upstreamBranch)
/
A--B
但是: With Git 2.0, all you will need to do is :
fork_point=$(git merge-base --fork-point origin/upstreamBranch yourBranch)
# return D
git rebase --onto origin/upstreamBranch $fork_point yourBranch
不再“在抓取之前制作分支!
如果您在 git fetch
之后发现了问题,您仍然可以在重写的上游分支之上很好地修改您的分支。
由于commit ad8261d中的John Keeping (johnkeeping
),git rebase
可以使用相同的新--fork-point
选项。