18

我早就听说过 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 无法处理这个问题,难道不能在这方面创建一个工具来帮助我(即自动解决这种不费吹灰之力的问题..)吗?我猜也许一个已经存在?

谢谢

4

3 回答 3

27

Subversion 中有两种类型的合并:

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

假设您正在开发主干,并且您有一个特定的功能需要完成。您创建一个功能 A分支。当你处理这个特性时,你希望你在主干上所做的工作包含在特性 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 分支中。
  • 修订版 11:我将主干合并到功能分支中。svn:mergeinfo将显示从 Revision 1 到 Revision 10 的所有主干都在 Feature 分支中。由于主干上的最后一次更改是修订版 10,因此这非常有意义。
  • 修订版 12:我将功能分支的修订版 11 合并到主干中。这是重新整合合并。在此之后,功能分支上的内容和主干中的内容应该完全一致。

现在,这是踢球者!

  • 修订版 13:我对主干进行了另一项更改。

现在,我想将它合并到我的 Feature 分支(创建 Revision 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(或重新集成合并更改)已经合并到我们的 Feature 分支中。我们可以和这处房产混在一起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。您应该使用重新集成合并:

$ 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 合并就可以很好地工作。

于 2014-02-20T21:08:55.227 回答
11

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

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

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

人们经常遇到的问题之一是当他们使用功能分支时,他们希望将功能分支合并回他们的主干,然后继续使用该分支。正如大卫的回答所暗示的那样,过去有两种方法可以解决这个问题。首先删除分支,然后使其新鲜。其次,只记录合并。我的 Subversion 开发人员之一 Julian Foad 在开发 1.8 时意识到这两种技术都不是必需的。他在 Subversion Live 2012 会议上关于合并的演讲中谈到了这一点。他的演示文稿的幻灯片可在此处在线获得(请注意,这是会议所有幻灯片的 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,删除 file1 的一部分和 file2 的不同部分),那么当您尝试将两个功能分支中的最后一个合并回主干时(假设您已将拆分合并到两个兄弟分支),您将遇到问题。最好将合并限制在两个分支(子分支和父分支)之间。您可以从其他分支创建分支并合并回其父级,然后再转到父级的父级等等,这样就可以了。我们想让这些东西在未来也能正常工作,但这是目前 1.8 中的情况(比我们过去好得多,但没有我们想要的那么好)。

于 2014-02-21T00:41:26.090 回答
-1

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

于 2014-02-20T20:40:38.107 回答