我的结构看起来像这样->
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 E
是develop
的尖端,并且E
指向D
,而D
指向B
。提交B
及其所有祖先(A
和更早的版本)都在master
和develop
上。同时commit
G
是project
的技巧; G
指向F
,指向E
,依此类推。这意味着提交A
和B
实际上在所有三个分支上。但是,等等,还有更多! H
是<sprint_number>
的尖端,H
指向G
,依此类推; I
是<task_number>
的尖端,I
指向H
。最后,这意味着提交
A
和B
在(至少)五个分支上(此处显示的五个),而D
和`E在至少四个分支上,依此类推。确定是否需要重新定基
在git中,变基实际上意味着将提交复制到新的,稍有不同/已修改的提交。 (这可能不是正确的方法。不过,稍后再讲,因为除非您了解更多,否则它才有意义。)
现在,
master
的提示是提交C
而不是提交B
。大概在更早的时候,master的尖端是B
,那是当我们提交D
的时候(也可能是E
)。但是现在,您正在考虑将develop
重新引入master
的新提示。为此,您必须将提交
D
和E
复制到新的不同提交中。我们将这些副本称为D'
和E'
。即使没有其他更改,也可能有其他更改,特别是B
和C
之间的区别都将放入新的D'
中,原始提交D'
的副本D
必须指向提交C
而不是提交B
。仅绘制此复制阶段(将所有悬挂在原始
E
之外的东西都留了下来),我们得到:A - B - C <-- master
\ \
\ D' - E' <-- develop (after rebase)
\
D - E [abandoned]
(由于我们知道提交指向左,所以这次我也简化了向左箭头。)但是,虽然原来的
D
和E
不再由分支名称develop
指向,但它们填写完其余的图形后,仍然可以访问:A - B - C <-- master
\ \
\ D' - E' <-- develop (after rebase)
\
D-E
\
F-G <-- project
\
H <-- <sprint_number>
\
I <-- <task_number>
此时特别重要的是,原始提交
D
和E
不再位于develop
上。变基如何工作
忽略
--fork-point
(可以在此处找到解决方案),git rebase
命令实际上接受三个参数,其中一个通常是从HEAD
提取的:最尖端的提交副本(通常只是“您当前的分支”,即
HEAD
);一个说明符,它限制要提交的内容,即(但间接地)指定不进行复制;和
第一次复制的提交将添加到的提交的标识。
后两个通常合并为一个
<upstream>
参数。同时,您首先对分支的git checkout
进行重新设置,以设置第一个参数。例如,如果我们决定将develop
重新设置为master
:git checkout develop
git rebase master
此处,复制的最尖端提交是通常的
HEAD
提交,由于git checkout
是develop
的最尖端提交,因此将要生长新副本的起始位置是尖端的master
。 Git首先考虑应对develop
上的每个提交(应该是A
,B
,D
和E
),但是在这里告诉我们避免复制每个master
上的提交,表示A
,B
和C
。(等等,什么?我们不应该复制
C
?但是我们首先不打算复制C
!那么,那没问题,我们就是不复制它!)可以将这两件事组合成一个<upstream>
参数。我们希望在C
之后添加新副本,同时避免复制C
以及从C
返回的路径中的所有内容。因此,如果选择继续进行
git rebase
操作,则将D
和E
复制到D'
和E'
,最后得到绘制的新图形片段。这对于
develop
很好,但是如果我们这样做,现在会发生什么:git checkout project
git rebase develop
这次,我们将要求git复制从
project
提示可访问的所有内容-这些是G
,F
,E
,D
,B
和A
(也许还有一些更多)-到已经重新建立基准的develop
的尖端,即,提交E'
。这是个问题。如果幸运的话,这可能是一种自我解决的方法,因为rebase会检测到某些复制的提交案例,并避免重新复制它们。也就是说,当git将
D
复制到另一个新副本D''
时,它可能会检测到D
已经存在于E'
中。如果确实检测到此错误,它将跳过该副本。将E
复制到E''
时也会发生相同的情况:它可能会检测到不需要,并跳过该副本。另一方面,git的检测器可能会被欺骗,并且可能会复制
D
和/或E
。我们绝对不想这么做,因此最好避免要求git复制它们。可以通过多种方式询问,包括交互式变基(我们可以在其中编辑
pick
指令,因此我们可以删除提交pick
和D
的两条E
行),或者变得更加聪明。带有git rebase
的参数:git checkout project
git rebase --onto develop 'project@{1}'
第二个命令使用reflog历史记录来告诉git,要复制的提交是
project
(当前分支)上的提交,而这些副本不包含在project
的上一个提示中。也就是说,'project@{1}'
解析为原始(未复制)提交E
的提交ID。因此,这只会将提交F
和G
复制到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
上的master
和project
上的develop
,依此类推。这将告诉git将提交D
复制到D'
,将E
复制到E'
,将F
复制到F'
,依此类推一直复制到将I
复制到I'
。然后,任务分支将指向新的提交I'
,其历史链可追溯到C
。现在,我们需要做的就是通过找到正确的副本,将sprint分支,project
分支和develop
分支指向复制的提交。更新后的develop
应指向E'
;更新后的project
应指向G'
;并且更新的sprint分支应指向H'
。如果还有其他的sprint和/或任务分支,则可能需要复制一些上面的内容无法复制的提交,因此,必须谨慎使用此技巧。与往常一样,它将有助于首先绘制DAG。
变基对吗?
如果您拥有如此复杂的分支结构,则重新建立基础可能是错误的方法。即使没有,也可能是错误的方法。
请记住,正如我们刚刚看到的那样,重新定级涉及复制提交,然后移动分支标签以指向新副本,而不是原始副本。当您仅使用自己的存储库来执行此操作时,通常不会太令人困惑,因为您移动了所有分支标签,现在就完成了:您具有旧的,复制前的状态,或者具有新的复制后的状态状态,您可以忽略所有中间(中间基准)状态,只是短暂的所有这些基准操作除外。
但是,如果其他人正在共享此存储库,请考虑您将对他们执行的操作。在进行所有这些大型基础调整之前,他们已经拥有了正确的
develop
,project
,sprint和任务分支指针。他们正在使用原始(尚未复制)的提交,并根据这些原始提交进行自己的新提交。现在,您来告诉他们:“哦,嘿,忘掉所有那些旧的提交!改为使用这些全新的闪亮的提交!”现在他们必须去找到他们所做的一切都依赖于旧提交,并更新所有这些依赖于新提交。
换句话说,它们必须处理“上游资源库”,或者实际上是来自众多上游资源库。通常没有什么好玩的(尽管使用相同的
--fork-point
代码可以实现此自动化,也可以使他们实现从上游资源库中恢复的自动化)。--fork-point
有时间限制,因为它使用引用日志条目,并且引用日志条目到期。如果您尚未重新配置,则git默认在30天后过期关键reflog条目,因此,如果这样做,则其他所有人大约有一个月的时间可以从中恢复。
关于git - GIT:如何重新设置嵌套分支的基准?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36292272/