Defining and manipulating a WIP feature branch

Andrew Halberstadt ahalberstadt at mozilla.com
Fri Feb 27 21:46:39 UTC 2015


Hi,

I recently switched from mq to a multi-headed development workflow. The 
new workflow is much better, but there were still some things missing, 
namely the concept of a "feature branch" (like a git-style branch). 
Bookmarks come close, but it is difficult to guess what they are based 
on, there is no concept of "ownership" from a commit to a bookmark.  The 
revset "only(<bookmark>) and not public()" comes pretty close, but 
doesn't quite get there. I want to be able to rebase bookmarks on top of 
one another, and have a convenient shortcut for expanding a bookmark 
label to the full revset of commits within that bookmark.

E.g, with:

o rev: 5
| bookmark: C
|
o rev: 4
|
o rev: 3
| bookmark: B
|
o rev: 2
|
o rev: 1
| bookmark: A
|
o rev: 0

I want "hg log -r B" to return 2:3. I came up with an informal 
definition for a WIP feature.  A commit /C/ is "within" a WIP feature 
branch ending at revision /R/ if:

 1. /C /is/R/ or /C/ is an ancestor of /R/
 2. /C/ is not public
 3. /C/ is not a merge commit
 4. no bookmarks exist in [/C/, /R/) for /C/ != /R/

There's no requirement that /R/ be a head, nor any requirement that /R/ 
have a bookmark. This definition can be implemented by a somewhat 
complicated revset (thanks to smacleod for coming up with it):

     [revsetalias]
     feature($1) = ($1 or (ancestors($1) and not (excludemarks($1) or hg ancestors(excludemarks($1))))) and not public() and not merge()
     excludemarks($1) = ancestors(parents($1)) and bookmark()

This lets you manipulate a feature* as a series of commits:
     hg log -r 'feature(B)'

That revset is a bit tricky to grok, and it requires some extra typing, 
so I wrote an extension:
https://bitbucket.org/halbersa/bookbinder

Bookbinder does two things:

 1. It defines an actual 'feature' revset (not an alias)
 2. It wraps (almost) all commands with a --rev argument. If --rev
    <bookmark> is detected, <bookmark> is replaced with
    'feature(<bookmark>)'. It is still possible to treat the bookmark as
    a label by escaping it with a period.

Bookbinder makes it really convenient to do things like:
     hg log -r <bookmark>
     hg fold -r <bookmark>
     hg rebase -r <bookmark> -d <dest>
     hg graft -r <bookmark>
     etc..

I'd like to get feedback on this approach. Is the definition sane? Is 
the extension sane? Would others find it useful? What things could be 
done in mercurial core to make the situation better? I'm pretty new to 
the world of mercurial extensions/development, so please bear with me as 
I figure out what is or isn't a good approach.

Cheers,
Andrew

* I'm not too happy with the name feature, but couldn't think of 
anything better.. suggestions welcome!
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://selenic.com/pipermail/mercurial/attachments/20150227/f4c6349f/attachment.html>


More information about the Mercurial mailing list