views:

748

answers:

1

Recently I switched from SVN to Mercurial. Now I wonder how to realize my intended branching work flow in Mercurial according to good practice, hoping other developers understand what happens in the repository.

This is the work flow:

  1. Usually I have a trunk/default branch where work on the current release series happens. Let's say that is 1.x. At the same time I use a branch 2.x to work on the next major release. Changes in this branch may be radical, so merging with the trunk/default/1.x branch makes no sense here.
  2. After a while work on 2.x may be finished and version 2.0 gets released. Now I want the 2.x branch to be the new default/trunk branch and the current default/trunk to be the 1.x branch.
  3. Repeating this process, there may come a new 3.x branch. As before, if 3.0 gets released, 3.x should become the new default branch while the then current default should become the 2.x branch (again).

May question is not whether this work flow is a good one (I guess it's not fundamentally wrong). My questions is whether the way I realize this in Mercurial can be seen as good practice or if there are better opportunities.

So here is how I plan to manage branches in Mercurial ...

Starting from a repository with a single branch which holds the code of the current release series 1.x:

$ hg init
$ echo "hello world" > file1.txt
$ hg ci -A -m "Initial commit of 1.x code"

Start working on release 2.x:

$ hg branch 2.x
$ hg ci -m "Create new branch for 2.x development"
$ echo "Big new feature for 2.x" > file2.txt
$ hg ci -A -m "Add big new feature"

Meanwhile, do some work in current release series (1.x):

$ hg up default
$ echo "Minor adjustments specific for 1.x" > file3.txt
$ hg ci -A -m "Minor adjustments"

After some time release 2.0 is ready, yippee! Make default branch to 1.x and 2.x to default:

$ hg up default
$ hg branch 1.x
$ hg ci -m "Make default branch to 1.x branch"
$ hg up 2.x
$ hg ci --close-branch -m "Close branch 2.x"
$ hg branch --force default
$ hg ci -m "Make former 2.x branch to new default"

Now create a new branch 3.x and work in it, also work on default. Again, after some time 3.0 is ready and it's time again to manage branch names:

$ hg up default
$ hg branch --force 2.x # (reuse previously closed 2.x branch name)
$ hg ci -m "Make default branch to 2.x branch"
$ hg up 3.x
$ hg ci --close-branch -m "Close branch 3.x"
$ hg branch --force default
$ hg ci -m "Make former 3.x branch to new default"

The repo now may look like this ('o' are the heads):

o Branch default (3.x)
|
| o Branch 2.x
 \|
  | o Branch 1.x
   \|
    |
    .

The main point I'm not sure about is if reusing branch names and juggling with the branch name default is good practice.

A lot of text for that question - sorry - but I wanted to be clear about what I'm doing.

+12  A: 

Here's what I'd do:

Make default your "mainline" branch. The tip of this branch is the "currently released to the public" version of your code. Critical bugfixes can be committed directly to this branch and merged into development branches.

To start working on version 2.0, make a 2.0-dev branch. Commit changes for 2.0 to that branch, and merge critical bugfixes from the mainline (default) into it. Once you're done with 2.0, merge 2.0-dev into default and tag the result as 2.0.

Doing things this way means you don't have to worry about juggling branch names, and you can merge critical bugfixes to the mainline into development branches quite easily.

It also scales well when you're working on multiple future versions at once (say 2.1 and 3.0). You can periodically merge the 2.1 changes into 3.0 to keep 3.0 current.

You'll end up with a graph like this:

$ hg glog -l 1000
@       changeset:  25:efc0096f47c0  tip
|       summary:    Added tag 3.0 for changeset d1a7fc3d7d77
|
o       changeset:  24:d1a7fc3d7d77  3.0
|\      summary:    Merge in the redesign changes.
| |
| o     changeset:  23:b5b69d24c8f7 3.0-dev
| |     summary:    Finish 3.0 redesign.
| |
| o     changeset:  22:4c2f98fac54b 3.0-dev
|/|     summary:    Merge in the latest changes to 2.1/mainline.
| |
o |     changeset:  21:37df04521032
| |     summary:    Added tag 2.1 for changeset 39ecc520fc0a
| |
o |     changeset:  20:39ecc520fc0a  2.1
|\ \    summary:    2.1 development is done.
| | |
| o |   changeset:  19:208f3f9236af 2.1-dev
| | |   summary:    Finish the 2.1 work.
| | |
| | o   changeset:  18:4a024009a9d6 3.0-dev
| | |   summary:    More redesign work.
| | |
| | o   changeset:  17:00c416888c25 3.0-dev
| |/|   summary:    Merge in changes from the 2.1 branch to keep the redesign current.
| | |
| o |   changeset:  16:a57e781a0db1 2.1-dev
| | |   summary:    More 2.1 work.
| | |
| | o   changeset:  15:ddeb65402a61 3.0-dev
| | |   summary:    More redesign work.
| | |
+---o   changeset:  14:90f5d7a8af9a 3.0-dev
| | |   summary:    Merge in the fire fixes.
| | |
| o |   changeset:  13:78a949b67bb9 2.1-dev
|/| |   summary:    Merge in the fire fixes.
| | |
o | |   changeset:  12:6dfe9d856202
| | |   summary:    Oh no everything is on fire, fix it in the mainline.
| | |
| o |   changeset:  11:86767671dcdb 2.1-dev
| | |   summary:    Smaller changes for 2.1.
| | |
| | o   changeset:  10:25dec81d2546 3.0-dev
| | |   summary:    Work more on the redesign.
| | |
+---o   changeset:  9:42c7d689fb24 3.0-dev
| |     summary:    Start working on a complete redesign.
| |
| o     changeset:  8:3da99186ca7d 2.1-dev
|/      summary:    Start working on 2.1.
|
o       changeset:  7:9ba79361827d
|       summary:    Added tag 2.0 for changeset 755ed5c5e291
|
o       changeset:  6:755ed5c5e291  2.0
|\      summary:    Merge in the dev branch for 2.0.
| |
| o     changeset:  5:44a833fcc838 2.0-dev
| |     summary:    Finish work on 2.0.
| |
| o     changeset:  4:d7ba6aae1651 2.0-dev
|/|     summary:    Merge in the critical fix.
| |
o |     changeset:  3:968049f1b33a
| |     summary:    Fix a critical bug on the main branch.
| |
| o     changeset:  2:917869609b25 2.0-dev
| |     summary:    More work on the new version.
| |
| o     changeset:  1:f95798b9cb2e 2.0-dev
|/      summary:    Start working on version 2.0.
|
o       changeset:  0:8a3fb044d3f4
        summary:    Initial commit.
Steve Losh
I heard of this workflow as the recommended one but I was unsure how well it applies if there are several changes in the mainline which do not make sense for a devel branch. I guess it comes down to how I merge the latest mainline changes into the devel branch. How do I handle unwanted changes from the mainline? Is it possible to express something like "Merge changeset 23 and 27 from mainline but ignore all other changesets (or undo them after merging)"?
Oben Sonne
If you want to merge 23 and 27 and ignore the others, you want the Transplant extension: http://mercurial.selenic.com/wiki/TransplantExtension If you want to merge everything and undo 23 and 27 you would merge normally and then `hg backout 23 --merge; hg backout 27 --merge` while on the dev branch.
Steve Losh
I think the `backout` command is what fits best for my purpose. I've tested it and it does what I want to do. It seems to perform well when the are only a couple of changes to backout. Otherwise, having a lot of unwanted changes, it would bloat the history graph and would mean a lot of manual work .. but as long as this is not the case I'm totally happy with your suggestion :) Thanks!
Oben Sonne
Steve Losh
Yep, I guess the way to prevent merging nightmares is to *merge early, merge often*. That way it should be no problem to merge a dev branch into the mainline again at some point. Consequently keeping branches separate as described in my question and juggling with branch names to get a new mainline is not necessary. Thanks a lot, this discussion really helped me to get a good mental model of merging and branching in DVCS.
Oben Sonne
I'm glad it helped. If you want a more Mercurial-oriented guide to DVCS branching, you might like this post I wrote: http://stevelosh.com/blog/entry/2009/8/30/a-guide-to-branching-in-mercurial/
Steve Losh