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

时间:2016-03-29 18:27:56

标签: git merge

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

master
  develop 
    project
      <sprint_number>
        <task_number>

我在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>

3 个答案:

答案 0 :(得分:7)

Git分支名称实际上并不存在于任何意义上:它们只是指向特定提交的指针。

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

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

master
  develop 
    project
      <sprint_number>
        <task_number>

并添加一些节点(并为它们提供单个大写字母而不是它们的&#34;真实姓名&#34;类似a1cf93a...的哈希值,因为它们太大而且不实用):

A <- B <- C                <-- master
      \
       D <- E              <-- develop
             \
              F <- G       <-- project
                    \
                     H     <-- <sprint_number>
                      \
                       I   <-- <task_number>

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

也就是说,在这种情况下,我们在master上进行了(至少)三次提交(在提交之前可能有任意数量的提交A我们根本没有绘制)。 master的提示是提交C,它指向提交B,返回A

我们在develop上有两个提交但不在master上:提交Edevelop的提示,E指向D 1}},而D指向B。提交B及其所有祖先(A以及更早的内容)位于 master {{1 }}

同时提交developG的提示; project指回G,指向F,依此类推。这意味着提交EA实际上是在所有三个分支上。但是等等,还有更多! B的提示是H<sprint_number>的提示,指向H,依此类推;并且GI<task_number>点的提示,返回I

最后,这意味着提交HA在(至少)五个分支(此处显示的五个分支)上,B和`E至少在四个分支,等等。

决定是否需要和允许变基

在git中, rebasing 实际上意味着将提交复制到新的,略有不同/修改的提交。 (这可能不是正确的方法。但是,我们稍后会谈到这一点,因为在你了解更多信息之前它不会有意义。)

D的提示现在提交master而不是提交C。据推测,早些时候,主人的提示是B,那时我们提交了B(也许D)。但现在您正在考虑将E重新定位到develop的新提示上。

要实现此目的,您必须复制masterD提交到新的不同提交。我们会将这些副本称为ED'。即使没有其他任何改变 - 并且其他内容可能会发生变化,特别是E'B之间的任何不同之处都会进入新的C - 副本{{原始提交D'的1}}必须指向提交D'而不是提交D

绘制只是这个副本阶段(遗漏原始C的所有内容)我们得到:

B

(这次我也简化了左箭头,现在我们知道提交指向左侧了。)但原来的EA - B - C <-- master \ \ \ D' - E' <-- develop (after rebase) \ D - E [abandoned] 不再指向分支名称D,一旦我们填写了图纸的其余部分,它们仍然可以到达

E

此时特别重要的是原始提交developA - B - C <-- master \ \ \ D' - E' <-- develop (after rebase) \ D-E \ F-G <-- project \ H <-- <sprint_number> \ I <-- <task_number> *不再在D上。

rebase如何运作

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

  • 最常见的提交复制(通常只是#34;您当前的分支&#34;,即git rebase);
  • 提交复制的限制的说明符,即指定 - 但间接提交而不是进行复制;和
  • 将添加第一个复制的提交的提交的标识。

后两者通常合并为一个HEAD参数。与此同时,您首先要对分支HEAD进行rebase,以设置第一个参数。例如,如果我们决定将<upstream>重新定位到git checkout

develop

这里最常见的提交复制提交是master提交,因为git checkout develop git rebase master HEAD提交最多的提交,以及它的起始位置新副本将成长为git checkout的提示。 Git首先考虑处理develop上的每个提交(可能是masterdevelopAB ),但它告诉避免复制D上的每个提交,这意味着Emaster和{{1} }。

(等等,什么?我们不应该复制A?但我们首先要复制B!好吧,没问题那么,我们只是不会复制它!)这就是我们如何将这两个事物合并为一个C参数。我们希望在C之后添加新副本,同时避免复制C以及从<upstream>返回的路径中的所有内容。

因此,如果我们选择继续执行此操作C,我们会将CC复制到git rebaseD并结束我们绘制了新的图片片段。

这对于E来说非常棒,但如果我们这样做会发生什么:

D'

这一次,我们要求git复制从E'提示可以访问的所有内容 - 这些是developgit checkout project git rebase develop project,{{1 }},GF(可能更多) - 已经重新定位E的提示,即提交D

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

另一方面,git的检测器可能会被愚弄,它可能会复制D和/或E'。我们绝对不想要那样,所以最好避免让git复制它们。

有很多方法可以提出,包括交互式rebase(我们可以在其中编辑E指令,因此我们可以删除提交E''的两个DE),或者对pick的参数更加聪明:

pick

第二个命令使用reflog历史来告诉git要复制的提交是D(当前分支)上的提交,这些提交未包含在{em>之前的提示中{ {1}}。也就是说,E解析为原始(未复制)提交git rebase的提交ID。因此,这只会将提交git checkout project git rebase --onto develop 'project@{1}' project复制到project'project@{1}'

(顺便提一下,如果您在带有彩色标记的白板上绘制DAG,您可以使用颜色来表示原始提交及其副本。我发现这比所有E和{{1}更容易阅读我只是无法在StackOverflow上绘制它。)

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

从git 1.9开始,F现在有G,这实际上使我们在这里使用reflog自动化了。 (git 2.1中有一个错误修复,因为F'未能发现不需要复制的提交,因此将此选项限制为2.1或更高版本是明智的。因此,这是一种方法。

最后,在回答这是否是一个好主意的问题之前,我还要再说一遍。而不是在G'D' D''上重新git rebase,等等,假设我们通过重新定位任务来启动。这会告诉git将提交--fork-point复制到git rebase --fork-point,将develop复制到master,将project复制到develop,依此类推将D复制到D'。然后,任务分支将指向新提交E,其历史链回到E'。现在我们需要做的就是通过查找正确的副本,在复制的提交中重新指向sprint分支,F分支和F'分支。更新后的I应指向I';更新后的I'应指向C;并且更新后的sprint分支应指向project

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

正在重新定位吗?

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

请记住,正如我们刚刚看到的那样,变基涉及复制提交,然后移动分支标签以指向新副本而不是原始副本。当您使用仅使用的存储库执行此操作时,它通常不会太令人困惑,因为您移动了所有分支标签并且现在已经完成:您要么使用旧的,要么-copy状态或新的复制后状态,除了执行所有这些rebase的短暂时间外,您可以忽略所有中间(中间rebase)状态。

如果其他人正在共享此存储库,请考虑您将为他们做什么 。在你完成所有这些大规模的变基之前,他们有思想是正确的developdevelop,冲刺和任务分支指针。他们正在使用原始(尚未复制)的提交,并根据这些原始提交进行自己的新提交。

现在你过来告诉他们:&#34;哦,嘿,忘了所有那些旧的提交!请改用这些全新的闪亮的!&#34;现在他们必须找到依赖于提交的他们所做的一切,并将所有这些更新为依赖于新的提交。

换句话说,他们必须处理一个&#34;上游变种&#34; - 或者实际上,来自众多上游变种。它通常不是很有趣(尽管相同的E'代码使您可以自动执行此操作,也可以使它们从上游rebase自动恢复)。

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

答案 1 :(得分:0)

  

通过这种方式,项目的所有提交都是sprint,sprint上的所有提交都是任务

你不需要这样做。只需使用您创建的pull请求,因为所有代码将合并到最终开发中。

只要您打开拉取请求,您也可以删除当地的分支机构,只保留最新的分支机构,以备将来更改时使用。

答案 2 :(得分:0)

在git中没有嵌套分支这样的东西。如果你有一个名为foo的分支,另一个名为foo / bar的分支,它们只是两个不同的分支。这些分支之间没有任何关系 - 以对待任何两个独立分支的方式对待它们。