git - 从另一个分支重新建立一个分支,该分支是从 master 重新建立的

标签 git rebase

我有这个场景:

o---o---o---o---o  master
     \
      o---o---o---o---o  next
                       \
                        o---o---o  topic

如上所述,我在 master 上做了一些提交,我想更新其他两个分支并有:
o---o  master
     \
      o---o---o---o---o  next
                       \
                        o---o---o  topic

我已经做了 git rebase masternext分支,不用考虑 topic现在还没有 next 的基础提交分支。

我现在能做什么?立即重新设置中间分支是错误的吗?我该怎么办?

最佳答案

这个问题有两种相对简单的解决方案。一种是尝试 rebase,这可能会奏效。但如果没有,它仍然很容易。这是第二种更强大的方法,其次是为什么:

git checkout topic; git rebase --onto next next@{1}

发生了什么:rebase 是如何工作的

我认为,如果您以不同的方式绘制图形,则 View 会更清晰。这是原始的,略有重绘:
A---B---C---D---E                   <-- master
     \
      F---G---H---I---J             <-- next
                       \
                        K---L---M   <-- topic

这是您在 git checkout next; git rebase master 之后所拥有的:
A---B---C---D---E                   <-- master
     \           \
     |            F'-G'-H'-I'-J'    <-- next
     \
      F---G---H---I---J             [was "next", but not anymore]
                       \
                        K---L---M   <-- topic

这是因为 rebase 通过复制提交来工作,并进行一些轻微(或重大)更改。新提交 F'是原件F的副本, G'G 的副本, 等等。

如果您现在运行 git checkout topic; git rebase next ,它通常只是有效 - 但并非总是如此。原因是,默认情况下,rebase从单个参数中选择要复制的提交和复制的位置。这有点复杂,所以让我们分两步进行。

当你说 git rebase next在这里,单个参数是 next : 副本将在 next 的提示提交之后进行(现在是 J' )。这部分非常简单:一个标识符,next , 找到提交 --onto附加副本。 (我拼写这个 --onto 而不仅仅是“onto”是有原因的。:-))

被复制的部分更棘手,有两种方式。一、next指定不复制的内容,而当前分支( topic )指定复制的内容。 Git 获取可从 topic 访问的所有提交的列表——这是提交 A , B , F , G , H ,等等一直到 L .然后它排除所有从 next 可达的提交,这是提交 A , B , C , D , E , F' (但不是 F ),G' (但不是 G ),依此类推至 J' .一些排除的提交不在包含列表中并不重要:我们只是排除那些在包含列表中的提交。

不幸的是,这会导致提交 F , G , H , I , 和 J在一堆要复制的提交中。正如您所注意到的,这就是事情出错的地方。但不知何故,大多数时候,这无论如何都有效。它工作时发生了什么?为什么它不总是有效?

答案是 rebase 也排除了“补丁等效提交”。这里的一般想法是,当您重新设置基础时,Git 会扫描“排除”列表以查看其中是否有任何提交与“包含”列表中的提交执行相同的操作。自 F'F 的副本,很可能与 F“相同” ,至少在补丁等效意义上。 G 也是如此对比 G' , H对比 H' , 等等。

只要提交与其重新调整的版本补丁相同,它们就会被排除在外。

如果这是真的——而且通常是这样——你可以简单地git checkout topic; git rebase next它只是有效。当它不是真的时——当一些 F通过 H被复制时被修改了“太多”,他们的副本不再是补丁等效的——它失败了。

作为一种经验法则,1 如果 Git 能够自己完成第一个 rebase ,那么它也能够自己完成第二个 rebase 。主要是当它因冲突而停止并让您手动修复它时,补丁会变成“非补丁等效”。但是处理这个很容易,因为 git rebase有一个更长的形式。

代替:
git rebase next

我们可以写:
git rebase --onto next <something>

根据我们绘制的图形,<something>在这里使用的是任何指定提交 J 的内容。 .也就是说,我们需要找到哪里next曾是。做了什么提交 next指点一下,在我们rebase之前呢?它现在指向 J'但它曾经指向J .我们需要 Git 排除的提交是那些 next用于到达:A , B , F , G , H , I , 和 J .如果 Git 不能自己找到这些,通过补丁等效性,我们可以告诉它。

这仍然留下了如何拼写标识提交 J 的名称的问题。 .一种方法是通过原始哈希 ID,它总是有效,但有点痛苦(查找和复制,尽管剪切和粘贴对于复制哈希非常有效)。但是再看看重绘的图,上面写着 was "next", but not anymore .如果我们能告诉 Git 查找 next 的先前值就好了.

可是等等! Git 有 reflog!我们可以告诉它这样做! next 的先前值拼写为 next@{1} . (每次我们更改标签 next 时,数字都会增加,例如通过向其添加新提交,但我们刚才所做的只是 rebase ,所以它仍然是 next@{1} 。)所以在这里我们可以这样做:
git checkout topic; git rebase --onto next next@{1}

(根据您的 shell,您可能需要在带有大括号的内容周围使用引号,例如 'next@{1}'next@\{1\} 或类似的内容)。

1 有一些异常(exception)。例如,在这种情况下,如果 git diff E F'不会产生与 git diff B F 相同的变更集由于 diff 选择不同的半无关匹配,例如空白行或大括号行,这会导致问题。但是,处理这些问题的答案与处理由于冲突而手动应用的 rebase 的答案相同。

关于git - 从另一个分支重新建立一个分支,该分支是从 master 重新建立的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39594021/

相关文章:

git - 如何在 SourceTree 中正确地 rebase ?

git - `git svn rebase` 与 `git rebase trunk`

git - 了解 Git 以及如何使用 EGit(git eclipse 插件)

git - 如何列出git中的本地提交差异

git - 可用选项卡中缺少 Jenkins 插件

git - 增加 Github Enterprise 中 protected `master` 分支的代码覆盖率门

ruby-on-rails - Rails + git + heroku - git 推送时出错

Git pull --rebase 将空格转换为制表符

git - 如何在交互式 git rebase 期间编辑挂起的命令?

git - 将一个父级的提交重新定位到另一个父级