开发者

When SVN fails to merge and Mercurial succeeds

Before it comes up, I've already looked at several threads on this topic, including these:

  • Merging: Hg/Git vs. SVN

  • Mercurial compared to private branches in SVN

  • Why is branching and merging easier in Mercurial than in Subversion?

I'm looking at switching to Mercurial (from Subversion) for our development group. Before I do so I'm doing the usual pros / cons list.

One of my "pros" is that merging is superior in Mercurial, but so far I'm failing to find compelling evidence of that. Namely, there is a statement made on HgInit.com that I've been unable to verify:

For example, if I change a function a little bit, and then move it somewhere else, Subversion doesn’t really remember those steps, so when it comes time to merge, it might think that a new function just showed up out of the blue. Whereas Mercurial will remember those things separately: function changed, function moved, which means that if you also changed that function a little bit, it is much more likely that Mercurial will successfully merge our changes.

This would be extremely compelling functionality, but as far as I can tell, it's just hot air. I've been unable to verify the above statement.

I created a Mercurial repository, did a Hello World and then cloned it. In one I modified the function, committed it and then moved it, and then committed it. In the other I simply added another output line in the function and committed.

When I merge, I get basically the same merge conflict I would get using Subversion.

I get that mercurial can track file renames better and that there are other advantages to DVCS besides merging, but I'm interested in this example. Is Joel Spolsky off-base here, or am I missing something?

开发者_JAVA技巧I'm not experienced in this area, but it does seem like since Mercurial keeps more information that it could, in theory, do better at merging (if developers also did frequent checkins). For example, I see it as feasible for Mercurial to get contextual changes from comparing multiple changes, e.g., I modify a function, check in, move the function, check in, and Mercurial associates those two pieces.

However, Mercurial's merging doesn't seem to actually take advantage of the added information, and appears to be operate the same way as Subversion. Is this correct?


As far as I know, anyone who says Mercurial tracks moving code around in less than file-sized chunks is just wrong. It does track file renames independently from code changes, so if Dave renames a file and Helen changes something in the file, it can automerge that, but as far as I know, Subversion can do that too! (CVS can't.)

But there is a way in which Mercurial's merge logic is dramatically better than Subversion's: it remembers conflict resolutions. Consider this history graph:

base ---> HELEN1 ---> merge1 --> HELEN2 -> merge2
     \--> DAVE1 ---/                    /
                   \--> DAVE2 ---------/

Helen and Dave made changes independently. Helen pulled Dave's tree and merged, then made another change on top of that. Meantime, Dave went on coding without bothering to pull from Helen. Then Helen pulled Dave's tree again. (Maybe Dave is working on the main development trunk, and Helen's off on a feature branch, but she wants to sync up with trunk changes periodically.) When constructing "merge2", Mercurial would remember all of the conflict resolutions done in "merge1" and only show Helen new conflicts, but Subversion would make Helen do the merge all over again from scratch. (There are ways you can avoid having to do that with Subversion, but they all involve extra manual steps. Mercurial handles it for you.)

For more information, read about the mark-merge algorithm which was developed for Monotone and AFAIK is now used by both Mercurial and Git.


AFAIK, SVN does all its merging internally - the merge tools are there only for cases where there's a conflict as (obviously) it needs to tell you about it and get you to fix it.

The non-conflicting cases are based around applying patches - ie, svn will take the changes you made in a revision, and will apply those to the target. Recent versions of SVN (since 1.5) remember the merges you did previously, storing this information in a property associated with the directory. 1.6 does a much better job of handling these properties compared to 1.5.

SVN does not merge by comparing 2 trees and diffing them - see the book - except when the trees to merge are not related, only then it'll perform a diff-type merge operation (or you specify the --ignore-ancestry option). Here's a brief description of what happens. You can see this when you merge past conflicts - once you've resolved a tricky revision merge, svn remembers which revisions were merged and will apply those changes again. you can prove this by trying it - branch, edit 2 files, merge to get a conflict. Edit the branched file only on the same line, then merge - it'll pop a conflict even though the target file hasn't changed, but because the merge is applying a change to a line that's been changed from what the branched file expected (ie just like patch which shows what it thinks the line its going to change should have been). In practice you don't see this as you don't tend to repeatedly reject your merge changes.

However, SVN does a poor job with renamed files as it tracks them as delete+add operations. Other SCMs do a better job - but even they cannot really tell if a file is renamed, or is deleted and added, especially when that file is modified as well. Git uses some heuristics to try and determine this, but I can't see it guarantees success. Until we have a SCM that hooks into the filesystem, I think this will remain the case.


Two things:

  1. Mercurial does have internal code to do merges and it will only call out to an external merge tool if the internal "pre-merge" fails.

    You link to the HG Book and it says that there is no built-in tool for handling conflicts (not the same as no built-in merge) and the Mercurial wiki where it is stated that Mercurial will try to do merges internally before calling external programs.

  2. You link to a question where my answer gives an explicit case where Mercurials succeeds and Subversion fails in a merge. This is an out-of-the-box comparison using the internal merge code in both Mercurial and Subversion.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜