您如何处理已经重新定位的公共存储库?

时间:2011-12-09 16:58:28

标签: git version-control

之前可能已经提出过这个问题,但是我正忙着寻找它。

经验丰富的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丢失了。我将遇到哪些错误以及如何解决?

2 个答案:

答案 0 :(得分:3)

git-rebase联机帮助页中有一个名为“从上游重拍中恢复”的部分可以很好地描述这一部分。

撷取

针对您的具体情况,让我们来看看会发生什么。如果你只是提取,那么所做的就是更新你的远程跟踪分支。这仍然可行 - 它会给你一个警告,但是:

From <url>:
 + abc123...def456 master    -> origin/master  (forced update)

拉取,无论是正常(合并)还是重新定位,都会在内部调用fetch。所以如果你还没有提取,你会看到“强制更新”警告。如果您已经提取,则之前已发出强制更新警告,并且现在没有强制更新警告。如果在它之后有一个大的差异报告作为合并的一部分,您也可能会错过强制更新警告。获取之后,git-pull然后根据请求调用merge或rebase。这就是真的可怕的地方。就Git而言,提交CD现在只是本地提交。它们的出版有一点并不重要。所以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选项。