【问题标题】:How to rebase a branch after its base branch was rebased on master [duplicate]如何在其基础分支基于主分支后重新设置分支[重复]
【发布时间】:2020-09-04 07:31:59
【问题描述】:

我有一个分支A,在其中我做了一些更改a,然后我从A创建了一个新的分支B继续进行一些更改b,这样总的变化在B 中是a, b。最后,其他人将他们的更改 c 合并到 master 中,因此工作树现在如下所示:

- c (master)
 \ 
  - a (A) - b (B)

然后我将A 重新定位为master,将更改从a 更改为a' 并强制将它们推送到分支A,因此工作树如下所示:

- c (master) - a' (A)
 \
  - a - b (B)

问题

我怎样才能将B 变基为A,这样只有新的更改b 会被更改,然后工作树看起来像这样:

- c (master) - a' (A) - b' (B)

如果我在B 上尝试git rebase origin/A,那么它会尝试解决提交ac, a',这是我不想要的,因为我担心它会创建一个新的提交@987654343 @ 然后工作树将如下所示:

- c (master) - a' (A) - a'' - b' (B)

现在有没有办法只改变b? (我曾尝试用谷歌搜索这个,但我没有找到任何具体涵盖此案例的内容)

编辑:

我尝试在B 上压缩a'b,但情况更糟,因为现在b 的所有更改都被注册为冲突。为了说明,假设我:

  1. a 中添加了新行l1
  2. b 中将该行更改为l2
  3. 在另一个提交 c 中未触及此行。
  4. 现在是冲突了,因为l1是在a'中添加的,但l2是在被撤销的提交a'&b中添加的。

【问题讨论】:

    标签: git branch rebase branching-and-merging


    【解决方案1】:
    git rebase --onto A a B
    

    --onto A 使用A 作为新的基础。 a B 获取一组提交,例如 git log a..B,在您的情况下仅包括 b。这组提交被应用到新的基础上。

    您可以随时使用interactive rebase 处理简单或复杂的情况。

    git rebase -i A
    

    在编辑页面,将a这一行的前导pick修改为drop,或者干脆去掉整行。保存并退出。

    如果git rebase 不起作用,您也可以尝试git cherry-pick 仅应用您想要的提交,然后将B 重置为新的头部。

    git checkout -b tmp A
    git cherry-pick b
    git checkout B
    git branch B_bak
    git reset tmp --hard
    git branch -D tmp
    

    如果出现任何问题,您可以运行git checkout B && git reset B_bak --hard 来恢复B

    【讨论】:

    • git rebase --onto A a B 中的a 应该设置什么?我尝试了提交哈希的开头,但效果不佳。不幸的是,我不记得问题出在哪里(尝试了 5 次以上,每次都以不同的方式失败)。
    • @kajacx 您的图中a 的SHA1 - a - b (B)
    • 谢谢,经过大量尝试和阅读,我发现git rebase --onto A a 是我需要的(在B 上签出时似乎与git rebase --onto A a B 相同)。我仍然不记得为什么第一次失败(可能我粘贴了错误的提交哈希)。
    【解决方案2】:

    如何将B 重新设置为A,以便只更改新的更改b

    你可以这样做:

    git rebase A B
    

    这与说“切换到分支B 并将其重新设置在A 之上”相同。 Git 不会重新应用提交 a,因为它可以确定 a' 已经引入了相同的更改。

    补丁等效

    当两个不同的提交(即具有不同 SHA-1 哈希的提交)引入相同的更改集时,它们被称为补丁等效

    您可以使用git log--cherry-pick 选项来确定两个分支之间哪些提交是等效的补丁:

    --cherry-pick
    当提交集受限于对称差异时,忽略与“另一边”上的另一个提交引入相同更改的任何提交。

    在你的情况下,如果你这样做:

    git log --oneline --cherry-pick --left-right A...B
    

    您会看到提交 a'a 被排除在输出之外,因为它们是补丁等效的。1

    当您将B 重新设置在A 之上时,Git cherry-picks 来自B 的提交并将它们一次应用到A 之上。当它到达一个引入与现有更改相同的更改的提交时,它会简单地跳过它并移至下一个。


    1... 表示法(也称为triple dot)选择可以从两个引用之一中访问的提交,但不能同时从两个引用中访问。这对于选择两个不同分支中的“新”提交很有用,从它们的共同父级开始。

    【讨论】:

    • git rebase B A 似乎不起作用,不仅它会将我当前的分支切换到A,而且我什至看不到正确的更改。
    • 对不起,我不小心颠倒了参数:$。我更新了我的答案。
    • 谢谢,我尝试切换参数,但这似乎并没有什么,它可能是 VSCode 中的错误或其他什么。最后我硬着头皮rebase到master,再次解决了ac之间的冲突。
    • 好的,我明白它现在是如何工作的了。 Git 不会重新应用提交 a,因为它可以确定 a' 已经引入了相同的更改。 这将不起作用,因为存在冲突,我不仅必须再次解决冲突,而且我必须像以前一样完全解决它,否则提交将不一样。
    【解决方案3】:

    告诉 rebase 寻找新基地的尖端所在的位置并从那里开始:

    git rebase --fork-point A
    

    如果你当前分支的上游被设置,那和--fork-point选项都是默认的,所以你的命令是

    git rebase
    

    【讨论】:

      【解决方案4】:

      事后找到了这篇文章:RECOVERING FROM UPSTREAM REBASE,不过还没试过。

      编辑:经过大量研究,正确答案是:

      git checkout B
      git rebase --onto A a
      

      请参阅I can't understand the behaviour of git rebase --onto 了解说明。

      【讨论】:

        猜你喜欢
        • 2016-05-06
        • 2014-03-20
        • 2022-11-09
        • 2021-07-23
        • 2019-08-23
        • 2015-10-14
        • 2012-06-03
        • 1970-01-01
        • 2020-10-11
        相关资源
        最近更新 更多