【问题标题】:git pull --rebase fork infomationgit pull --rebase 分叉信息
【发布时间】:2016-09-08 12:00:29
【问题描述】:

我们使用git pull --rebase 而不是git pull。我们在检索服务器上不再显示的分叉信息时遇到问题。

假设一个“主”分支,人们从中推拉。 另外,假设人们修改了不同的文件并且 git 没有引发冲突。

这是我的本地分支及其在拉取之前的上游的图像

      A---B---C local
     /
D---E---F---G server

如果我们使用git pull(标准的),就会发生这种情况

      A---B---C 
     /         \
D---E---F---G---M---

但由于我们使用的是git pull --rebase,如果我没记错的话会发生这种情况:

D---E---F---G---A'--B'--C' 

现在我们知道其中一项测试在 C' 中不再有效。我们确信它在 C 和 G 中都有效。即使没有冲突,这也可能发生,如果有两个冗余代码块,并且第一个人删除了一个块,而另一个人删除了另一个块。

通常要检查的修改是提交 A',B',C,F,G 中的修改,因为分叉发生在 E。但是由于我们使用git pull --rebase,我们无法知道分叉何时发生发生了,所以我们无法识别集合 A',B',C,F,G。

1) 知道问题现在只出现在 C' 是否有办法识别 E 并因此识别集合 A',B',C,F,G ?

2) 如果 1) 的答案是“否”,那么应该检查哪些提交?

【问题讨论】:

    标签: git git-rebase git-pull


    【解决方案1】:

    TL;DR:您可以使用 git merge-base 和 reflog 信息找到提交 E


    您的绘图是正确的,因为 rebase 所做的是 copy 提交(对新的、略有不同的提交)。

    原始提交会在存储库中保留一段时间(默认情况下通常为 30 天):它们只是被放置在视野之外。有可能(使用 reflogs 和/或 ORIG_HEAD 找到原始的 C 并从那里找到合并基础提交 E

    如果这种事情经常发生,我建议不要直接使用git pull。事实上,我建议不要git pull 开始,尽管使用这两个独立的底层步骤需要更多的工作和更多的输入。这里的想法是让 Git 在做什么很明显,从而更容易插入有用的自动化。 (之后,根据您自己的自动化,您可以返回使用git pull,有或没有--rebase,如果您发现运行一个命令而不是两个命令更方便。)

    git pull 所做的是运行git fetch,然后运行git mergegit rebase。虽然pull 有一些额外的花里胡哨,但这确实是其行动的核心。单独执行这两个操作的好处是它们可以让您检查fetch 之后和merge-或-rebase 之前的状态。

    (这对于merge 的必要性要小得多,因为“合并前”状态很明显:只需剥离合并提交,您就有了合并前状态。rebase 更有用,因为“之前”合并”状态让您可以根据需要分别查看分叉开发的双方,然后再允许 Git 做疯狂而疯狂的事情,制作提交副本。)

    让我们在这里重现您的“之前”状态,然后展示 rebase 的真正作用。这是运行git fetch 之后、运行git rebase 之前的“之前”状态:

          A---B---C   <-- HEAD -> branch
         /
    D---E---F---G     <-- origin/branch
    

    我添加了名称 HEAD 指向您当前的分支,并将分支重命名为更典型。

    git rebase 步骤之后,我们有这个:

          A---B---C            <-- ORIG_HEAD, branch@{1}
         /
    D---E---F---G              <-- origin/branch
                 \
                  A'--B'--C'   <-- HEAD -> branch
    

    通常,查看者(git loggitk --all 等)会跳过特殊引用,例如 ORIG_HEADCHERRY_PICK_HEAD 等。他们还会忽略 reflogs (branch@{1}) 的内容。使用git refloggit log -g,或在命令行中添加ORIG_HEAD 之类的名称,您可以指示这些查看者向您展示原件。

    如果您将这两个步骤分开,则可以轻松保存任何特定提交的信息或添加标记(例如临时git tags)。比如git fetch之后,可以标记commit E:找到branchorigin/branch1合并基,并标记:

    $ git tag temp-marker $(git merge-base branch origin/branch)
    

    (假设 sh 或 bash 或类似的;请注意,为了概括这一点,您可以使用 HEAD@{u} 代替 branchorigin/branch)。

    如果您不想混淆他人(“为什么在我们所有的存储库中都有一个名为 temp-marker 的标签?!”),请确保删除 temp-marker 标签(或至少避免推送它),一次你已经完成了。

    即使您确实使用 git pull --rebase 将它们组合在一起,之后您也可以使用 reflog 和/或 ORIG_HEAD 来做同样的事情。 (这里的主要问题是 reflog 中的数字——上面的 branch@{1}ORIG_HEAD 本身都会更新。如果你做另一个 rebase,ORIG_HEAD 现在跟踪 that rebase 而不是一个你仍然关心的。同时如果你对branch进行任何更新,数字(@{1})会增加:现在你需要branch@{2},然后是branch@{3},等等。(你也可以使用@987654378 @,它检查记录到 branch 的每个更改的日期戳,并使用来自“昨天”的最新日期戳。有关详细信息,请参阅 the git reflog documentation 及其返回到 git log 的链接以及 gitrevisions .)

    换句话说,一旦你在这个特定的pickle中,检查branch@{1}是否是正确的参考(或者ORIG_HEAD仍然是好的)。如果没有,请使用git reflog 或类似名称来查找正确的参考。无论如何,一旦你找到它:

    git merge-base branch@{1} origin/branch
    

    将定位提交E


    1这假设只有一个合并基础提交。对于我们在这里考虑的所有情况,这应该是正确的。只有在存在“交叉合并”(必须手工制作)的情况下,您才会获得多个合并基础:

    ...--o--*---o--o   <-- branch1
             \ /
              X
             / \
    ...--o--*---o--o   <-- branch2
    

    这里,branch1branch2 之间没有单一的合并基础:* 提交都是合适的合并基础。这不会发生在具有单个上游分支的正常工作流程中(因为您无法签出 origin/branch 并对其进行提交)。您必须自己做一些疯狂而疯狂的事情(使用临时分支、两次显式合并和至少一次推送)才能实现。

    【讨论】:

    • 非常感谢这个有启发性的回答。我希望我能给你投票 100 次。
    猜你喜欢
    • 2021-11-17
    • 2017-12-07
    • 1970-01-01
    • 2021-08-16
    • 2013-03-14
    • 2014-02-17
    • 2012-06-10
    • 2018-08-02
    • 2011-01-13
    相关资源
    最近更新 更多