【问题标题】:What's SVN's actual use of mergeinfo?SVN对mergeinfo的实际用途是什么?
【发布时间】:2014-02-20 19:11:29
【问题描述】:

我早就听说过 svn 的合并冲突问题。

当我得知 svn 几个版本前实现了一个名为 mergeinfo 的功能时,我心想,我松了一口气。似乎它的引入将允许 svn 有足够的信息来解决它的合并问题,只要它们弹出。直到我陷入以下情况:

上图的脚本示例:

SVN=${SVN:-svn}
SVNADMIN=${SVNAMDIN:-svnadmin}

rm -rf repo wc

$SVNADMIN create repo

$SVN co file://$PWD/repo wc
cd wc

# r1
$SVN mkdir trunk branches
$SVN ci -m 'structure'
$SVN up

# r2
echo 2 > trunk/t.txt
$SVN add trunk/t.txt
$SVN ci -m 'add t.txt'
$SVN up

# r3
$SVN cp trunk branches/A
$SVN ci -m 'create branch A'
$SVN up

# r4
echo 4 > branches/A/a.txt
$SVN add branches/A/a.txt
$SVN ci -m 'add a.txt'
$SVN up

# r5
$SVN cp trunk branches/B
$SVN ci -m 'create branch B'
$SVN up

# r6
echo 6 > branches/B/b.txt
$SVN add branches/B/b.txt
$SVN ci -m 'add b.txt'
$SVN up

# r7
$SVN merge ^/branches/B branches/A
$SVN ci -m 'merge branch B into A'
$SVN up

# r8
echo 8 > branches/A/d.txt
$SVN add branches/A/d.txt
$SVN ci -m 'add d.txt'
$SVN up

# r9
$SVN merge ^/branches/A branches/B

如果 svn 保存了每个分支来自哪里的历史记录,为什么它不能理解 b.txt 未被触及 @branch A

如果它不能解决这个问题,svn 对mergeinfo 有什么实际用途?

如果svn无法处理这个问题,难道不能在这方面创建一个工具来帮助我(即自动解决这种不费吹灰之力的问题..)吗?我猜可能已经存在一个?

谢谢

【问题讨论】:

  • 显示真实输出,而不是图片 - 我看不出您的绘图中可能存在的问题
  • 我添加了一个我认为与提问者图表相匹配的示例。有些地方不清楚。文件是已经存在还是新文件(我将它们设为新文件,如果发生冲突,差异将取决于冲突类型)?合并究竟是如何完成的?他们是精选还是完整的分支合并。
  • BenReser 没问题。它们是新文件,我正在执行完整的分支合并。

标签: svn version-control merge mergeinfo


【解决方案1】:

Subversion 中有两种类型的合并:

  1. 三点合并
  2. 两点合并(又名重新整合合并)

假设您正在开发主干,并且您有一个特定的功能需要完成。您创建一个 Feature A 分支。在开发该功能时,您希望您在 trunk 上所做的工作包含在 Feature A 中,这样您就可以跟上其他人的工作。 Subversion 将使用三点合并。

Subversion 将从分支发生的那一点开始查看主干和分支特性 A 之间的区别。最近的共同祖先。然后,它会考虑对功能 A 所做的所有更改(您不想触摸)以及在主干上完成的代码更改。

Subversion 将合并主干上的更改,而不会覆盖分支上所做的更改。标准的合并过程,Subversion 做得很好。

svn:mergeinfo 来自哪里?您不想合并两次相同的更改,因此 Subversion 使用 svn:mergeinfo 属性跟踪更改。如果 Subversion 发现来自 Revision 5 的主干中的更改已经被合并,它不会重新合并该更改。这个很好用。

现在,您已经完成了您的功能,并且您希望将这些更改合并回主干。您将最后一个主干进行分支合并,提交这些更改,然后从功能分支合并回主干。

这里有点问题。我们通过svn:mergeinfo 跟踪了我们从主干合并到功能分支的内容。但是,由于我们还没有从 Feature 分支合并到主干,所以那里没有 svn:mergeinfo。如果我们尝试从 Feature 分支正常的三点合并到主干,主干将假定 Feature 分支中的所有更改都应该合并回主干。但是,其中许多功能实际上是已合并的主干更改。

说实话,在这一点上,我们想做一个两点合并。一旦我们进行合并,我们希望主干和功能分支完全匹配。毕竟,我们现在已经定期将主干合并到功能分支中。我们想要做的是将这些功能重新合并到主干中。因此,主干将与功能分支相同。

在 Subversion 1.8 之前,您必须通过运行 svn merge --reintegration 来强制重新集成合并。现在,Subversion 将查看合并历史并确定何时应该进行重新集成。


现在是棘手的部分。仔细查看修订号。这些将非常非常重要!

  • 修订版 10:我对 Trunk 进行了最后的更改,我需要将这些更改合并到 Feature 分支中。
  • Revision 11:我将主干合并到功能分支中。 svn:mergeinfo 将显示从 Revision 1 到 Revision 10 的所有主干都在 Feature 分支中。由于主干上的最后一次更改是修订版 10,因此这非常有意义。
  • Revision 12:我将 Feature Branch 的 Revision 11 合并到主干。这是一个重新整合合并。在此之后,Feature 分支上的内容和主干中的内容应该完全一致。

现在,关键来了!

  • 修订版 13:我对后备箱进行了另一项更改。

现在,我想将其合并到我的功能分支(创建修订版 14)。现在,功能分支上的svn:mergeinfo 是什么意思?它表示从 Revision 1 到 Revision 10 的主干已合并到 Feature 分支中。但是,主干的修订版 12 和修订版 13 还没有。因此,Subversion 将希望将 Revision 12 和 Revision 13 合并回 Feature 分支。

请稍等!

主干上的修订版 12 是我将功能分支中的所有更改合并回主干!也就是说,Revision 12 已经包含了我在 Feature 分支中所做的所有修订更改。如果我将修订版 12 合并回我的功能分支,我会说修订版 12 中主干上的所有这些更改(实际上是在功能分支上进行的更改并合并到主干)需要合并到功能分支中。但是,这些更改也是在 Feature 分支上进行的。你能说合并冲突吗?我知道你可以!


有两种处理方式:

  • 推荐方式:将功能分支重新集成回主干后,删除该分支。锁定它。永远不要再使用它。不要碰!这并不像听起来那么糟糕。重新集成合并后,您的主干和该功能分支无论如何都会匹配。从主干删除和重新创建分支不会那么糟糕。
  • 棘手的工作方式:我们需要做的是让 Subversion 认为 Revision #12(或重新集成合并更改)已经合并到我们的功能分支中。我们可以使用svn:mergeinfo 属性来解决问题。而且,我过去就是这样做的。如果上面写着trunk:1-11,我会手动将其更改为trunk:1-12

    这很棘手,但是太棘手了,而且有风险,因为 Subversion 已经为您提供了一种无需手动更改即可操作 svn:mergeinfo 的方法。

这称为仅记录合并。

$ svn co svn://branches/feature_a
$ cd feature_a
$ svn merge --record-only -c 12 svn://trunk
$ svn commit -m "Adding in the reintegration merge back into the feature branch."

这会更改功能分支上的svn:mergeinfo,而不会影响文件的实际内容。没有进行真正的合并,但 Subversion 现在知道主干的修订版 12 已经在 Feature 分支中。一旦你这样做了,你就可以重用功能分支。


现在看一下您的图表:当您将分支 B 合并到分支 A 时,您将 B 中的所有更改合并到 A 中,svn:mergeinfo 跟踪了这一点。当您将分支 B 合并回分支 A 时,您已经在分支 A 中拥有来自分支 B 的所有更改,并且您不希望将这些更改带回分支 B。您应该使用 reintegration 合并:

$ cd $branch_a_working_dir
$ svn merge $REPO/branches/B
$ svn commit -m "Rev 7: All of my changes on Branch B are now in A"
$ vi d.txt
$ svn add d.txt
$ svn commit -m"Rev 8: I added d.txt"
$ cd $branch_b_working_dir
$ svn merge --reintegrate svn://branch/A  # Note this is a REINTEGRATION merge!
$ svn commit -m"Rev 9: I've reintegrated Branch A into Branch B

现在,如果我们想继续使用分支 A 进行进一步的更改:

$ cd $branch_a_working_dir
$ svn merge -c 9 --record-only $REPO/branches/b
$ svn commit -m"I've reactivated Branch A and can make further changes"

我希望这能解释一下svn:mergeinfo 的工作原理,为什么你必须知道你是使用普通的三点合并还是两点重新整合合并,以及如何能够重新激活分支在您完成重新集成合并之后。

只要记住这一点,Subversion 合并就可以很好地工作。

【讨论】:

  • 如果需要做进一步的工作,我总是删除一个分支并重新创建。直接编辑合并信息并不可靠,并且重新切割分支比只使用记录合并要快。
【解决方案2】:

David W.'s 答案非常好,但我将提供我自己的答案,以更现代的方法回答这个问题。大卫告诉你的是真的,在阅读我的答案时理解他的答案很有用,所以如果你还没有读过他的话,你应该先读一下。我的回答为大卫指出的问题和解决方案提供了更现代的理解。

首先,简单的答案是,如果您使用的是 Subversion 1.8,那么您所呈现的情况就可以正常工作。我添加到问题中的示例脚本在使用 Subversion 1.8 运行时没有冲突。只需更新客户端即可获得此功能。

这里更长的答案是,对于旧版本的 Subversion,您需要知道何时使用名称不佳的 --reintegrate 选项(1.8 为您解决了这个问题)。尽管名称 --reintegrate 不仅仅适用于将分支重新集成到主干时。

人们经常遇到的一个问题是,当他们使用功能分支时,他们希望将功能分支合并回他们的主干,然后继续使用该分支。正如大卫的回答所暗示的那样,过去有两种方法可以解决这个问题。首先删除分支,然后使其新鲜。其次,只记录合并。我的 Subversion 开发人员之一 Julian Foad 在开发 1.8 时意识到这两种技术都不是必需的。他在his presentation on merging at the Subversion Live 2012 conference 中对此进行了报道。他的演示文稿的幻灯片可在此处在线获得(请注意,这是会议所有幻灯片的 PDF,因此它并不十分微小),有关此问题的部分从第 123 页开始。

总结他的介绍,典型的功能分支工作流程是创建一个分支离开主干。向您的功能分支提交并定期从主干合并到您的功能分支,以与主干上的内容同步,例如svn merge ^/trunk $BRANCH_WC。然后,当您准备好合并回主干时,您可以使用svn merge --reintegrate ^/branches/myfeature $TRUNK_WC。现在您已经到了传统智慧必须删除功能分支或仅合并记录的地步。相反,Julian 发现如果您遵循这些规则,您可以继续使用该分支。

  • 每次在与两个分支之间的最后一次合并方向相同的方向合并时,您使用--reintegrate 选项。将分支的创建视为从该创建位置合并。

  • 每次更改要在两个分支之间合并的方向时,使用 --reintegrate 进行合并。

在您的具体情况下,您正在更改合并的方向。 r7 正在从分支 B 合并到分支 A。r9 从分支 A 合并到分支 B。因此,当您进行 r9 合并时,您需要使用 --reintegrate。这个逻辑正是 Subversion 在 1.8 中为您所做的。

最后,我不建议像您在这里所做的那样在兄弟分支之间进行合并。很多简单的案例,比如你在这里做的事情都会很好地工作。但是,如果您例如拆分文件(svn cp file file2,删除文件 1 的一部分和文件 2 的不同部分),那么当您尝试将两个功能分支中的最后一个合并回主干时,您将遇到问题(假设您已将拆分合并到两个兄弟分支)。最好将合并限制在两个分支(子分支和父分支)之间。您可以在转到父级的父级之前从其他分支中创建分支并合并回其父级,依此类推,就可以了。我们想让这些事情在未来也能正常工作,但这是目前 1.8 中的情况(比我们过去好得多,但不如我们想要的那么好)。

【讨论】:

    【解决方案3】:

    您误解了 mergeinfo 的含义和用法 - 它仅包含已经(对于此节点)合并的修订和合并来源,以便不要合并修订两次,与内容无关

    【讨论】:

      猜你喜欢
      • 2011-04-02
      • 2019-05-17
      • 2015-04-22
      • 2021-07-22
      • 1970-01-01
      • 2018-02-02
      • 2011-09-20
      • 2015-06-07
      • 1970-01-01
      相关资源
      最近更新 更多