2017-02-11 17 views
3

我有这样的事情:修改基地分支和变基所有的孩子一下子

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

而且我想获得这样的事:

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

我想执行一个命令,立即重新绑定所有分支,所以我不必逐一重新绑定它们。这可能吗?

回答

4

简答:不,这是不可能的......但你可以最大限度地减少你必须做的工作量。最小化工作的长答案如下。

理解的关键这是Git的分支 -the名branch_1branch_2branch_3在你的例子,仅仅是标识符“点”的一个特定承诺。这是构成实际分支的承诺本身。有关详细信息,请参阅What exactly do we mean by "branch"?同时,git rebase所做的是复制一些提交,新副本通常在新基础上创建(因此为“重新基础”)。

在您的具体情况下,只有一个提交链需要复制。这是B--C--D链。如果我们去掉所有的标签(分支名称),我们可以绘制图形片段是这样的:

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

你的任务是B--C--D复制到B'--C'--D',这就像B通过DE后跟从来代替A。我把它们放在顶部,这样我们就可以保持原有B--C--D链中的图片也:

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

一旦你所做的副本,你就可以改变标签使它们指向副本,而不是指向原件;现在我们需要移动D'起来,这样我们可以指出branch_2C'

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

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

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

    git checkout branch_3 && git rebase branch_1 
    

    但实际上你可以使用一个Git的命令git rebase这样做有这样做的初始git checkout选项:

    git rebase branch_1 branch_3 
    
  2. git branch -f,重新点branch_2

    我们知道(我们认为我们表明,我们可以做的branch_3git rebase复制所有提交小心图形绘制),其branch_2指向提交“退一步”,从提交到branch_3点。也就是说,在开始时,branch_2名称提交Cbranch_3名称提交D。因此,一旦我们全部完成,branch_2需要命名承诺C'

    由于一个步骤从旧branch_3尖端背面,它必须是一个步骤中从新branch_3之后的尖端背面。 所以,现在我们已经完成了rebase并有B'-C'-D'链,我们干脆直接Git的移动标签branch_2点从退一万步的地方branch_3点:

    git branch -f branch_2 branch_3~1 
    

因此,对于本情况下,至少需要两个Git命令(如果您更喜欢单独的git checkout,则需要三个)。

请注意,有些情况下需要更多或不同的命令,即使我们只移动/复制两个分支名称。举例来说,如果我们开始:

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

,想要复制所有的G-H-(K;I),我们不能用一个git rebase命令做到这一点。我们可以做的是第一次复制br2br3,复制四个提交中的三个;然后使用git rebase --onto <target> <upstream> <branch>与剩余的分支复制剩下的一个提交。

(事实上,虽然git rebase --onto是最普遍的形式。我们总是能够做到整个作业只用了一系列git rebase --onto <target> <upstream> <branch>命令,每分支之一。这是因为git rebase确实件事:(1)复制一些可能为空的提交集合;(2)移动分支标签a la git branch -f复制的集合由<upstream>..HEAD的结果确定 - 请参阅gitrevisions和下面的脚注1并且复制位置由--onto设置;以及分支的目的地是HEAD,在复制完成后,如果复制集为空,则HEAD右移--onto目标,因此请参阅MS像一个简单的(ISH)脚本可以做所有的工作......不过见注1)


然而这假定的是,实际上git rebase卷起复制所有的提交。 (这种假设的安全级别取决于所讨论的基准变化很大)。

实际上,尽管初始复制提交集是通过运行git rev-list --no-merges <upstream>..HEAD确定的 - 或者它的等效,真的 - 初始集合是立即的通过计算git patch-id的“复制”和“不需要复制因为现在将在上游”范围中的每个提交而进一步减少。也就是说,代替<upstream>..HEAD,重新分配代码使用<upstream>...HEAD加上--right-only --cherry-pick。所以我们省略不仅合并提交,还提交已经上游的提交。

我们可以自己编写一个脚本,以便我们可以找到每个分支名称在我们想要分支的分支集合中的相对位置。 (前一段时间我做了大部分的实验)。但是还有一个问题:在采摘过程中,有些提交可能会因为冲突解决而变空,而你会这样做。这更改其余复制的提交的相对位置。

这到底意味着什么,除非git rebase增加了一点,为了记录哪个提交映射到哪个新提交以及哪个提交被完全删除,不可能做出完全正确的,完全可靠的多重底板脚本。我认为可以在不修改Git本身的情况下使用 reflog充分关闭:如果有人启动了这种复杂的rebase,只会出错,但在其中间会执行一些操作,然后继续执行基地。

对于某些形式的git rebase而言并非如此,对于其他人而言,这是字面上的。生成列表的代码取决于您使用--interactive和/或--keep-empty和/或--preserve-merges。一个non-interactive git am based rebase用途:

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通过哪个命令被使用(摘樱桃或格式补丁)到git rev-list代码中传递,使之能得到提交ID的右手边列表中,与“预先挑选的樱桃”从对称差异中删除。

交互式底池相当复杂(!)。

+0

为了澄清:是你包括在实验结束部分脚本你提到,还是从Git来源? (如果是这样,它会很好地提及/链接它们。) – mkrieger1

+1

@ mkrieger1:末尾的脚本片段超出了'git-rebase-am.sh'。我将添加一个链接。向ptyo ixfes倾斜。 :-) – torek

0

为了至少找出哪些分支你将拥有初始重订后更新,您可以运行

$ git branch --contains <commit A>