[prev in list] [next in list] [prev in thread] [next in thread] 

List:       mercurial
Subject:    Defining and manipulating a WIP feature branch
From:       Andrew Halberstadt <ahalberstadt () mozilla ! com>
Date:       2015-02-27 21:46:39
Message-ID: 54F0E5BF.1020304 () mozilla ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


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!


[Attachment #5 (text/html)]

<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=utf-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <p>Hi,<br>
    </p>
    <p>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(&lt;bookmark&gt;) 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.</p>
    E.g, with:<br>
    <br>
    o rev: 5
    <br>
    | bookmark: C
    <br>
    |
    <br>
    o rev: 4
    <br>
    |
    <br>
    o rev: 3
    <br>
    | bookmark: B
    <br>
    |
    <br>
    o rev: 2
    <br>
    |
    <br>
    o rev: 1
    <br>
    | bookmark: A
    <br>
    |
    <br>
    o rev: 0
    <br>
    <br>
    I want "hg log -r B" to return 2:3. I came up with an informal
    definition for a WIP feature.  A commit <i>C</i> is "within" a WIP
    feature branch ending at revision <i>R</i> if:<br>
    <ol>
      <li><i>C </i>is<i> R</i> or <i>C</i> is an ancestor of <i>R</i></li>
      <li><i>C</i> is not public</li>
      <li><i>C</i> is not a merge commit</li>
      <li>no bookmarks exist in [<i>C</i>, <i>R</i>) for <i>C</i> != <i>R</i><br>
      </li>
    </ol>
    There's no requirement that <i>R</i> be a head, nor any requirement
    that <i>R</i> have a bookmark. This definition can be implemented
    by a somewhat complicated revset (thanks to smacleod for coming up
    with it):<br>
    <pre wrap="">    [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()</pre>  This lets you manipulate a feature* as a \
series of commits:<br>  hg log -r 'feature(B)'<br>
    <br>
    That revset is a bit tricky to grok, and it requires some extra
    typing, so I wrote an extension:<br>
    <a class="moz-txt-link-freetext" \
href="https://bitbucket.org/halbersa/bookbinder">https://bitbucket.org/halbersa/bookbinder</a><br>
  <br>
    Bookbinder does two things:<br>
    <ol>
      <li>It defines an actual 'feature' revset (not an alias)<br>
      </li>
      <li>It wraps (almost) all commands with a --rev argument. If --rev
        &lt;bookmark&gt; is detected, &lt;bookmark&gt; is replaced with
        'feature(&lt;bookmark&gt;)'. It is still possible to treat the
        bookmark as a label by escaping it with a period.<br>
      </li>
    </ol>
    Bookbinder makes it really convenient to do things like:<br>
        hg log -r &lt;bookmark&gt;<br>
        hg fold -r &lt;bookmark&gt;<br>
        hg rebase -r &lt;bookmark&gt; -d &lt;dest&gt;<br>
        hg graft -r &lt;bookmark&gt;<br>
        etc..<br>
    <br>
    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.<br>
    <br>
    Cheers,<br>
    Andrew<br>
    <br>
    * I'm not too happy with the name feature, but couldn't think of
    anything better.. suggestions welcome!<br>
  </body>
</html>


[Attachment #6 (text/plain)]

_______________________________________________
Mercurial mailing list
Mercurial@selenic.com
http://selenic.com/mailman/listinfo/mercurial


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic