Mercurial Repository structure for features, stable releases, etc
I will be more specific with a question if I need to be, or make this into a community wiki if you all think that's where it fits, but my question is:
My dev team has recently started using Mercurial (moved from subversion) and we love it so far. I'm wondering if there is a 'best practices' resource out there about repository architecture. What I'm interested in is, what is the best way to leave a stable copy of the repo (for shipping/emergency bug fixes) while working on features and new versions. I have read a lot about named branches vs. cloned repositories and I'm hoping some of you hg guys can shed some light on what works for your team.
What is easier to merge after a feature has been tested and is ready for the next release? Are there any serious drawbacks to the 2 methods I have mentioned? Are there other repo management strategies out there?
We are nearing the deployment of our 2.0.0 release and I'm looking to start fresh once it is out with a new way of working with hg.
Let me rephrase to hit on some basics that I'm still struggling with - Let's say I finish 2.0.0 tomorrow... I wan开发者_如何学Ct to start work on 2.1.0, what do I do? Clone my repo, name it "working/projects/widgets2.1" and keep rolling along, having my "workin/projects/widgets2.0" sitting there ready to be used in bug fixing situations?
Further, if the customer calls and says there is a bug and the widget machine is shaking and smoke is starting to billow, do I pop open widgets2.0, fix the bug, deploy to server, then commit/push? Do I then go back over to widgets2.1 and pull/merge that bug fix?
As far as I can tell, what you're asking about is how to handle different branches of development in Mercurial. In your example, you want to easily be able to release fixes to the release version without having to deal with all the stuff that has happened in the development branch since the last release.
There are many ways to handle branches in Mercurial. You can use separate repositories, named branches and the Bookmarks extension among others. How you choose to handle branches in Mercurial is related to the type of workflow you have but there are many possible combinations.
With that in mind, I will give some general suggestions for workflow between branches, regardless of how they're represented in Mercurial (as separate repositories, named branches etc). If you want to learn more about what branching model to choose I suggest you read Steve Losh's guide to branching in Mercurial and my blog post on choosing a branching model in Mercurial.
First of all, even with no branches at all, you can still always go back to an earlier revision of the code, e.g. the 2.0 release, and fix the bug there. This will make it easy to tag and release a new version (say 2.0.1) with the only change being the bug fix.
You do this by simply updating, fixing and committing:
$ hg update 2.0
hack hack hack
$ hg commit -m "Fix nasty bug"
$ hg tag 2.0.1
The above assumes you have tagged the 2.0 revision so it's easy to get at it, otherwise you will have to supply a hash or revision id instead.
This will give you two heads, which you can merge with hg merge
, bringing the fix back into the development version.
If you have a separate repository for the 2.0 release, you make the fix there and then pull it into the development repository where you then merge. The basic principle is the one Ry4an outlined, you make the change where there isn't already a bunch of other changes made that you don't want.
Here's an example:
Where I work we have many repositories representing different branches. Most development happens in the "dev" branch. When a release nears, we clone that repository into a new one called say "release-2.4". In this branch/repo we test and fix bugs for the upcoming release. More experimental development that won't be ready until the next release can happen in "dev" in parallel.
When the release is tested and ready to go out, we pull everything from "release-2.4" into "prod", which only contains released versions of code. We tag it with a version number and release it to the world. We can then delete the "release-2.4" branch, and pull everything from "prod" into "dev". This might require a merge, but then we have all the changes made during the release process back in "dev" and can continue working on the next release.
If we want to fix a bug outside the bigger planned releases, we can do it a couple of ways. If the fix is small (a couple of commits) or doesn't involve many developers, we can just commit directly to "prod", tag the release and the ship it. After that we pull from "prod" into "dev" to make sure the fix is there in the next release as well.
If the bug fix release is larger and will take more time, we can instead make a new branch off "prod", do all our work on the release there, and then pull the changes into "prod". When it's released into "prod", we can then "pull" from "prod" into "dev" to get the changes there. The special release branch can then be deleted.
The piece of advice I wish I'd heard earlier on is to "fix the bug as early as possible", and I don't mean right after you code it. I mean that if you're fixing a bug that introduced in changeset number 400, two years ago, you should do:
hg update 400
vi .... # fix bug
hg commit
Mercurial will say "new head created", which seems alarming at first, but what you've done is create a changeset (an anonymous branch actually) that can be hg pull
ed into any branch that has the bug.
Before I figured that out, we'd fix the bug in the release branch, or in a development branch, or on some other active line of development, and then we'd want to move that fix to other branches, and couldn't do it well. The reason being when you pull (branches as clones) or merge (named or anonymous branches) there's the firm requirement that if you're pulling/merging changeset X then you're pulling/merging all the ancestors of changeset X -- but you don't necessarily want all of those ancestors (which might be new, experimental features) you just want the bug fix.
Moving a change without its ancestors requires "cherry picking" of one form of another, either via import/export or transplant or some other out-of-band mechanism.
If, however, you make your bugfix changesets such that their only ancestor is the changeset in which the bug was first created then you can always 'hg pull' that fix into any branch that has the bug without bringing anything else with you.
Just to bring it back to your original query a little more, what I'm suggesting above is equally applicable if you're using clones as branches (my preference) or named branches.
Here's one nice "structure" tutorial:
http://hginit.com/05.html
I too started using Mercurial after being quite familiar with Subversion, and I'm quite happy with the switch -- hg definitely solved some problems for me. I basically don't bother with any of the "named branches" and just rely on the heads to do my work. I make sure that I use the log adequately to figure out where I need to go when I need to go back to an older revision. Of course, tags are really good too, and the tutorial covers that.
The company I work for is entertaining migrating to Mercurial and thinking about supporting 'feature branches' for each feature being developed. A feature branch is a named branch that contains either some new development, or updates to an existing feature - basically, whatever you want.
The feature branches exist only in a user's repo clone until they are ready to be integrated into the main line of development at which point they get merged back into the active development branch. If you have someone in a build manager role, the feature branches can be pushed to a shared repo un-merged and the manager can merge them as needed. If you do not, the developers can merge them in their cloned repos and then push the already merged branch. (Side note: Hg will still show the merged feature branch in the shared repo)
Once the shared repo has been updated to the point of stability, it can be tagged and released. Bugfixes can be applied to the tagged version, and if prolonged or parallel development is required, a second active development branch can be created with the tagged version as the parent. Ry4an's method for bugfixing can be applied here as well.
This whole workflow can be duplicated using feature clones instead of feature branches, but it changes some of the development dynamics. If you can afford to give each a try for a release, that should help you and your team decide.
edit to address your newly added specific question
Let's say I finish 2.0.0 tomorrow... I want to start work on 2.1.0, what do I do?
In this scheme you would make a tag when 2.0.0 is completed and then continue work on 2.1.0 on the default branch. Subsequent commits to the default branch and any new feature branches starting from this point on will support 2.1.0.
Further, if the customer calls and says there is a bug and the widget machine is shaking and smoke is starting to billow, do I pop open widgets2.0, fix the bug, deploy to server, then commit/push?
Bugfixes for 2.0.0 would be made by updating to the 2.0.0 changeset (ie: the tag) and then creating a new named branch (maybe called version2.0.0). This branch is never merged wholly into the default branch which has now moved on to 2.1.0 development, and this branch will likely remain open indefinitely. It will contain any 2.0.0-based bugfixes.
Once you have made a 2.0.0 bugfix, there are several ways you can move a specific chagneset from 2.0.0 to the default (2.1.0) branch. You can create a patch of a specific changeset and apply it to the default branch, or you can perform a merge from version2.0.0 to default if it makes sense.
精彩评论