You should do both.
Start with the accepted answer from @Norman: Use one repository with one named branch per release.
Then, have one clone per release branch for building and testing.
One key note is that even if you use multiple repositories, you should avoid using transplant
to move changesets between them because 1) it changes hash, and 2) it may introduce bugs that are very difficult to detect when there are conflicting changes between the changeset you transplant and the target branch. You want to do the usual merge instead (and without premerge: always visually inspect the merge), which will result in what @mg said at the end of his answer:
The graph might looks different, but it has the same structure and the end result is the same.
More verbosely, if you use multiple repositories, the "trunk" repository (or default, main, development, whatever) contains ALL changesets in ALL repositories. Each release/branch repository is simply one branch in the trunk, all merged back one way or the other back to trunk, until you want to leave an old release behind. Therefore, the only real difference between that main repo and the single repo in the named branch scheme is simply whether branches are named or not.
That should make it obvious why I said "start with one repo". That single repo is the only place you'll ever need to look for any changeset in any release. You can further tag changesets on the release branches for versioning. It's conceptually clear and simple, and makes system admin simpler, as it's the only thing that absolutely has to be available and recoverable all the time.
But then you still need to maintain one clone per branch/release that you need to build and test. It's trivial as you can hg clone <main repo>#<branch> <branch repo>
, and then hg pull
in the branch repo will only pull new changesets on that branch (plus ancestor changesets on earlier branches that were merged).
This setup best fits the linux kernel commit model of single puller (doesn't it feel good to act like Lord Linus. At our company we call the role integrator), as the main repo is the only thing that developers need to clone and the puller needs to pull into. Maintenance of the branch repos is purely for release management and can be completely automated. Developers never need to pull from/push to the branch repos.
Here is @mg's example recasted for this setup. Starting point:
[a] - [b]
Make a named branch for a release version, say "1.0", when you get to alpha release. Commit bug fixes on it:
[a] - [b] ------------------ [m1]
\ /
(1.0) - [x] - [y]
(1.0)
is not a real changeset since named branch does not exist until you commit. (You could make a trivial commit, such as adding a tag, to make sure named branches are properly created.)
The merge [m1]
is the key to this setup. Unlike a developer repository where there can be unlimited number of heads, you do NOT want to have multiple heads in your main repo (except for old, dead release branch as mentioned before). So whenever you have new changesets on release branches, you must merge them back to default branch (or a later release branch) immediately. This guarantees that any bug fix in one release is also included in all later releases.
In the meanwhile development on default branch continues toward the next release:
------- [c] - [d]
/
[a] - [b] ------------------ [m1]
\ /
(1.0) - [x] - [y]
And as usual, you need to merge the two heads on default branch:
------- [c] - [d] -------
/ \
[a] - [b] ------------------ [m1] - [m2]
\ /
(1.0) - [x] - [y]
And this is the 1.0 branch clone:
[a] - [b] - (1.0) - [x] - [y]
Now it's an exercise to add the next release branch. If it's 2.0 then it'll definitely branch off default. If it's 1.1 you can choose to branch off 1.0 or default. Regardless, any new changeset on 1.0 should be first merged to the next branch, then to default. This can be done automatically if there's no conflict, resulting in merely an empty merge.
I hope the example makes my earlier points clear. In summary, the advantages of this approach is:
- Single authoritative repository that contains complete changeset and version history.
- Clear and simplified release management.
- Clear and simplified workflow for developers and integrator.
- Facilitate workflow iterations (code reviews) and automation (automatic empty merge).
UPDATE hg itself does this: the main repo contains the default and stable branches, and the stable repo is the stable branch clone. It doesn't use versioned branch, though, as version tags along the stable branch are good enough for its release management purposes.