繁体   English   中英

理解“git pull --rebase”vs“git rebase”

[英]Understanding “git pull --rebase” vs “git rebase”

根据我对git pull --rebase origin master理解,它应该相当于运行以下命令:

(from branch master):  $ git fetch origin
(from branch master):  $ git rebase origin/master

我似乎找到了一些不按预期工作的情况。 在我的工作区中,我有以下设置:

  • 分支origin/master参考远程origin上的分支master
  • branch master设置为跟踪origin/master ,并且通过多次提交落后于 master。
  • 分支feature设置为跟踪本地分支master ,并通过多次提交在master 之前

有时,我会通过运行以下一系列步骤来丢失提交

(from branch master):  $ git pull --rebase
(from branch master):  $ git checkout feature
(from branch feature): $ git pull --rebase

在这一点上,我正在进行的一些提交feature现在已经丢失了。 现在,如果我重置我的位置,而是执行以下操作:

(from branch feature): $ git reset --hard HEAD@{2} # rewind to before second git pull
(from branch feature): $ git rebase master

提交已正确应用,我的新提交feature仍然存在。 这似乎直接与我对git pull如何工作的理解相矛盾,除非git fetch . 做了比我想象的更奇怪的事情。

不幸的是,对于所有提交,这不是100%可重复的。 但是,当它确实适用于提交时,它每次都有效。

注意:我的git pull --rebase在这里应该被理解为--rebase=preserve ,如果这很重要的话。 我的~/.gitconfig有以下~/.gitconfig

[pull]
    rebase = preserve

(编辑,2016年11月30日:另请参阅这个答案 为什么git rebase会丢弃我的提交? 。现在几乎可以肯定它是由于fork-point选项。)

手动和之间的一些区别pull基于git rebase (现在少在2.7比有在比罗马的Git版本--fork-point在选项git merge-base )。 而且,我怀疑你的自动保留合并可能会涉及到。 这有点难以确定,但是你的本地分支跟随你的另一个本地分支这一事实正在变得非常具有启发性。 同时,最近在C中重写了旧的git pull脚本,因此很难看到它的作用(尽管你可以将环境变量GIT_TRACE设置为1以使git在内部运行时显示命令)。

在任何情况下,这里有两三个关键项目(取决于你如何计算和拆分它们,我会把它变成3):

  • git pull运行git fetch ,然后根据指令运行git mergegit rebase ,但是当它运行git rebase它使用新的fork-point机器“从上游rebase恢复”。

  • git rebase在没有参数的情况下运行时,它有一个特殊的情况来调用fork-point机制。 使用参数运行时,除非使用--fork-point显式请求,否则将禁用fork-point机制。

  • git rebase被指示保留合并时,它使用交互式rebase代码(非交互式)。 我不确定这在这里真的很重要(因此上面可能会涉及)。 通常它会使合并变平,只有交互式rebase脚本才能保存它们(这段代码实际上重新进行了合并,因为没有其他方法可以处理它们)。

这里最重要的项目(肯定)是叉点代码。 此代码使用reflog来处理通过绘制提交图的一部分而最佳显示的案例。

在正常情况下(没有叉点需要)rebase案例你有这样的事情:

... - A - B - C - D - E   <-- origin/foo
            \
              I - J - K   <-- foo

其中AB是您启动分支时的提交(因此B是合并基础), CE是您通过git fetch从远程git fetch新提交,而IK是您自己的提交。 底垫代码副本I通过K ,所述第一副本附加到E ,第二到拷贝-OF- I ,第三到拷贝-OF- J

无论如何 Git计算出 - 或者习惯 - 使用git rev-list origin/foo..foo进行复制,即使用当前分支( foo )的名称来查找K并向后工作,以及其名称上游( origin/foo )找到E并向后工作。 向后行军在合并基础处停止,在本例中为B ,复制的结果如下所示:

... - A - B - C - D - E   <-- origin/foo
           \            \
            \             I' - J' - K'   <-- foo
             \
              I - J - K   [foo@{1}: reflog for foo]

当这里的上游origin/foo本身重新定位时,会出现这种方法的问题。 比方说,比如,关于origin有人力推动,使得B是由新副本替换B'有不同的提交措辞(也许不同的树为好,但我们希望,没有什么会影响我们的I -through- K )。 起点现在看起来像这样:

          B' - C - D - E    <-- origin/foo
        /
... - A - B   <-- [origin/foo@{n}]
            \
              I - J - K   <-- foo

使用git rev-list origin/foo..foo ,我们选择要复制的提交BIJK ,并尝试像往常一样在E之后粘贴它们; 但我们不想复制B因为它真的来自origin并且已被其自己的副本B'取代。

fork point code的作用是查看origin的reflog,看看B是否可以在某个时间到达。 也就是说,它不仅检查origin/master (找到E并扫描回B'然后是A ),还检查origin/master@{1} (可能直接指向B ,取决于你运行git fetch频率) , origin/master@{2} ,依此类推。 任何来自任何 origin/master@{n} foo提交都包括在内,以便在图中找到最低公共祖先节点时考虑(即,它们都被视为选项,成为git merge-base打印出来)。

(值得注意的是这里存在的缺陷:这种自动化叉点检测只能找到维护reflog条目时可以访问的提交,在这种情况下默认为30天。但是,这与您的问题不是特别相关。)


在您的情况下,您有三个分支名称(因此涉及三个reflog):

  • origin/master ,由git fetch更新(分支master git pull第一步)
  • master ,由你(通过正常提交)和git rebasegit pull第二步)更新,以及
  • feature ,由你(通过正常提交)和git rebase (你的第二个 git pull第二步)更新:你从你自己“获取”,一个无操作,然后在master上的rebase feature )。

两个rebase都使用--preserve-merges --onto new-tip fork-point (因此是非交互式交互模式)和--onto new-tip fork-point ,其中通过运行git merge-base --fork-point upstream-name HEAD找到fork-point commit ID git merge-base --fork-point upstream-name HEAD 第一个rebase的upstream-nameorigin/master (well, refs/remotes/origin/master ),第二个rebase的upstream-namemasterrefs/heads/master )。

应该都是Just Work。 如果整个过程开始时的提交图与您所描述的类似:

... - A - B   <-- master, origin/master
            \
              I - J - K   <-- feature

然后第一次fetch引入一些提交并使origin/master指向新的提示:

              C - D - E   <-- origin/master
            /
... - A - B   <-- master, origin/master@{1}
            \
              I - J - K   <-- feature

然后第一个rebase找不到任何要复制的东西( masterB的合并库 - B = fork-point(master,origin / master) - 只是B所以没有什么可复制的),给出:

              C - D - E   <-- master, origin/master
            /
... - A - B   <-- master@{1}, origin/master@{1}
            \
              I - J - K   <-- feature

第二次获取是来自你自己和完全没有操作/跳过,留下这作为第二个rebase的输入。 --onto目标是master ,它是提交EHEADfeature )和master的fork-point也提交B ,像往常一样将提交IK复制到E之后。

如果某些提交被删除,在这个过程中出现问题,但我看不清楚是什么。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM