本文经原地址 http://blogs.collab.net/subversion/where-did-that-mergeinfo-come-from 许可转载。已删除或更新了失效链接。
作者:Paul Burba
发布 2009-11-19
合并跟踪中一个令人困惑的领域是如何设置 svn:mergeinfo 属性。在许多常见用例中,只设置了合并目标上的合并信息。但是,在许多情况下,会在目标下的合并信息(称为“子树合并信息”)中创建或更新。这些情况经常让用户想知道合并过程中是否出了问题,以及是否应该提交这些子树合并信息更改。由于给定子树可能除了 svn:mergeinfo 属性本身之外没有任何更改,因此这种困惑更加严重。
这篇文章描述了在合并期间创建或更新子树合并信息的几种常见情况。
到目前为止,子树合并信息更改的最常见原因是,您的工作副本合并目标在合并之前已经存在子树合并信息。
例如,我们有一个从主干创建的分支 B1.0,它有一些子树合并信息
1.6.6>svn propget svn:mergeinfo branches/B1.0 -vR
branches/B1.0/A/D/H/psi 上的属性
svn:mergeinfo
/trunk/A/D/H/psi:4-10
我们将修订版 12 从主干合并到 B1.0,合并输出显示添加了一个新文件。
1.6.6>svn merge ^/trunk branches/B1.0 -c12
— 将 r12 合并到 'branches/B1.0'
A branches/B1.0/A/C/nu
但是,当我们检查工作副本的状态时,我们看到了一些属性更改。
1.6.6>svn status
M branches/B1.0
A + branches/B1.0/A/C/nu
M branches/B1.0/A/D/H/psi
检查 B1.0 本身的差异,我们看到添加了 mergeinfo,它描述了我们刚刚执行的合并,即来自主干的 r12。这很有道理,因为这正是我们刚刚做的!
1.6.6>svn diff –depth empty branches/B1.0
branches/B1.0 上的属性更改
___________________________________________________________________
添加:svn:mergeinfo
合并 /trunk:r12
但是 A/D/H/psi 上的属性更改是怎么回事呢?
1.6.6>svn diff branches/B1.0/A/D/H/psi
branches/B1.0/A/D/H/psi 上的属性更改
___________________________________________________________________
修改:svn:mergeinfo
合并 /trunk/A/D/H/psi:r12
哦,这是一个 mergeinfo 更改。但是等等,我们恰好知道 r12 没有影响 psi,我们检查日志以确认这一点。
1.6.6>svn log -v -r12
————————————————————————
r12 | pburba | 2009-11-17 18:21:47 -0500 (Tue, 17 Nov 2009) | 1 行
更改路径
A /trunk/A/C/nu.c
那么为什么 psi 上的 mergeinfo 会发生变化呢?简而言之,对于所有低于 1.6.x 版本的 Subversion,子树 mergeinfo 始终会更新以描述合并,无论子树是否受到合并的影响。这样做是为了支持更轻松的 mergeinfo 省略*,以及在单个合并范围和多个单独合并(总计相同)之间实现一致的 mergeinfo(例如,svn merge ^/trunk branch -r 100:103 或 svn merge ^/trunk branch -c101、svn merge ^/trunk branch -c102 和 svn merge ^/trunk branch -c103 将产生相同的 mergeinfo)。
早些时候,我们在开发社区意识到这种行为造成的混乱比其带来的微不足道的益处更多。当然,只有受特定合并影响的子树才应该更新其 mergeinfo。在这点上几乎没有异议,从编码的角度来看,更改几乎是可笑地简单。不幸的是,进行此更改对后续合并产生了一些意想不到的后果。我在这里不会详细说明,但其中一个后果是合并性能可能大幅下降。
好消息是,最终我们能够保留(在许多情况下甚至提高)合并性能,同时停止记录对不受合并影响的子树的 mergeinfo 更改。坏消息是这些更改非常广泛,不会移植到 1.5.x 或 1.6.x 版本,而只会出现在 1.7 版本中。
同时,如果您想知道是否应该提交这些子树合并信息更改,您应该这样做。如果您选择在提交合并之前恢复它们,您将无法再使用合并 -reintegrate 选项,更糟糕的是,对于长期分支,您将在后续合并中遇到性能下降,这很容易达到无法忍受的程度。
svn:mergeinfo 属性是一个版本化的属性,就像任何其他版本化的属性一样,合并应用的差异可能包括对该属性的更改或添加。
例如,我们可能有一个分支工作副本,它根本没有合并信息
1.6.6>svn propget svn:mergeinfo branches/B3.0 -vR
我们从主干中挑选一个单独的修订版
1.6.6>svn merge ^/trunk branches/B3.0 -c21
— 将 r21 合并到 'branches/B3.0'
UU branches/B3.0/A/D/gamma
注意到第二列中的 'U' 吗?属性更改是 r21 的一部分,我们可以在检查工作副本的状态时看到这一点
1.6.6>svn status
M branches/B3.0
MM branches/B3.0/A/D/gamma
鉴于这篇博文的主题,传入的属性更改是针对 svn:mergeinfo 属性的,这一点不足为奇
1.6.6>svn propget svn:mergeinfo branches/B3.0 -vR
'branches/B3.0' 上的属性
svn:mergeinfo
/trunk:21
'branches/B3.0/A/D/gamma' 上的属性
svn:mergeinfo
/branches/B1.0/A/D/gamma:20
/trunk/A/D/gamma:21
如果我们检查合并源的差异,我们可以看到在 r21 中向 trunk/A/D/gamma 添加了一个 svn:mergeinfo 属性
1.6.6>svn diff -r20:21 ^/trunk
索引:A/D/gamma
===================================================================
— A/D/gamma (修订版 20)
+++ A/D/gamma (修订版 21)
@@ -1 +1 @@
-新的 gamma 文件
+nc
对 A/D/gamma 的属性更改
___________________________________________________________________
添加:svn:mergeinfo
合并 /branches/B1.0/A/D/gamma:r20
这种情况可能以多种方式发生,但通常涉及子树合并(即针对分支根目录下某个目标的合并),这些合并后来作为整个分支合并(即针对分支根目录的合并)合并到其他分支。
其余情况都有一个共同点,记录子树合并信息是因为合并目标的部分“丢失”。它们可能由于多种原因而丢失,但产生的合并信息非常相似。
如果您合并到浅层工作副本(即那些设置为除无限之外的深度的副本),那么您将看到创建子树合并信息。
例如,假设我们以“immediates”深度检出分支工作副本
1.6.6>svn checkout %ROOT_URL%/branches/B2.0–depth immediates
A B2.0/A
已检出修订版 13。
在这种情况下,分支没有任何 mergeinfo。
1.6.6>svn propget svn:mergeinfo -vR B2.0
现在,我们合并来自主干的所有可用修订版。由于工作副本的一部分由于浅层检出而丢失,因此我们得到了一些树冲突。
1.6.6>svn merge ^/trunk B2.0
— 将 r4 到 r13 合并到 'B2.0/A' 中
C B2.0/A/mu
C B2.0/A/C
C B2.0/A/D
冲突摘要
树冲突:3
我们还获得了添加到 B2.0/A 的新 mergeinfo。
1.6.6>svn status B2.0
M B2.0
M B2.0/A
! C B2.0/A/mu
> 本地缺失,合并时传入编辑
! C B2.0/A/C
> 本地删除,合并时传入编辑
! C B2.0/A/D
> 本地删除,合并时传入编辑
1.6.6>svn propget svn:mergeinfo -vR B2.0
'B2.0' 上的属性
svn:mergeinfo
/trunk:4-13
'B2.0/A' 上的属性
svn:mergeinfo
/trunk/A:4-13*
之所以会发生这种情况,是因为 svn:mergeinfo 属性是可继承的。由于我们实际上没有 B2.0/A 的任何子项在我们的工作副本中,因此我们实际上没有从主干将 r3:13 合并到它们。B2.0/A 上不可继承的 mergeinfo 范围“/trunk/A:4-13*”实际上意味着“我们已将 r3:13 从主干合并到此深度,但不再进一步”。
如果没有 B2.0/A 上的此不可继承的 mergeinfo,如果我们解决树冲突并提交此合并,然后另一个用户检出一个分支的完整深度工作副本,那么他们将看到 r3:13 已完全合并到分支中,这显然不是事实。
如果您始终合并到完整深度工作副本中,则可以轻松避免这种类型的子树 mergeinfo。如果您需要合并到浅层工作副本中,请记住此行为,并确保提交所有子树 mergeinfo。
与浅层工作副本密切相关的是浅层合并。在这里,不是合并目标的深度是浅层的,而是合并本身的深度,如合并命令的 –depth 选项所指定(您甚至可以对浅层工作副本进行浅层合并)。
例如,我们检出一个分支的完整深度工作副本
1.6.6>svn checkout %ROOT_URL%/branches/B2.0/branches/B2.0 B2
A B2/A
A B2/A/B
A B2/A/B/lambda
A B2/A/B/E
A B2/A/B/E/alpha
A B2/A/B/E/beta
A B2/A/B/F
A B2/A/mu
A B2/A/C
A B2/A/D
A B2/A/D/gamma
A B2/A/D/G
A B2/A/D/G/pi
A B2/A/D/G/rho
A B2/A/D/G/tau
A B2/A/D/H
A B2/A/D/H/chi
A B2/A/D/H/omega
A B2/A/D/H/psi
已检出修订版 13。
浅层合并“下降”到请求的深度,在本例中,下降到目标的直接子级。
1.6.6>svn merge ^/trunk/A B2/A –depth immediates
— 将 r4 到 r13 合并到 'B2/A/mu'
U B2/A/mu
与合并到浅层工作副本类似,非可继承的合并信息将设置在合并到的范围的边界上。
1.6.6>svn status B2
M B2
M B2/A/B
M B2/A/mu
M B2/A/C
M B2/A/D
1.6.6>svn propget svn:mergeinfo -vR B2
Properties on 'B2'
svn:mergeinfo
/trunk:4-13
Properties on 'B2/A/B'
svn:mergeinfo
/trunk/A/B:4-13*
Properties on 'B2/A/C'
svn:mergeinfo
/trunk/A/C:4-13*
Properties on 'B2/A/D'
svn:mergeinfo
/trunk/A/D:4-13*
同样,可以通过执行完整的 –depth infinity 合并来避免这种情况。
如果你的合并目标中包含切换的子树,它们几乎与深度为空的子树相同。主要区别在于合并将记录切换子树根部的正常可继承合并信息,因为从该点向下,你拥有完整的工作副本(除非切换的子树内部还有切换的子树,或者浅层切换的子树等)。
另一种“缺少子树”的情况是,当你合并到一个你没有完全读取权限的工作副本中。同样,你也会看到在缺少的子树周围添加了非可继承的合并信息。我怀疑这种情况很少见,但如果你在合并后突然看到子树合并信息出现,并且上述原因都不适用,这可能是原因。
* 如果你不熟悉合并信息省略,可以在这里阅读相关信息 http://www.open.collab.net/community/subversion/articles/merge-info.html。
Paul Burba 是 Apache 软件基金会 Subversion 项目的提交者,过去九年一直致力于 Subversion 的开发。他作为 Collabnet 的软件工程师在家乡新罕布什尔州工作,在不写代码的时候,他通常会和侄子一起滑雪,和朋友一起骑山地自行车,或者和妻子一起旅行。在遥远的过去,Paul 从新罕布什尔大学获得了商业学位。最近,他从波士顿大学获得了计算机科学硕士学位。