GIT:我如何重新嵌套嵌套分支?

Mus*_*ore 4 git merge

我的结构看起来像这样 - >

master
  develop 
    project
      <sprint_number>
        <task_number>
Run Code Online (Sandbox Code Playgroud)

我在task_number分支上工作.然后我将任务与sprint分支合并.然后我将sprint与项目分支合并.通过这种方式,项目上的所有提交都是sprint,sprint上的所有提交都是任务.合并到项目分支后,我提交合并请求并在合并到开发之前执行代码审查.

我应该一直在链条上做一次变装吗?例如:

git checkout develop
git rebase master
git checkout project
git rebase develop
git checkout <sprint_number>
git rebase project
git checkout <task_number>
git rebase <sprint_number>
Run Code Online (Sandbox Code Playgroud)

tor*_*rek 8

Git分支名称实际上并不构成嵌套:它们只是指向特定提交的指针.

首先,绘制(部分)提交DAG

像往常一样,我们需要做的是绘制一些提交D irected A循环G raph (DAG)片段并考虑重新定义有意义的情况.所以我们从你的例子开始:

master
  develop 
    project
      <sprint_number>
        <task_number>
Run Code Online (Sandbox Code Playgroud)

并添加一些节点(并给它们单个大写字母而不是它们的"真实名称"哈希,a1cf93a...因为它们太大而且不实用):

A <- B <- C                <-- master
      \
       D <- E              <-- develop
             \
              F <- G       <-- project
                    \
                     H     <-- <sprint_number>
                      \
                       I   <-- <task_number>
Run Code Online (Sandbox Code Playgroud)

(这里的反斜杠应该是左上箭头,但是那些太难以用纯文本绘制).

也就是说,在这种情况下,我们(至少)有三次提交master(在提交之前可能有任意数量的提交A,我们根本没有提取).的尖端master是提交C,它指向回提交B,其指向回A.

我们有两个提交,develop但也没有master:提交E是指向developE指回D,而D指向返回B.提交B,其所有的祖先(沿A以及任何更早),是对双方 master develop.

同时承诺G是最重要的project; G指回到F哪些点E,依此类推.这意味着提交A并且B实际上是在所有三个分支上.但等等,还有更多! H是尖端<sprint_number>H指向G,等等; 并且I是指向<task_number>I指出H.

最后,这意味着提交A并且B(至少)有五个分支(这里显示的是五个),D并且`E至少在四个分支上,依此类推.

决定是否需要和允许变基

在git中,rebasing实际上意味着将提交复制到新的,略有不同/修改的提交.(这可能不是正确的方法.但是,我们稍后会谈到这一点,因为直到你知道更多才会有意义.)

提示master现在是提交C而不是提交B.据推测,早些时候,主人的提示是B,那就是我们提交时D(也许E也是如此).但现在你正在考虑重订develop到的新的提示master.

要实现这一点,您必须复制提交DE新的不同提交.我们称这些副本D'E'.即使没有其他任何改变 - 并且可能其他东西确实发生了变化,特别是在它们之间有什么不同B并且C将进入新的D'- D'原始提交的副本D必须指向提交C而不是提交B.

绘制这个复制阶段(省略了原来的所有内容E),我们得到:

A - B - C             <-- master
     \    \
      \     D' - E'   <-- develop (after rebase)
       \
        D - E         [abandoned]
Run Code Online (Sandbox Code Playgroud)

(我已经简化箭头指向左边的这个时间太长了,现在我们知道,犯点向左).但同时,原来的DE由分行名称不再指向的develop,他们仍然可达一旦我们填写的休息图纸:

A - B - C             <-- master
     \    \
      \     D' - E'   <-- develop (after rebase)
       \
        D-E
           \
            F-G       <-- project
               \
                H     <-- <sprint_number>
                 \
                  I   <-- <task_number>
Run Code Online (Sandbox Code Playgroud)

在这一点上,特别重要的是原始提交D并且E不再开启develop.

rebase是如何工作的

忽略--fork-point(这可以是一个解决方案),该git rebase命令确实需要三个参数,其中一个通常只取自HEAD:

  • 最尖端提交复制(这通常只是"你当前的分支",即,HEAD);
  • 一个说明符限制应该承担复制,即,指定-但间接承诺复制; 和
  • 将添加第一个复制的提交的提交的标识.

后两者通常合并为一个<upstream>参数.与此同时,你首先要做一个git checkout分支来重组,设置第一个参数.举例来说,如果我们决定要变基developmaster:

git checkout develop
git rebase master
Run Code Online (Sandbox Code Playgroud)

在这里,最常见的提交复制是HEAD像往常一样提交,因为它git checkout是最常见的提交develop,并且新副本将在其中生成的起始位置是提示master.Git的开始考虑应对每一个承诺就是上develop(这将是A,B,D,和E),但它在这里告诉避免复制每一个承诺就是对master的,这意味着A,BC.

(等等,什么?我们不应该复制C?但我们不会C在第一时间复制!好吧,没问题,我们就是不会复制它!) 这就是我们如何将两者结合起来一个<upstream>论点.我们希望在之后添加新副本C,同时避免复制C以及从路径返回的所有内容C.

因此,如果我们选择继续前进,这样做git rebase,我们将复制DED'E'与我们制定新的图形片段结束.

这很好develop,但如果我们这样做,现在会发生什么:

git checkout project
git rebase develop
Run Code Online (Sandbox Code Playgroud)

这一次,我们会要求混帐复制一切从尖端可达project-These是G,F,E,D,B,和A(也许更多的东西)-to的已重订基期的尖端develop,即承诺E'.

这是个问题.如果我们幸运的话,它可能是一个自我解决的,因为rebase将检测一些复制提交的情况并避免重新复制它们.也就是说,当git复制D到(另一个)新副本时D'',它可能会检测到D已存在的副本E'.如果它确实检测到它,它将跳过副本.复制EE''它时发生同样的情况:它可能会检测到这不需要,并跳过副本.

另一方面,git的检测器可能被愚弄,它可能会复制D和/或E.我们绝对不希望这样,所以最好不要让git复制它们.

有很多方法可以提出,包括交互式rebase(我们可以编辑pick指令,所以我们可以删除pick提交的两行DE),或者使用以下参数更聪明git rebase:

git checkout project
git rebase --onto develop 'project@{1}'
Run Code Online (Sandbox Code Playgroud)

第二个命令使用reflog历史来告诉git要复制的提交是那些project未包含在上一个提示中的(当前分支)project.也就是说,'project@{1}'解析为原始(未复制)提交的提交ID E.因此,这将复制提交FG,F'以及G'.

(顺便提一下,如果你在带有彩色标记的白板上绘制DAG,你可以使用颜色来表示原始提交及其副本.我发现这比所有D'D''符号更容易阅读.我无法在StackOverflow上绘制它. )

我们可以使用sprint和task重复此过程,使用reflog来识别要遗漏的提交.

从git 1.9开始,git rebase现在已经--fork-point基本上自动化了我们在这里使用reflog做的事情.(在git 2.1中有一个错误修复因为git rebase --fork-point未能发现不需要复制的提交,因此将这个选项限制为2.1或更高版本是明智的.)因此,这可能是一种方法.

最后,在回答这是否是一个好主意的问题之前,我将再说一遍.假设我们开始重新定义任务,而不是重新develop开启master,project开启develop等等.这将让Git拷贝提交给,给,给,等一路下跌到复制到.然后,任务分支将指向新提交,其历史链将返回到.现在,我们需要做的就是通过查找正确的副本,将sprint分支,分支和分支重新指向复制的提交.更新后应指向; 更新后应指向; 并且更新的sprint分支应该指向.DD'EE'FF'II'I'CprojectdevelopdevelopE'projectG'H'

如果有额外的sprint和/或任务分支,他们可能需要复制一些不会被上面复制的提交,所以必须小心使用这个技巧.与往常一样,它将有助于首先绘制DAG.

重新定位是对的吗?

如果你有一个这种复杂的分支结构,那么变基可能是错误的方法.即使不是,它仍然是错误的方法.

请记住,正如我们刚刚看到的那样,变基涉及复制提交,然后移动分支标签以指向新副本而不是原始副本.当你做到这一点,只有一个仓库,你使用,这通常不是个太可怕的混乱,因为你把所有的分支标签和你现在做:你要么有旧的,预拷贝状态,或新的,拷贝后state,你可以忽略所有中间(mid-rebase)状态,除了做所有这些rebase的短暂时期.

但是,如果其他人正在共享此存储库,请考虑您将对他们执行的操作.你做这一切的大规模基础重建之前,他们有什么,他们认为是正确的develop,project冲刺和任务分支指针.他们使用原始(尚未复制)的提交,并根据原始提交进行自己的新提交.

现在你来告诉他们:"哦,嘿,忘记所有那些旧的提交!改用这些全新的闪亮的!" 现在他们必须找到他们所做的所有依赖于提交的东西,并将所有这些更新为依赖于新的提交.

换句话说,他们必须处理"上游变革" - 或者事实上,来自众多上游变革.它通常不是很有趣(虽然相同的--fork-point代码使您可以自动执行此操作,也可以使它们从上游rebase自动恢复).

有一个时间限制--fork-point,因为它使用reflog条目,并且reflog条目到期.如果你没有重新配置东西,git默认会在30天后使关键的reflog条目到期,所以如果你这样做,其他人都有大约一个月的时间从中恢复.