From my understanding SVN is 'Easy to branch. Difficult to merge'. Why is that? Is there a difference how they merge?
Please see my Stack Overflow answer for a very concrete situation where Mercurial (and Git) merges without problems and where Subversion presents you with a bogus conflict. The situation is a simple refactoring done on a branch where you rename some files.
With regard to tdammers answer, then there is a number of misunderstandings there:
Subversion, Mercurial, and Git all track repository-wide snapshots of the project. Calling them versions, revisions, or changesets makes no difference. They are all logically atomic snapshots of a set of files.
The size of your commits makes no difference when it comes to merging. All three systems merge with the standard three-way merge algorithm and the inputs to that algorithm are
- greatest common ancestor version
- version on one branch
- version on other branch
It doesn't matter how the two branch versions were created. You can have used 1000 small commits since the ancestor version, or you can have used 1 commit. All that matters is the final version of the files. (Yes, this is surprising! Yes, lots of DVCS guides get this horribly wrong.)
He also raises some good points about the differences:
Subversion has some "voodoo" where you can merge from
/branches/foo. Mercurial and Git does not use this model — branches are instead modeled directly in the history. The history therefore becomes a directed acyclic graph instead of being linear. This a much simpler model than the one used by Subversion and this cuts away a number of corner cases.
You can easily delay a merge or even let someone else handle it. If
hg mergegives you a ton of conflicts, then you can ask your coworker to
hg pullfrom you and then he has the exact same state. So he can
hg mergeand maybe he's better at resolving conflicts than you are.
This is very difficult with Subversion where you're required to update before you can commit. You cannot just ignore the changes on the server and keep committing on your own anonymous branch. In general, Subversion forces you to play around with a dirty working copy when you
svn update. This is kind of risky since you haven't stored your changes anywhere safe. Git and Mercurial lets you commit first, and then update and merge as necessary.
The real reason Git and Mercurial are better at merging than Subversion is a matter of implementation. There are rename conflicts that Subversion simply cannot handle even thought it's clear what the correct answer is. Mercurial and Git handles those easily. But there's no reason why Subversion couldn't handle those as well — being centralized is certainly not the reason.
The core problem lies in the way these systems represent a versioned directory structure.
Subversion's basic concept around which the whole system revolves is that of a version (or, in svn lingo, "revision"): a snapshot of a file at a certain point. As long as the history is perfectly linear, all is fine, but if you need to merge changes from two independent lines of development, svn has to compare the current versions of both, and then do a three-way comparison between the last shared version and the two head versions. Lines that appear changed in one of the heads, but not the other, can easily be resolved; lines that deviate exactly the same way in both heads are harder, but usually doable; lines that deviate in different ways are what makes svn say "I can't figure this out, human, please resolve this for me."
By contrast, git and mercurial track changesets rather than versions. The entire repository is a tree of changesets, each one depending on a parent, where a parent changeset can have any number of children, and the tree root represent an empty directory. In other words, git/hg says "first I had nothing, then this patch was applied, then that patch, etc.". When you need to merge two lines of development, git/hg not only knows what each head currently looks like, and what the last common version looked like, it also knows how the transition happened, allowing for much smarter merging.
Another thing that makes merging easier in a DVCS is an indirect consequence of separating the concepts of commit and push, and of allowing all sorts of cross-merges between any two clones of the same repository at any time. With svn, people tend to commit large changesets with often unrelated changes, because a commit is also an update on the central repository which affects all other team members; if you commit a broken version, everyone is going to be angry with you. Since most setups involve a networked svn server, committing also involves pumping data over the network, which means committing introduces a considerable delay to the workflow (especially when your working copy is outdated and you have to pull first). With git and mercurial, the commit happens locally, and because both are very efficient at handling local filesystems, it usually finishes instantly. As a result, people (once they get used to it) commit small incremental changes, and then when it works, push a dozen or so commits in one go. Then when merge time comes around, the SCM has much more detailed information to go by, and can do a better job resolving conflicts safely and automatically.
And then there's the nice details that make things even easier:
- You can have multiple heads and still commit on either; unlike subversion, you don't need to combine pull, update and merge before committing again - the multiple heads just stay that way until you choose to merge
- Directories are not treated specially; instead, the path is just considered one large filename, and all your directories have to be at the same revision at all times. This means you can't do the subversion voodoo where a project's subfolders are at different revisions, but it also means the working copy is less likely to become a huge unmanageable broken mess, and more interestingly, a move isn't represented as a delete-and-add (which would break entirely in svn if it weren't for the retrofit metadata), but simply as a rename; if you move a file, its entire history is preserved; merging can even apply changes to the moved file that were made to a non-moved version of the same file after the move, in another branch
- Most of the time, you actually don't even need to branch: instead, you just clone the entire repository. Cloning is cheap, especially if it's done on the same filesystem, and if you decide you want to get rid of the clone, you just delete the directory it lives in and that's that. You don't even need to use hg or git for that.
- There are few (if any) restrictions on what you can merge. You can have six clones of the same repository, and merge (or rather, push or pull; an explicit merge is often not required) from A to B, then C to B, then B to D, then C to D, B back to A, D to E, at any time and as often as you like.
- You can test a merge by cloning one of the repositories you want to merge, and then pulling into that from the other. If it does what you want, you can push back to the real target, if it doesn't, you throw away the clone and start afresh.