svnmerge helps to block some changesets from a specific branch. How can this be achieved with Mercurial?
What subversion calls a merge is pretty different to what a merge is in Mercurial. The operation that svnmerge or svn merge
does is usually referred to as a "cherry pick" in other version control systems. Basically it creates a new commit that is a copy of the original changeset in the target branch. In Mercurial that can be done using the transplant extension.
This method is less popular in DVCS compared to regular merging as it creates multiple heads, which are harder to maintain. Here's how you would use it and what the resulting output would look like:
$ hg checkout -r 8
$ hg branch release
$ hg transplant 10
applying 8ba2867cf974
8ba2867cf974 transplanted to 1f5920f61c94
7---8---9---A [default]
\
B [release]
Here you have your commit A that fixes the bug on trunk and you hg transplant
it to release, creating a new commit B that contains the same changes as A, but with different parents.
The alternative approach is to not use transplant and check in only fixes to the release branch. You would create a new release branch and make the fix there, then you would merge the release branch into default. That would look like this:
$ hg checkout -r 8
$ hg branch release
... fix fix fix ...
$ hg commit -m"Make fix A"
$ hg checkout -r 9
$ hg merge release
1 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
$ hg commit -m"Merged release branch"
7---8---9---B- [default]
\ /
A---- [release]
Here B is a merge commit. It has no "extra" changes associated with it (unless there were conflicts that were resolved) and has the special property of marking the release branch as merged into default at that point. There's only one actual changeset that fixes the bug - the one marked "A" - although of course the changes themselves are in the default branch too.
The advantage of this approach are:
- only have one head. This is less confusing for developers and gives a nice sense of closure
- you can see at a glance that default contains all the bug fixes from release
- you can see release has that important bug fix
Normally you'd tag the release branch "v1.1" or whatever at the appropriate point so you know where that release came from.
The disadvantages with this approach are when:
- release is so old that merging to default causes conflicts
- changes on release that you didn't want to merge into default mixed in with changes that you did want
There is not much you can do with the first issue - if you have to maintain a branch that is very old, then you will end up with diverging branches anyway. The patches are probably very different anyway.
The second problem occurs if for example you have an "emergency" patch to the release version that papers over or works around a bug, but requires a larger fix on default to address the underlying problem. In that case, you would have to merge release and then create a new explicit commit to "undo" the commit you didn't want.
This practice actually has similar advantages with svn merge
in a way. If you merge changes selectively from trunk to release, you have to remember the revisions that you merged (assuming you aren't merging all of trunk). svn merge
sets the svn:mergeinfo properties, but tracking these can cause problems, plus you have to check the logs carefully to say "oh, yeah, I did merge that fix into release".
If you svn-merge the whole release branch to trunk when you have a fix, there is no need to remember what revisions have been merged.