【问题标题】:How-to git backport (rebase/cherry-pick) an already merged branch如何 git backport (rebase/cherry-pick) 一个已经合并的分支
【发布时间】:2013-05-17 09:52:42
【问题描述】:

在我们的 Git 流程中,“master”是当前发布周期的 topic 和 fix 分支的集成分支,但我们还维护一个“稳定”分支,我们必须小心地向后移植一些已经在 master 上成功测试的修复.

所有的困难是分支已经被合并回“master”(否则使用 rebase --onto 真的很容易)

  • 我们不想以其他方式更改流程,因为 a) 我们不想修复“稳定”分支中的所有内容,并且 b) 我们有时必须对“稳定”分支进行一些更改我们不想合并到“master”中。
  • 显然,我们无法将修复合并到“稳定”分支中,因为这会向后移植许多不需要的功能。

我描述的初始情况图表:

          I--J (stable)
         /
        /
       /
- A - B - C - D - E - F - G  (master) 
              \      /
               X -- Y (fix/123)

我们想要达到的那种情况的图表:

          I--J (stable)
         /    \
        /      X'- Y' (fix/123-stable)
       /
- A - B - C - D - E - F - G  (master) 
              \      /
               X -- Y (fix/123)

更复杂的情况是可能的,例如多次合并以完成修复:

- A - B - C - D - E - F - G - H (master) 
               \     /       /
                X - Y ----- Z (fix/123)

但是我们不允许合并到一个修复分支中,所以我们永远不会有这样的东西:

- A - B - C - D - E - F - G (master) 
               \   \     /
                X - Y - Z (fix/123)

为了实现这一点,我们可以挑选或变基修复分支:

1) 樱桃采摘(典型的是How do I backport a commit in git?):

git checkout -b fix/123-stable stable
git cherry-pick X Y

这看起来很容易,但在处理现实生活中的例子时却不是这样;忘记某些提交或选择错误的提交总是有风险!

2) 变基 --onto (https://www.kernel.org/pub/software/scm/git/docs/git-rebase.html) :

2.a) “不工作”的方式:

git rebase --onto stable master fix/123

这没有任何作用,因为 fix/123 已经合并到 master ! 2.b)“比樱桃采摘好不了多少”的方式:

git rebase --onto stable D fix/123

这仍然有点冒险,因为您需要采用 D 的 SHA(例如,而不是 X)。

2.c)“使用临时起始参考”方式:

git tag begin D
git rebase --onto stable begin fix/123
git tag -d begin

这改善了以前的情况,因为标签更容易做到或在图形工具中绘制它,但仍然需要大量的手动工作。

3.d) "reset hard master before merge"(到第一个分支点) 嗯,似乎很难描述和做。

所以,我正在寻找的是一种 git portable(没有隐含的 bash/grep/cut/sed)方式;

1) 列出已经合并回“master”的分支上的所有提交(这里是 X 和 Y,在“多合并”的情况下还有 Z),以便轻松挑选它们

2) 获取已经合并回“master”的分支的第一个分支点的提交

2.a) 这不能通过“git merge-base”命令完成,因为合并已经完成(甚至多次)

2.b) 我在这里Finding a branch point with Git? 找到了以下我稍微调整的 bash 命令:

git rev-list --boundary --date-order --reverse fix/123..master | grep -m 1 - | cut -c2-

但他的命令不是 git 简单或可移植的命令(即在没有 Bash 或 Cygwin 工具的情况下无法工作)

【问题讨论】:

    标签: git branch rebase cherry-pick


    【解决方案1】:

    为了记录,这是我最终使用的两个解决方案,基于指向answer of lindes on "Finding a branch point with Git"answer of Craig Otis here,使用Bash别名“.gitconfig”文件(在Linux Ubuntu 12.10下测试)

    初始状态,分支“fix/123”已经合并回“master”

            I--J (stable)
           /
    - A - B - C - D - E - F - G  (master) 
                   \     /
                    X - Y (fix/123)
    

    1) 要将分支“fix/123”从“master”重新定位到“stable”(对于大多数阅读本文的人来说,这是一个通用答案):

    将以下 Bash 别名添加到您的“.gitconfig”文件中:

    [alias]
        oldest-ancestor = !bash -c 'diff -u <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne \"s/^ //p\" | head -1' -
        rebase-onto     = !bash -c 'git rebase --onto $1 `git oldest-ancestor $2 $3` $3' -
    

    然后使用命令行:

    git rebase-onto stable master fix/123
    

    你在这里:

              I--J (stable)
             /    \
            /      X'- Y' (fix/123)
           /
    - A - B - C - D - E - F - G  (master) 
                   \     /
                    X - Y
    

    2)要从“master”开始重新设置分支“fix/123”,在“stable”上创建一个新分支“fix/123-stable”(这是我更具体的答案我打算使用)。

    将以下 Bash 别名添加到您的“.gitconfig”文件中:

    [alias]
        oldest-ancestor = !bash -c 'diff -u <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne \"s/^ //p\" | head -1' -
        rebase-onto     = !bash -c 'git branch $4 $2 && git rebase --onto $3 `git oldest-ancestor $1 $4` $4' -
    

    然后使用命令行:

    git rebase-onto master fix/123 stable fix/123-stable
    

    你在这里:

              I--J (stable)
             /    \
            /      X'- Y' (fix/123-stable)
           /
    - A - B - C - D - E - F - G  (master) 
                   \     /
                    X - Y (fix/123)
    

    【讨论】:

    • 由于您的回答是基于 Craig 的工作,我认为他也值得一票。
    • 运行git rebase-onto 命令时你在哪里?
    【解决方案2】:

    我对 Git 还很陌生,所以请原谅我对您的情况可能造成的任何误解。

    使用此代码sn-p:

    diff -u <(git rev-list --first-parent fix/123) <(git rev-list --first-parent master) | sed -ne 's/^ //p' | head -1
    

    来自这个答案:@lindes answer to "Finding a branch point with Git?"

    如果你有一棵这样的树:

    那么上面用some-fix代替fix/123的命令的结果将是:

    f1efa4a9c029281a22d4fa8dd6607c523e7191f5

    创建初始修复分支时的提交。

    您还可以快速运行merge-base 以确定some-fix 的结束位置:

    $ git merge-base master some-fix
    1b9ebb7157a958e9adc3b8eda9bf4175cd821c4b
    

    然后,使用精选的修订范围,您可以将这些更改拉入:

    $ git cherry-pick f1efa4a..1b9ebb7
    

    你最终得到:

    其中包含来自初始修复分支的额外提交。您可能希望执行额外的checkout -b 来创建您的fix/123-stable 分支,而不是仅仅将它们附加到stable,但方法应该相同。

    另请注意,我引用的答案提到将那个冗长乏味的diff 命令安装为一个名为oldest-ancestor 的别名,这应该可以为您节省大量时间。

    【讨论】:

    • 实际上,这并不完全正确 - 因为一旦您的 fix/123 分支合并回 mastercherry 就不包含提交。我还在研究。
    • 是的,我要告诉你这是我的问题的重点(所有困难在于分支已经合并回“master”)。但是感谢您的尝试!
    • 非常感谢您抽出宝贵的时间,诚然,这工作得很好,但这太复杂了,而且在没有 Bash 或 Cygwin 工具的 Windows 机器上无法工作。我正在寻找的是一个 git 命令(或一些命令)来在标准工作流程中记录这一点,目前这似乎不可行。
    • 对。我认为如果不将一些别名或 shell 脚本放在一起,您将无法轻松完成此操作。尤其是不使用常见的 Unix 工具,如 diffgrep
    猜你喜欢
    • 2012-09-19
    • 2012-12-02
    • 2021-03-28
    • 2018-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多