修改基础分支并一次重新设置所有子级的基础 [英] Modify base branch and rebase all children at once

查看:63
本文介绍了修改基础分支并一次重新设置所有子级的基础的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这样的东西:

A //branch 1
 \
  B - C //branch 2
       \
        D //branch 3

我想得到这样的东西:

 A - E //branch_1
      \
       B' - C' //branch_2
             \
              D' //branch_3

我想执行一个命令并立即为所有分支重新设置基准,因此我不必一个接一个地对其进行重新基准化.这可能吗?

I would like to perform a command and rebase all branches at once, so I don't have to rebase them one by one. Is this possible?

推荐答案

简短的回答:不,这不可能...但是您可以尽量减少您要做的工作.接下来是关于最小化工作的长答案.

Short answer: no, it's not possible ... but you can minimize the amount of work you have to do. Long answer on minimizing work follows.

了解这一点的关键是Git的分支名称(在您的示例中为名称branch_1branch_2branch_3)仅仅是指向"一个特定提交的标识符.正是提交本身构成了实际的分支.有关详细信息,请参见分支"到底是什么意思?同时,git rebase的作用是 copy 进行一些提交,新副本通常在新的基础上创建(因此称为"re-base").

The key to understanding this is that Git's branch names—the names branch_1, branch_2, and branch_3 in your example—are merely identifiers that "point to" one specific commit. It's the commits themselves that form the actual branches. For details, see What exactly do we mean by "branch"? Meanwhile, what git rebase does is to copy some commits, with the new copies normally being made on a new base (hence "re-base").

在您的特定情况下,只有一个提交链需要复制.这就是B--C--D链.如果我们剥离所有标签(分支名称),则可以通过以下方式绘制图形片段:

In your particular case, there's only one chain of commits that requires copying. That's the B--C--D chain. If we strip off all the labels (branch names) we can draw the graph fragment this way:

A--E
 \
  B--C--D

您的任务是将B--C--D复制到B'--C'--D',就像BD一样,但是要放在E之后,而不是放在A之后.我将它们放在顶部,以便我们也可以在图片中保留原始的B--C--D链:

Your task is to copy B--C--D to B'--C'--D', which are like B through D but come after E instead of coming after A. I'll put them on top so that we can keep the original B--C--D chain in the picture too:

     B'-C'-D'
    /
A--E
 \
  B--C--D

制作完副本后,就可以更改标签,以使它们指向副本,而不是指向原始文档.现在我们需要向上移动D'以便将branch_2指向C':

Once you've made the copies, you can then change the labels so that they point to the copies, rather than pointing to the originals; and now we need to move D' up so that we can point branch_2 to C':

          D'   <-- branch_3
         /
     B'-C'     <-- branch_2
    /
A--E           <-- branch_1

这至少需要两个Git命令来完成:

This takes a minimum of two Git commands to accomplish:

  1. git rebase,将B-C-D复制到B'-C'-D'并移动branch_3指向D'.通常这是两个命令:

  1. git rebase, to copy B-C-D to B'-C'-D' and move branch_3 to point to D'. Normally this would be two commands:

git checkout branch_3 && git rebase branch_1

,但是实际上您可以使用一个Git命令执行此操作,因为git rebase可以选择执行初始的git checkout:

but you can actually do this with one Git command as git rebase has the option of doing the initial git checkout:

git rebase branch_1 branch_3

  • git branch -f,以重新指向branch_2.

  • git branch -f, to re-point branch_2.

    我们知道(从我们仔细的图形图中可以看出,我们可以对branch_3进行单个git rebase复制所有提交),branch_2指向从提交到提交退后一步"的提交. branch_3指向哪个.也就是说,开始时,branch_2名称提交C,而branch_3名称提交D.因此,完成所有操作后,branch_2需要将提交命名为C'.

    We know (from our careful graph drawing that showed us that we could do a single git rebase of branch_3 to copy all the commits) that branch_2 points to the commit "one step back" from the commit to which branch_3 points. That is, at the start, branch_2 names commit C and branch_3 names commit D. Hence, once we're all done, branch_2 needs to name commit C'.

    由于它是 从旧branch_3的尖端向后退一步,因此它必须又从新branch_3的尖端向后退一步. 1 因此,既然我们已经完成了rebase并拥有了B'-C'-D'链,我们只需指示Git将标签branch_2移动到从branch_3指向的任何位置后退一步:

    Since it was one step back from the tip of the old branch_3, it must be one step back from the tip of the new branch_3 afterward.1 So now that we have done the rebase and have the B'-C'-D' chain, we simply direct Git to move the label branch_2 to point one step back from wherever branch_3 points:

    git branch -f branch_2 branch_3~1
    

  • 因此,在这种情况下,它至少需要两个Git命令(如果您希望使用单独的git checkout,则需要三个).

    Thus, for this case, it takes at least two Git commands (three if you prefer a separate git checkout).

    请注意,在某些情况下,即使我们仅移动/复制两个分支名称,也需要使用更多或不同的命令.例如,如果我们从以下内容开始:

    Note that there are cases where more or different commands are required, even if we are moving / copying just two branch names. For instance, if we started with:

    F--J        <-- br1
     \
      G--H--K   <-- br2
          \
           I    <-- br3
    

    并想复制所有G-H-(K;I),我们不能用一个git rebase命令来完成.我们 所要做的是首先重新设置br2br3的基准,复制四个提交中的三个;然后将git rebase --onto <target> <upstream> <branch>与其余分支一起使用,以复制剩下的一个提交.

    and wanted to copy all of G-H-(K;I), we cannot do this with one git rebase command. What we can do is rebase either br2 or br3 first, copying three of the four commits; then use git rebase --onto <target> <upstream> <branch> with the remaining branch, to copy the one remaining commit.

    (实际上,git rebase --onto是最通用的形式:我们总是可以只用一系列git rebase --onto <target> <upstream> <branch>命令来完成整个工作,每个分支一个.这是因为git rebase确实是两件事:(1)复制一些提交集,可能为空;(2)移动分支标签la git branch -f.复制的集是根据<upstream>..HEAD的结果确定的,请参见 gitrevisions 和下面的脚注1-复制位置为由--onto设置;分支的目的地是完成复制后HEAD结束的任何地方;如果要复制的集为空,则HEAD恰好在--onto目标处结束. 就像一个简单的(ish)脚本可以完成所有工作……但请参见脚注1.)

    (In fact, though, git rebase --onto is the most general form: we can always do the entire job with just a series of git rebase --onto <target> <upstream> <branch> commands, one per branch. This is because git rebase really does two things: (1) copy some set of commits, possibly empty; (2) move a branch label a la git branch -f. The copied set is determined from the result of <upstream>..HEAD—see gitrevisions and footnote 1 below—and with the copy location being set by --onto; and the branch's destination is wherever HEAD winds up after doing the copying. If the to-copy set is empty, HEAD winds up right at the --onto target. So it seems like a simple(ish) script could do all the work ... but see footnote 1.)

    1 但是,这假设git rebase实际上结束了复制 all 提交. (此假设的安全级别根据所讨论的基准而有很大差异.)

    1This assumes, however, that git rebase actually winds up copying all the commits. (The safety level of this assumption varies a lot depending on the rebase in question.)

    实际上,虽然要复制的初始提交集是通过运行git rev-list --no-merges <upstream>..HEAD或它的等效值来确定的,但是通过在两个复制"和不需要复制,因为现在将是上游"范围.也就是说,rebase代码将<upstream>...HEAD--right-only --cherry-pick结合使用,而不是<upstream>..HEAD. 2 因此,我们不仅忽略合并提交,而且还忽略已经在上游的提交.

    In fact, while the initial set of commits to copy is determined by running git rev-list --no-merges <upstream>..HEAD—or its equivalent, really—that initial set is immediately further whittled-down by computing the git patch-id for each commit in both the "to copy" and "don't need to copy because now will be upstream" ranges. That is, instead of <upstream>..HEAD, the rebase code uses <upstream>...HEAD combined with --right-only --cherry-pick.2 So we omit not just merge commits, but also commits that are already upstream.

    我们可以编写一个自己执行的脚本,以便可以在希望重新建立基础的分支集中找到每个分支名称的相对位置. (我之前做过大部分实验).但是还有另一个问题:在挑选樱桃的过程中,由于冲突解决,有些提交可能会变为空,您可以git rebase --skip进行处理.这会更改其余复制的提交的相对位置.

    We could write a script that does this ourselves, so that we can locate the relative position of each branch-name in the set of branches we wish to rebase. (I did most of this as an experiment some time ago.) But there is another problem: during the cherry-picking process, it's possible that some commits will become empty due to conflict resolution, and you will git rebase --skip them. This changes the relative position of the remaining copied commits.

    这最终意味着,除非对git rebase进行一点扩展,否则要记录哪些提交映射到哪些新提交以及哪些提交被完全删除,就不可能制作出完全正确,完全可靠的多重新设置脚本.我认为可以使用HEAD reflog而不修改Git本身就足够接近:如果有人启动这种复杂的变基,这只会出错,但是在中间执行一些git reset --soft和喜欢,然后恢复变基.

    What this means in the end is that unless git rebase is augmented a bit, to record which commits map to which new commits and which commits were dropped entirely, it's impossible to make a completely-correct, completely-reliable multi-rebase script. I think one could get sufficiently close, without modifying Git itself, using the HEAD reflog: this would only go awry if someone starts this kind of complex rebase, but then in the middle of it, does some git reset --softs and the like and then resumes the rebase.

    2 对于某些形式的git rebase而言,这确实是正确的,而对于另一些形式而言,则不是.生成列表的代码取决于您是否使用--interactive和/或--keep-empty和/或--preserve-merges. 基于非交互式git am的rebase 用途:

    2This is literally true for some forms of git rebase and not for others. The code to generate the list varies depends on whether you're using --interactive and/or --keep-empty and/or --preserve-merges. A non-interactive git am based rebase uses:

    if test -n "$keep_empty"
    then
        # we have to do this the hard way.  git format-patch completely squashes
        # empty commits and even if it didn't the format doesn't really lend
        # itself well to recording empty patches.  fortunately, cherry-pick
        # makes this easy
        git cherry-pick ${gpg_sign_opt:+"$gpg_sign_opt"} --allow-empty \
            --right-only "$revisions" \
            ${restrict_revision+^$restrict_revision}
        ret=$?
    else
        rm -f "$GIT_DIR/rebased-patches"
    
        git format-patch -k --stdout --full-index --cherry-pick --right-only \
            --src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
            "$revisions" ${restrict_revision+^$restrict_revision} \
            >"$GIT_DIR/rebased-patches"
        ret=$?
    
    [snip]
    
        git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \
            ${gpg_sign_opt:+"$gpg_sign_opt"} <"$GIT_DIR/rebased-patches"
        ret=$?
    
    [snip]
    

    --cherry-pick --right-only通过使用哪个命令(cherry-pick或format-patch)传递到git rev-list代码,以便它可以使用"pre-采摘的樱桃",从对称差异中删除.

    The --cherry-pick --right-only are passed through whichever command is used (cherry-pick or format-patch) to the git rev-list code, so that it can get the right-hand-side list of commit IDs, with "pre-picked cherries" removed, from the symmetric difference.

    交互式rebase要复杂得多(!).

    Interactive rebase is considerably more complicated (!).

    这篇关于修改基础分支并一次重新设置所有子级的基础的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆