git - GIT:如何重新设置嵌套分支的基准?

标签 git merge

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

master
  develop 
    project
      <sprint_number>
        <task_number>


我在task_number分支上工作。然后,我将任务与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>

最佳答案

Git分支名称实际上并没有任何意义的嵌套:它们只是指向特定提交的指针。

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

与往常一样,我们在这里需要做的是绘制一些提交的有向无环图(DAG)片段,并考虑重新基准化有意义的情况。因此,我们从您的示例开始:


master
  develop 
    project
      <sprint_number>
        <task_number>



并添加一些节点(并给它们一个大写字母,而不是像a1cf93a...这样的“真实名称”散列,因为这些散列太大且难以处理):

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


(此处的反斜杠应为上下箭头,但很难用纯文本绘制)。

也就是说,在这种情况下,我们(至少)对master进行了3次提交(在提交A之前可能有许多我们根本没有画过的提交)。 master的尖端是提交C,它指向提交B,后者指向A

我们在develop上有两个提交,但在master上也没有:commit Edevelop的尖端,并且E指向D,而D指向B。提交B及其所有祖先(A和更早的版本)都在masterdevelop上。

同时commit Gproject的技巧; G指向F,指向E,依此类推。这意味着提交AB实际上在所有三个分支上。但是,等等,还有更多! H<sprint_number>的尖端,H指向G,依此类推; I<task_number>的尖端,I指向H

最后,这意味着提交AB在(至少)五个分支上(此处显示的五个),而D和`E在至少四个分支上,依此类推。

确定是否需要重新定基

在git中,变基实际上意味着将提交复制到新的,稍有不同/已修改的提交。 (这可能不是正确的方法。不过,稍后再讲,因为除非您了解更多,否则它才有意义。)

现在,master的提示是提交C而不是提交B。大概在更早的时候,master的尖端是B,那是当我们提交D的时候(也可能是E)。但是现在,您正在考虑将develop重新引入master的新提示。

为此,您必须将提交DE复制到新的不同提交中。我们将这些副本称为D'E'。即使没有其他更改,也可能有其他更改,特别是BC之间的区别都将放入新的D'中,原始提交D'的副本D必须指向提交C而不是提交B

仅绘制此复制阶段(将所有悬挂在原始E之外的东西都留了下来),我们得到:

A - B - C             <-- master
     \    \
      \     D' - E'   <-- develop (after rebase)
       \
        D - E         [abandoned]


(由于我们知道提交指向左,所以这次我也简化了向左箭头。)但是,虽然原来的DE不再由分支名称develop指向,但它们填写完其余的图形后,仍然可以访问:

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


此时特别重要的是,原始提交DE不再位于develop上。

变基如何工作

忽略--fork-point(可以在此处找到解决方案),git rebase命令实际上接受三个参数,其中一个通常是从HEAD提取的:


最尖端的提交副本(通常只是“您当前的分支”,即HEAD);
一个说明符,它限制要提交的内容,即(但间接地)指定不进行复制;和
第一次复制的提交将添加到的提交的标识。


后两个通常合并为一个<upstream>参数。同时,您首先对分支的git checkout进行重新设置,以设置第一个参数。例如,如果我们决定将develop重新设置为master

git checkout develop
git rebase master


此处,复制的最尖端提交是通常的HEAD提交,由于git checkoutdevelop的最尖端提交,因此将要生长新副本的起始位置是尖端的master。 Git首先考虑应对develop上的每个提交(应该是ABDE),但是在这里告诉我们避免复制每个master上的提交,表示ABC

(等等,什么?我们不应该复制C?但是我们首先不打算复制C!那么,那没问题,我们就是不复制它!)可以将这两件事组合成一个<upstream>参数。我们希望在C之后添加新副本,同时避免复制C以及从C返回的路径中的所有内容。

因此,如果选择继续进行git rebase操作,则将DE复制到D'E',最后得到绘制的新图形片段。

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

git checkout project
git rebase develop


这次,我们将要求git复制从project提示可访问的所有内容-这些是GFEDBA(也许还有一些更多)-到已经重新建立基准的develop的尖端,即,提交E'

这是个问题。如果幸运的话,这可能是一种自我解决的方法,因为rebase会检测到某些复制的提交案例,并避免重新复制它们。也就是说,当git将D复制到另一个新副本D''时,它可能会检测到D已经存在于E'中。如果确实检测到此错误,它将跳过该副本。将E复制到E''时也会发生相同的情况:它可能会检测到不需要,并跳过该副本。

另一方面,git的检测器可能会被欺骗,并且可能会复制D和/或E。我们绝对不想这么做,因此最好避免要求git复制它们。

可以通过多种方式询问,包括交互式变基(我们可以在其中编辑pick指令,因此我们可以删除提交pickD的两条E行),或者变得更加聪明。带有git rebase的参数:

git checkout project
git rebase --onto develop 'project@{1}'


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

(顺便说一句,如果您在带有彩色标记的白板上绘制DAG,则可以使用颜色来表示原始提交及其副本。我发现这比所有D'D''标记更易于阅读。我可以将其绘制在StackOverflow上。)

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

从git 1.9开始,git rebase现在有了--fork-point,它实际上使我们在这里使用引用日志自动化。 (git 2.1中有一个错误修复程序,用于git rebase --fork-point无法发现不需要复制的提交,因此将此选项限制为2.1或更高版本是明智的。)因此,这可能是一种方法去做这个。

最后,在再次讨论这是否是个好主意之前,我将再作一个说明。假设我们从重新定基任务开始,而不是重新定标develop上的masterproject上的develop,依此类推。这将告诉git将提交D复制到D',将E复制到E',将F复制到F',依此类推一直复制到将I复制到I'。然后,任务分支将指向新的提交I',其历史链可追溯到C。现在,我们需要做的就是通过找到正确的副本,将sprint分支,project分支和develop分支指向复制的提交。更新后的develop应指向E';更新后的project应指向G';并且更新的sprint分支应指向H'

如果还有其他的sprint和/或任务分支,则可能需要复制一些上面的内容无法复制的提交,因此,必须谨慎使用此技巧。与往常一样,它将有助于首先绘制DAG。

变基对吗?

如果您拥有如此复杂的分支结构,则重新建立基础可能是错误的方法。即使没有,也可能是错误的方法。

请记住,正如我们刚刚看到的那样,重新定级涉及复制提交,然后移动分支标签以指向新副本,而不是原始副本。当您仅使用自己的存储库来执行此操作时,通常不会太令人困惑,因为您移动了所有分支标签,现在就完成了:您具有旧的,复制前的状态,或者具有新的复制后的状态状态,您可以忽略所有中间(中间基准)状态,只是短暂的所有这些基准操作除外。

但是,如果其他人正在共享此存储库,请考虑您将对他们执行的操作。在进行所有这些大型基础调整之前,他们已经拥有了正确的developproject,sprint和任务分支指针。他们正在使用原始(尚未复制)的提交,并根据这些原始提交进行自己的新提交。

现在,您来告诉他们:“哦,嘿,忘掉所有那些旧的提交!改为使用这些全新的闪亮的提交!”现在他们必须去找到他们所做的一切都依赖于旧提交,并更新所有这些依赖于新提交。

换句话说,它们必须处理“上游资源库”,或者实际上是来自众多上游资源库。通常没有什么好玩的(尽管使用相同的--fork-point代码可以实现此自动化,也可以使他们实现从上游资源库中恢复的自动化)。

--fork-point有时间限制,因为它使用引用日志条目,并且引用日志条目到期。如果您尚未重新配置,则git默认在30天后过期关键reflog条目,因此,如果这样做,则其他所有人大约有一个月的时间可以从中恢复。

关于git - GIT:如何重新设置嵌套分支的基准?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36292272/

相关文章:

php - Wordpress网站团队开发

git - .git/index.lock 文件实际上做了什么?

git - 将两个独立的功能分支 merge 到同一个主分支中

mercurial - 在 Mercurial 中将一个分支的选定修订 merge 到另一个分支

java - 合并两个流

c++ - 合并两个排序数组的最坏情况下的比较次数?

Git 将文件保存在存储库中但停止跟踪本地更改

git - 提交可以是 git 中它自己的祖先吗?

java - 在 git bisect 期间,仅运行失败的测试是否安全?或者我们应该运行所有测试?

merge - Gitlab 将 "merge request"与提交合并