合并:Hg/Git 与 SVN

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2475831/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 08:05:39  来源:igfitidea点击:

Merging: Hg/Git vs. SVN

gitsvnmercurialmergedvcs

提问by stmax

I often read that Hg (and Git and...) are better at merging than SVN but I have never seen practical examples of where Hg/Git can merge something where SVN fails (or where SVN needs manual intervention). Could you post a few step-by-step lists of branch/modify/commit/...-operations that show where SVN would fail while Hg/Git happily moves on? Practical, not highly exceptional cases please...

我经常读到 Hg(和 Git 和...)在合并方面比 SVN 更好,但我从未见过 Hg/Git 可以在 SVN 失败的地方(或 SVN 需要手动干预的地方)合并某些东西的实际例子。您能否发布一些分支/修改/提交/...操作的分步列表,以显示当 Hg/Git 愉快地继续前进时 SVN 会失败的地方?实际情况,不是非常特殊的情况,请...

Some background: we have a few dozen developers working on projects using SVN, with each project (or group of similar projects) in its own repository. We know how to apply release- and feature-branches so we don't run into problems very often (i.e., we've been there, but we've learned to overcome Joel's problemsof "one programmer causing trauma to the whole team" or "needing six developers for two weeks to reintegrate a branch"). We have release-branches that are very stable and only used to apply bugfixes. We have trunks that should be stable enough to be able to create a release within one week. And we have feature-branches that single developers or groups of developers can work on. Yes, they are deleted after reintegration so they don't clutter up the repository. ;)

一些背景:我们有几十名开发人员在使用 SVN 开发项目,每个项目(或一组类似的项目)都在自己的存储库中。我们知道如何应用发布和功能分支,所以我们不会经常遇到问题(即,我们一直在那里,但我们已经学会克服乔尔的“一个程序员对整个团队造成创伤”的问题或“需要六个开发人员两周时间才能重新整合一个分支”)。我们有非常稳定的发布分支,仅用于应用错误修正。我们的主干应该足够稳定,能够在一周内创建一个版本。我们有功能分支,单个开发人员或开发人员组可以工作。是的,它们在重新集成后会被删除,因此它们不会弄乱存储库。;)

So I'm still trying to find the advantages of Hg/Git over SVN. I'd love to get some hands-on experience, but there aren't any bigger projects we could move to Hg/Git yet, so I'm stuck with playing with small artificial projects that only contain a few made up files. And I'm looking for a few cases where you can feel the impressive power of Hg/Git, since so far I have often read about them but failed to find them myself.

所以我仍在努力寻找 Hg/Git 相对于 SVN 的优势。我很想获得一些实践经验,但是我们还没有可以迁移到 Hg/Git 的任何更大的项目,所以我一直在玩只包含几个组成文件的小型人工项目。我正在寻找一些可以让您感受到 Hg/Git 令人印象深刻的力量的案例,因为到目前为止我经常阅读有关它们但未能自己找到它们的案例。

采纳答案by Jakub Nar?bski

I do not use Subversion myself, but from the release notes for Subversion 1.5: Merge tracking (foundational)it looks like there are the following differences from how merge tracking work in full-DAGversion control systems like Git or Mercurial.

我自己不使用 Subversion,但从Subversion 1.5:合并跟踪(基础)发行说明来看,与 Git 或 Mercurial 等完整DAG版本控制系统中合并跟踪的工作方式似乎存在以下差异。

  • Merging trunk to branch is different from merging branch to trunk: for some reason merging trunk to branch requires --reintegrateoption to svn merge.

    In distributed version control systems like Git or Mercurial there is no technicaldifference between trunk and branch: all branches are created equal (there might be socialdifference, though). Merging in either direction is done the same way.

  • You need to provide new -g(--use-merge-history) option to svn logand svn blameto take merge tracking into account.

    In Git and Mercurial merge tracking is automatically taken into account when displaying history (log) and blame. In Git you can request to follow first parent only with --first-parent(I guess similar option exists also for Mercurial) to "discard" merge tracking info in git log.

  • From what I understand svn:mergeinfoproperty stores per-path information about conflicts (Subversion is changeset-based), while in Git and Mercurial it is simply commit objects that can have more than one parent.

  • "Known Issues"subsection for merge tracking in Subversion suggests that repeated / cyclic / reflective merge might not work properly. It means that with the following histories second merge might not do the right thing ('A' can be trunk or branch, and 'B' can be branch or trunk, respectively):

    *---*---x---*---y---*---*---*---M2        <-- A
             \       \             /
              --*----M1---*---*---/           <-- B
    

    In the case the above ASCII-art gets broken: Branch 'B' is created (forked) from branch 'A' at revision 'x', then later branch 'A' is merged at revision 'y' into branch 'B' as merge 'M1', and finally branch 'B' is merged into branch 'A' as merge 'M2'.

    *---*---x---*-----M1--*---*---M2          <-- A
             \       /           / 
              \-*---y---*---*---/             <-- B
    

    In the case the above ASCII-art gets broken: Branch 'B' is created (forked) from branch 'A' at revision 'x', it is merged into branch 'A' at 'y' as 'M1', and later merged again into branch 'A' as 'M2'.

  • Subversion might not support advanced case of criss-cross merge.

    *---b-----B1--M1--*---M3
         \     \ /        /
          \     X        /
           \   / \      /
            \--B2--M2--*
    

    Git handles this situation just fine in practice using "recursive" merge strategy. I am not sure about Mercurial.

  • In "Known Issues"there is warning that merge tracking migh not work with file renames, e.g. when one side renames file (and perhaps modifies it), and second side modifies file without renaming (under old name).

    Both Git and Mercurial handle such case just fine in practice: Git using rename detection, Mercurial using rename tracking.

  • 将主干合并到分支与将分支合并到主干不同:出于某种原因,将主干合并到分支需要--reintegrate选择svn merge.

    在像 Git 或 Mercurial 这样的分布式版本控制系统中,主干和分支之间没有技术差异:所有分支都是平等的(尽管可能存在社会差异)。以相同的方式向任一方向合并。

  • 您需要提供 new -g( --use-merge-history) 选项svn logsvn blame考虑合并跟踪。

    在 Git 和 Mercurial 中,显示历史(日志)和责备时会自动考虑合并跟踪。在 Git 中,您可以请求仅跟随第一个父级--first-parent(我猜 Mercurial 也存在类似的选项)以“丢弃” git log.

  • 据我了解,svn:mergeinfo属性存储关于冲突的每个路径信息(Subversion 是基于变更集的),而在 Git 和 Mercurial 中,它只是提交可以有多个父级的对象。

  • Subversion 中合并跟踪的“已知问题”小节表明重复/循环/反射合并可能无法正常工作。这意味着对于以下历史记录,第二次合并可能不会做正确的事情('A' 可以是主干或分支,'B' 可以分别是分支或主干):

    *---*---x---*---y---*---*---*---M2        <-- A
             \       \             /
              --*----M1---*---*---/           <-- B
    

    在上述 ASCII-art 被破坏的情况下:分支 'B' 从修订版 'x' 的分支 'A' 创建(分叉),然后分支 'A' 在修订版 'y' 合并到分支 'B' 为合并'M1',最后分支'B'被合并到分支'A'中作为合并'M2'。

    *---*---x---*-----M1--*---*---M2          <-- A
             \       /           / 
              \-*---y---*---*---/             <-- B
    

    在上述 ASCII 艺术被破坏的情况下:分支 'B' 从修订版 'x' 的分支 'A' 创建(分叉),它在 'y' 处合并到分支 'A' 作为 'M1',然后再次合并到分支“A”中作为“M2”。

  • Subversion 可能不支持criss-cross merge 的高级案例。

    *---b-----B1--M1--*---M3
         \     \ /        /
          \     X        /
           \   / \      /
            \--B2--M2--*
    

    Git 在实践中使用“递归”合并策略很好地处理这种情况。我不确定 Mercurial。

  • “已知问题”中,警告合并跟踪可能不适用于文件重命名,例如,当一侧重命名文件(并可能修改它),而另一侧修改文件而不重命名(以旧名称)时。

    Git 和 Mercurial 在实践中都能很好地处理这种情况:Git 使用重命名检测,Mercurial 使用重命名跟踪

HTH

HTH

回答by Martin Geisler

I too have been looking for a case where, say, Subversion fails to merge a branch and Mercurial (and Git, Bazaar, ...) does the right thing.

我也一直在寻找一个案例,比如 Subversion 无法合并一个分支,而 Mercurial(和 Git、Bazaar 等)做正确的事情。

The SVN Book describes how renamed files are merged incorrectly. This applies to Subversion 1.5, 1.6, 1.7, and 1.8! I have tried to recreate the situation below:

SVN 手册描述了重命名的文件如何被错误地合并。这适用于 Subversion 1.51.61.71.8!我试图重现以下情况:

cd /tmp
rm -rf svn-repo svn-checkout
svnadmin 创建 svn-repo
svn 结帐文件:///tmp/svn-repo svn-checkout
cd svn 结帐
mkdir 主干分支
echo '再见,世界!> 中继/你好.txt
svn 添加主干分支
svn commit -m '初始导入。
svn copy '^/trunk' '^/branches/rename' -m '创建分支。
svn 开关 '^/trunk' 。
echo '你好,世界!> 你好.txt
svn commit -m '更新主干。
svn 开关 '^/branches/rename' 。
svn 重命名 hello.txt hello.en.txt
svn commit -m '在分支上重命名。
svn 开关 '^/trunk' 。
svn merge --reintegrate '^/branches/rename'

According to the book, the merge should finish cleanly, but with wrong data in the renamed file since the update on trunkis forgotten. Instead I get a tree conflict (this is with Subversion 1.6.17, the newest version in Debian at the time of writing):

根据这本书,合并应该干净利落地完成,但是由于trunk忘记了更新,因此重命名的文件中有错误的数据。相反,我遇到了树冲突(这是与 Subversion 1.6.17 相关的,这是撰写本文时 Debian 中的最新版本):

--- Merging differences between repository URLs into '.':
A    hello.en.txt
   C hello.txt
Summary of conflicts:
  Tree conflicts: 1

There shouldn't be any conflict at all — the update should be merged into the new name of the file. While Subversion fails, Mercurial handles this correctly:

根本不应该有任何冲突 - 更新应该合并到文件的新名称中。当 Subversion 失败时,Mercurial 可以正确处理:

rm -rf /tmp/hg-repo
hg init /tmp/hg-repo
cd /tmp/hg-repo
echo 'Goodbye, World!' > hello.txt
hg add hello.txt
hg commit -m 'Initial import.'
echo 'Hello, World!' > hello.txt
hg commit -m 'Update.'
hg update 0
hg rename hello.txt hello.en.txt
hg commit -m 'Rename.'
hg merge

Before the merge, the repository looks like this (from hg glog):

在合并之前,存储库看起来像这样(来自hg glog):

@  changeset:   2:6502899164cc
|  tag:         tip
|  parent:      0:d08bcebadd9e
|  user:        Martin Geisler 
|  date:        Thu Apr 01 12:29:19 2010 +0200
|  summary:     Rename.
|
| o  changeset:   1:9d06fa155634
|/   user:        Martin Geisler 
|    date:        Thu Apr 01 12:29:18 2010 +0200
|    summary:     Update.
|
o  changeset:   0:d08bcebadd9e
   user:        Martin Geisler 
   date:        Thu Apr 01 12:29:18 2010 +0200
   summary:     Initial import.

The output of the merge is:

合并的输出是:

merging hello.en.txt and hello.txt to hello.en.txt
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)

In other words: Mercurial took the change from revision 1 and merged it into the new file name from revision 2 (hello.en.txt). Handling this case is of course essential in order to support refactoring and refactoring is exactlythe kind of thing you will want to do on a branch.

换句话说:Mercurial 将修订版 1 的更改合并到修订版 2 ( hello.en.txt)的新文件名中。为了支持重构,处理这种情况当然是必不可少的,而重构正是您想要在分支上做的事情。

回答by VonC

Without speaking about the usual advantages (offline commits, publication process, ...) here is a "merge" example I like:

不谈通常的优势(离线提交,发布过程,......)这里是我喜欢的“合并”示例:

The main scenario I keep seeing is a branch on which ... twounrelated tasks are actually developed
(it started from one feature, but it lead to the development of this other feature.
Or it started from a patch, but it lead to the development of another feature).

我一直看到的主要场景是一个分支,在该分支上...实际上开发了两个不相关的任务
(它从一个特性开始,但它导致了另一个特性的开发。
或者它从一个补丁开始,但它导致了开发另一个功能)。

How to you merge only one of the two feature on the main branch?
Or How do you isolate the two features in their own branches?

如何仅合并主分支上的两个功能之一?
或者你如何在自己的分支中隔离这两个功能?

You could try to generate some kind of patches, the problem with that is you are not sure anymore of the functional dependencieswhich could have existed between:

您可以尝试生成某种补丁,问题在于您不再确定可能存在于以下之间的功能依赖关系:

  • the commits (or revision for SVN) used in your patches
  • the other commits not part of the patch
  • 补丁中使用的提交(或 SVN 的修订版)
  • 另一个提交不是补丁的一部分

Git (and Mercurial too I suppose) propose the rebase --onto option to rebase (reset the root of the branch) part of a branch:

Git(我想还有 Mercurial)建议使用 rebase --onto 选项来重新设置分支的一部分(重置分支的根):

From Jefromi's post

来自Jefromi 的帖子

- x - x - x (v2) - x - x - x (v2.1)
           \
            x - x - x (v2-only) - x - x - x (wss)

you can untangle this situation where you have patches for the v2 as well as a new wss feature into:

您可以解决这种情况,即您有 v2 的补丁以及新的 wss 功能:

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - x - x (v2-only)
           \
             x - x - x (wss)

, allowing you to:

,允许您:

  • test each branch in isolation to check if everything compile/work as intended
  • merge only what you want to main.
  • 单独测试每个分支以检查一切是否按预期编译/工作
  • 只合并你想要的主要内容。


The other feature I like (which influence merges) is the ability to squash commits(in a branch not yet pushed to another repo) in order to present:

我喜欢的另一个功能(影响合并)是能够压缩提交(在尚未推送到另一个存储库的分支中)以呈现:

  • a cleaner history
  • commits which are more coherent (instead of commit1 for function1, commit2 for function2, commit3 again for function1...)
  • 更清洁的历史
  • 更连贯的提交(而不是函数1的commit1,函数2的commit2,函数1的commit3......)

That ensure merges which are a lot easier, with less conflicts.

这确保合并更容易,冲突更少。

回答by cedd

We recently migrated from SVN to GIT, and faced this same uncertainty. There was a lot of anecdotal evidence that GIT was better, but it was hard to come across any examples.

我们最近从 SVN 迁移到 GIT,也面临着同样的不确定性。有很多轶事证据表明 GIT 更好,但很难找到任何例子。

I can tell you though, that GIT is A LOT BETTERat merging than SVN. This is obviously anecdotal, but there is a table to follow.

不过我可以告诉你,GIT在合并方面比 SVN 好得多。这显然是轶事,但有一个表格可以遵循。

Here are some of the things we found:

以下是我们发现的一些内容:

  • SVN used to throw up a lot of tree conflicts in situations where it seemed like it shouldn't. We never got to the bottom of this but it doesn't happen in GIT.
  • While better, GIT is significantly more complicated. Spend some time on training.
  • We were used to Tortoise SVN, which we liked. Tortoise GIT is not as good and this may put you off. However I now use the GIT command line which I much prefer to Tortoise SVN or any of the GIT GUI's.
  • SVN 过去常常在看起来不应该的情况下引发很多树冲突。我们从来没有深入到这个问题的底部,但它不会发生在 GIT 中。
  • 虽然更好,但 GIT 明显要复杂得多。花一些时间在训练上。
  • 我们习惯了我们喜欢的 Tortoise SVN。Tortoise GIT 没有那么好,这可能会让你失望。然而,我现在使用 GIT 命令行,我更喜欢 Tortoise SVN 或任何 GIT GUI。

When we were evaluating GIT we ran the following tests. These show GIT as the winner when it comes to merging, but not by that much. In practice the difference is much larger, but I guess we haven't managed to replicate the situations that SVN handles badly.

当我们评估 GIT 时,我们运行了以下测试。这些表明 GIT 在合并方面是赢家,但不是那么多。在实践中,差异要大得多,但我想我们还没有设法复制 SVN 处理不好的情况。

GIT vs SVN Merging Evaluation

GIT 与 SVN 合并评估

回答by Paul S

Others have covered the more theoretical aspects of this. Maybe I can lend a more practical perspective.

其他人已经涵盖了更多的理论方面。也许我可以提供一个更实际的观点。

I'm currently working for a company that uses SVN in a "feature branch" development model. That is:

我目前为一家在“功能分支”开发模型中使用 SVN 的公司工作。那是:

  • No work can be done on trunk
  • Each developer can have create their own branches
  • Branches should last for the duration of the task undertaken
  • Each task should have it's own branch
  • Merges back to trunk need to be authorized (normally via bugzilla)
  • At times when high levels of control are needed, merges may be done by a gatekeeper
  • 不能在trunk上做任何工作
  • 每个开发者都可以创建自己的分支
  • 分支应持续执行任务的持续时间
  • 每个任务都应该有自己的分支
  • 合并回主干需要授权(通常通过 bugzilla)
  • 有时需要高级别的控制,合并可能由看门人完成

In general, it works. SVN can be used for a flow like this, but it's not perfect. There are some aspects of SVN which get in the way and shape human behaviour. That gives it some negative aspects.

一般来说,它有效。SVN 可以用于这样的流程,但它并不完美。SVN 的某些方面会阻碍并影响人类行为。这给了它一些负面影响。

  • We've had quite a few problems with people branching from points lower than ^/trunk. This litters merge information records throughout the tree, and eventually breaks the merge tracking. False conflicts start appearing, and confusion reigns.
  • Picking up changes from trunk into a branch is relatively straight forward. svn mergedoes what you want. Merging your changes back requires (we're told) --reintegrateon the merge command. I've never truly understood this switch, but it means that the branch can't be merged into trunk again. This means it's a dead branch and you have to create a new one to continue work. (See note)
  • The whole business of doing operations on the server via URLs when creating and deleting branches really confuses and scares people. So they avoid it.
  • Switching between branches is easy to get wrong, leaving part of a tree looking at branch A, whilst leaving another part looking at branch B. So people prefer to do all their work in one branch.
  • 人们从低于^/trunk. 这些垃圾会合并整个树中的信息记录,并最终破坏合并跟踪。虚假的冲突开始出现,混乱盛行。
  • 获取从主干到分支的更改相对简单。svn merge做你想做的。合并您的更改需要(我们被告知)--reintegrate合并命令。我从来没有真正理解这个开关,但这意味着分支不能再次合并到主干中。这意味着它是一个死分支,您必须创建一个新分支才能继续工作。(见说明)
  • 创建和删除分支时通过 URL 在服务器上进行操作的整个业务确实使人们感到困惑和恐惧。所以他们避免它。
  • 在分支之间切换很容易出错,让树的一部分看着分支 A,而让另一部分看着分支 B。所以人们更喜欢在一个分支上完成所有的工作。

What tends to happen is that an engineer creates a branch on day 1. He starts his work and forgets about it. Some time later a boss comes along and asks if he can release his work to trunk. The engineer has been dreading this day because reintegrating means:

通常情况下,工程师在第 1 天创建了一个分支。他开始工作并忘记了它。过了一段时间,一个老板走过来,问他是否可以把他的工作放到行李箱里。工程师一直害怕这一天,因为重新整合意味着:

  • Merging his long lived branch back into trunk and solving all conflicts, and releasing unrelated code that should have been in a separate branch, but wasn't.
  • Deleting his branch
  • Creating a new branch
  • Switching his working copy to the new branch
  • 将他的长期存在的分支合并回主干并解决所有冲突,并发布本应在单独分支中但没有的无关代码。
  • 删除他的分支
  • 创建一个新分支
  • 将他的工作副本切换到新分支

...and because the engineer does this as little as they can, they can't remember the "magic incantation" to do each step. Wrong switches and URLs happen, and suddenly they're in a mess and they go get the "expert".

......因为工程师尽可能少地这样做,他们无法记住每一步的“魔法咒语”。错误的开关和 URL 发生了,突然他们陷入混乱,他们去找“专家”。

Eventually it all settles down, and people learn how to deal with the shortcomings, but each new starter goes through the same problems. The eventual reality (as opposed to what I set out at he start) is:

最终一切都安定下来,人们学会了如何处理缺点,但每个新手都会遇到同样的问题。最终的现实(与我在他开始时提出的相反)是:

  • No work is done on trunk
  • Each developer has one major branch
  • Branches last until work needs to be released
  • Ticketed bug fixes tend to get their own branch
  • Merges back to trunk are done when authorized
  • 树干上没有工作
  • 每个开发者都有一个主要分支
  • 分支持续到需要发布工作
  • 有票的错误修复往往有自己的分支
  • 授权后合并回主干

...but...

...但...

  • Sometimes work makes it to trunk when it shouldn't because it's in the same branch as something else.
  • People avoid all merging (even easy stuff), so people often work in their own little bubbles
  • Big merges tend to occur, and cause a limited amount of chaos.
  • 有时工作会在不应该的时候进入主干,因为它和其他东西在同一个分支。
  • 人们避免所有的合并(即使是简单的事情),所以人们经常在自己的小气泡中工作
  • 大合并往往会发生,并导致有限的混乱。

Thankfully the team is small enough to cope, but it wouldn't scale. Thing is, none of this is a problem with CVCS, but more that because merges aren't as important as in DVCS they're not as slick. That "merge friction" causes behaviour which means that a "Feature Branch" model starts to break down. Good merges need to be a feature of all VCS, not just DVCS.

值得庆幸的是,该团队足够小,可以应付,但无法扩展。事实是,这对 CVCS 来说都不是问题,但更重要的是,因为合并不像在 DVCS 中那么重要,所以它们并不那么光滑。“合并摩擦”会导致行为,这意味着“功能分支”模型开始崩溃。良好的合并需要成为所有 VCS 的一个特性,而不仅仅是 DVCS。



According to thisthere's now a --record-onlyswitch that could be used to solve the --reintegrateproblem, and apparentlyv1.8 chooses when to do a reintegrate automatically, and it doesn't cause the branch to be dead afterwards

根据this现在有一个--record-only可以用来解决--reintegrate问题的开关,显然v1.8选择何时自动重新集成,并且不会导致分支之后死亡

回答by Tomislav Nakic-Alfirevic

Prior to subversion 1.5 (if I'm not mistaken), subversion had a significant dissadvantage in that it would not remember merge history.

在 subversion 1.5 之前(如果我没记错的话),subversion 有一个明显的缺点,它不会记住合并历史。

Let's look at the case outlined by VonC:

让我们看一下 VonC 概述的案例:

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - A - x (v2-only)
           \
             x - B - x (wss)

Notice revisions A and B. Say you merged changes from revision A on the "wss" branch to the "v2-only" branch at revision B (for whatever reason), but continued using both branches. If you tried to merge the two branches again using mercurial, it would only merge changes after revisions A and B. With subversion, you'd have to merge everything, as if you didn't do a merge before.

注意修订版 A 和 B。假设您将“wss”分支上的修订版 A 的更改合并到修订版 B 上的“仅 v2”分支(无论出于何种原因),但继续使用这两个分支。如果您尝试使用 mercurial 再次合并这两个分支,它只会合并修订版 A 和 B 之后的更改。使用 subversion,您必须合并所有内容,就好像您之前没有进行过合并一样。

This is an example from my own experience, where merging from B to A took several hours due to the volume of code: that would have been a real pain to go through again, which would have been the case with subversion pre-1.5.

这是我个人经验中的一个例子,由于代码量,从 B 合并到 A 需要几个小时:再次经历这将是一个真正的痛苦,在 subversion 1.5 之前的版本就是这种情况。

Another, probably more relevant difference in merge behaviour from Hginit: Subversion Re-education:

Hginit合并行为的另一个可能更相关的差异:Subversion Re-education

Imagine that you and I are working on some code, and we branch that code, and we each go off into our separate workspaces and make lots and lots of changes to that code separately, so they have diverged quite a bit.

When we have to merge, Subversion tries to look at both revisions—my modified code, and your modified code—and it tries to guess how to smash them together in one big unholy mess. It usually fails, producing pages and pages of “merge conflicts” that aren't really conflicts, simply places where Subversion failed to figure out what we did.

By contrast, while we were working separately in Mercurial, Mercurial was busy keeping a series of changesets. And so, when we want to merge our code together, Mercurial actually has a whole lot more information: it knows what each of us changed and can reapply those changes, rather than just looking at the final product and trying to guess how to put it together.

想象一下,你和我正在处理一些代码,我们对这些代码进行分支,我们每个人都进入各自的工作区,分别对该代码进行了大量的更改,所以他们已经有了很大的分歧。

当我们必须合并时,Subversion 会尝试查看两个修订版——我修改过的代码和你修改过的代码——并尝试猜测如何将它们粉碎成一个巨大的邪恶混乱。它通常会失败,产生一页又一页的“合并冲突”,这并不是真正的冲突,只是 Subversion 无法弄清楚我们做了什么的地方。

相比之下,当我们在 Mercurial 中单独工作时,Mercurial 忙于维护一系列变更集。因此,当我们想要合并我们的代码时,Mercurial 实际上有更多的信息:它知道我们每个人更改了什么并且可以重新应用这些更改,而不是仅仅查看最终产品并试图猜测如何放置它一起。

In short, Mercurial's way of analyzing differences is (was?) superior to subversion's.

简而言之,Mercurial 分析差异的方式(曾经?)优于 subversion 的方式。