Local Branches
Named branches are nice for long-lived branches (eg stable vs development). But sometimes you want to create short-lived branches, perhaps to develop a feature. Once the code has matured to the point where it is ready for mainline, the history of its development is often just uninteresting clutter. The usual answer is to use Mercurial's nice lightweight clones. But even these require duplication of the working directory for each branch, and often other setup work (configure runs etc).
Local branches are clones that live within a repository, allowing you to share one working directory across several branches, but then to be able to drop those branches trivially when you are done with them.
Mechanism:
- local branches are clones of .hg/store, residing under .hg/branches.
- hg lbranch foo will clone the current repo to foo (or switch to an existing clone of that name) and write the branch name to .hg/localbranch
- hg lbranch without arguments displays the local branches, with a '*' by the currently checked-out branch
hg lbranch -d <foo> will delete the local branch foo
- hg pull/push/incoming/outgoing resolve local branch names to the appropriate subdirectory. You can prefix the target with lbranch:// in case of ambiguity.
An experimental extension is here: http://hg.kublai.com/mercurial/extensions/localbranch
Interesting conversation
So, on 2007-06-07 at 19:02 UTC -0700 BrendanCully and I (EricHopper aka Omnifarious) had this interesting conversation on IRC #mercurial:
| 19:02 | Omnifarious | I'm wondering if anybody has a good reason not to push my change to crew. :-) |
| 19:23 | brendan | I personally just haven't thought enough about it. I have this bad feeling there's a nicer way to do this... |
| 19:51 | Omnifarious | So, from a UI perspective you might well be right. |
| 19:51 | Omnifarious | I suspect you are. I think the way the command line options here are organized isn't really the best that could be done. |
| 19:51 | Omnifarious | But, from an internal mechanics perspective, I don't think there's a better way to do it. |
| 19:52 | Omnifarious | I actually find the whole branch idea rather suspect. Mostly because I think a branch is a piece of repository level information that does not belong indelibly recorded in a revision. |
| 19:53 | Omnifarious | I think the fast-forward merge problem is actually a really great illustration of the disconnect here. |
| 19:56 | brendan | yes, that's tricky. I am hoping the localbranch stuff will help |
| 19:57 | brendan | something I haven't put my finger on bothers me about hg's named branches too |
| 19:57 | Omnifarious | I think sometimes though that people have to have something to play with for awhile to know what they really want. |
| 19:57 | brendan | I think it can work fine for 'stable' vs 'devel' though |
| 20:01 | brendan | git's branches are nice in that they're throwaway, but I don't think it's very good about exporting things. I recall a lot of manually set up branch maps |
| 20:01 | brendan | and there seems to be a bit too much rebasing |
| 20:01 | Omnifarious | Mercurial in general has shyed away from copying repository meta-data between repositories. |
| 20:02 | Omnifarious | I think that branches are repository meta-data, not data about revisions. And I think it needs to be copied form repo to repo by pull, push, clone and other such utilities. |
| 20:03 | Omnifarious | I think a branch is mostly a label you've applied to a set of heads. |
| 20:03 | brendan | I don't think they're much different from tags, which are propagated |
| 20:04 | Omnifarious | Yes and no. |
| 20:04 | brendan | I don't know about this 'active branch' idea though. I think making active == 'has heads' is probably wrong. |
| 20:04 | Omnifarious | Hmm... |
| 20:05 | brendan | I suspect we may need to mark them manually |
| 20:06 | Omnifarious | So, here is why I think branches are different than tags.... |
| 20:06 | Omnifarious | Tags are a fact about a particular revision. |
| 20:07 | Omnifarious | Once that fact is recorded, it doesn't change. |
| 20:07 | Omnifarious | A branch is something that's about a group of revisions and future revisions. |
| 20:07 | brendan | we do have some logic for tag movement though |
| 20:08 | Omnifarious | Yes, but it's not like the tag is ever applied to two revisions at once. |
| 20:08 | brendan | hmm |
| 20:09 | Omnifarious | I don't know... do people ever have a 'latest stable release' tag that just gets updated all the time instead of labeling each stable release with a different tag? |
| 20:10 | Omnifarious | Even then that feels fundamentally different from a branch which seems a much more distributed and amorphous idea. |
| 20:10 | brendan | don't know. seems plausible though |
| 20:11 | brendan | well, a branch is basically a distinct changelog, but we need a way to make them live together |
| 20:18 | Omnifarious | I sort of think of a branch as being a little piece of meta-data about a particular repo. |
| 20:19 | Omnifarious | It's like "Hey, when we put stuff here, it's for this." |
| 20:21 | Omnifarious | And if you pull from a repo that isn't for that purpose, those changes should be invisible by default in your repo. |
| 20:21 | Omnifarious | Hmmm... |
| 20:22 | Omnifarious | So, here's a concrete idea... |
| 20:22 | Omnifarious | There is no data about branches in any individual revision. |
| 20:23 | Omnifarious | It exists solely in the branch cache, which can be propogated between repos. |
| 20:23 | brendan | this recalls the old idea of having a .hgbranches file |
| 20:24 | Omnifarious | But this branch cache is not version controlled. |
| 20:24 | Omnifarious | A repo updates it based on a set of distinct rules when a new version is committed or a revision is pulled. |
| 20:24 | brendan | hmm. you might get some pushback about that. |
| 20:25 | brendan | I remember mpm being of the opinion that propagation in hg was best done through the version control mechanism |
| 20:25 | Omnifarious | Each branch cache entry consists of a branch name and a set of revisions that are considered heads. |
| 20:25 | brendan | the mq nested repo approach might work around that though |
| 20:26 | Omnifarious | If a new cache entry is recieved for a particular branch it removes any cache entries for revisions that are ancestors of the new entry. |
| 20:26 | Omnifarious | You also have a 'working branch' recorded like the dirstate. |
| 20:27 | Omnifarious | And by default all commands only show you data about the 'working branch' if one is recorded. |
| 20:27 | brendan | sounds like you're replacing the heads algorithm |
| 20:27 | brendan | also sounds like it's going to look like localbranches |
| 20:27 | brendan | !localbranches |
| 20:27 | hgbot | brendan: Error: "localbranches" is not a valid command. |
| 20:27 | brendan | !whatis localbranch |
| 20:27 | hgbot | brendan: "localbranch" could be http://www.selenic.com/mercurial/wiki/index.cgi/LocalBranches |
| 20:29 | Omnifarious | Yes, it's very much like that idea. Except with the added feature of having them propogate between repositories. |
| 20:29 | brendan | yes, that's a bit I wanted to add in |
| 20:30 | Omnifarious | I think all branches are 'short-lived'. You might have a recurring development branch that forks off, but it always comes back to the stable branch. |
| 20:31 | codemac | I think that is a massive assumption about how others see their branches |
| 20:31 | brendan | otoh I think short-lived branches can be very handy |
| 20:32 | brendan | would be nice to support them. I've got some time to myself this weekend and I hope I can take another crack at the localbranch queue |
| 20:32 | brendan | I think transient branches are the biggest thing git users miss in hg |
| 20:33 | brendan | transient branch by cloning can be a bit annoying |
| 20:33 | Omnifarious | codemac: Well, I think this even accomodates long-term branches. |
| 20:33 | Omnifarious | brendan: I've been meaning to re-write the little contrib thingy that relinks files that are identical. |
| 20:33 | codemac | Omnifarious: I haven't checked out your code yet, I was just reacting to your statement :P |
| 20:34 | brendan | Omnifarious: ok. it was a pretty quick hack |
| 20:34 | Omnifarious | codemac: Oh, my code is based on the current branch model in which branches are forever and can never be forgotten. |
| 20:34 | Omnifarious | brendan: *nod* I've been meaning to change it to determine if two files are identical by comparing head lists. :-) |
| 20:35 | brendan | yep, that's probably a good idea |
| 20:35 | brendan | I think it already only compares indices though |
| 20:35 | Omnifarious | I have to disappear in a minute or two. |
| 20:35 | Omnifarious | Oh, well that's a good thing. |
| 20:36 | Omnifarious | Does it do a repo write lock on both repos while it's running? |
| 20:36 | brendan | no |
| 20:36 | brendan | it started out as a generic relinker |
| 20:37 | brendan | anyway, you're welcome to make it smarter :) |
| 20:37 | Omnifarious | It would reduce the annoyance of lots of clones. :-) |
| 20:37 | brendan | would be cool if it could do more than pairs :) |
