Submitter | Gregory Szorc |
---|---|
Date | June 17, 2017, 7:02 p.m. |
Message ID | <c9a3d8cdbb9256144192.1497726169@gps-mbp.local> |
Download | mbox | patch |
Permalink | /patch/21463/ |
State | Superseded |
Headers | show |
Comments
On Sat, Jun 17, 2017 at 01:02:49PM -0600, Gregory Szorc wrote: > # HG changeset patch > # User Gregory Szorc <gregory.szorc@gmail.com> > # Date 1497726059 21600 > # Sat Jun 17 13:00:59 2017 -0600 > # Node ID c9a3d8cdbb925614419216d6e99d9a70f03d926d > # Parent 9fcb6df413c9ca475e705ecc15df07584dadb0c1 > show: implement "stack" view > > People often want to know what they are working on *now*. As part of > this, they also commonly want to know how that work is related to other > changesets in the repo so they can perform common actions like rebase, > histedit, and merge. > > `hg show work` made headway into this space. However, it is geared > towards a complete repo view as opposed to just the current line of > work. If you have a lot of in-flight work or the repo has many heads, > the output can be overwhelming. The closest thing Mercurial has to > "show me the current thing I'm working on" that doesn't require custom > revsets is `hg qseries`. And this requires MQ, which completely changes > workflows and repository behavior and has horrible performance on large > repos. But as sub-optimal as MQ is, it does some things right, such as > expose a model of the repo that is easy for people to reason about. > This simplicity is why I think a lot of people prefer to use MQ, despite > its shortcomings. > > One common development workflow is to author a series of linear > changesets, using bookmarks, branches, anonymous heads, or even topics > (3rd party extension). I'll call this a "stack." We don't use the term stack yet in Mercurial, other than (oddly enough!) the help for MQ. I'm not wholly opposed to coming up with a noun for this (stack might even be the right choice), but I think it might be worth doing a survey of the existing terms of art for this in our features (eg I know patchbomb calls it a series - that might be the only other place this is explicitly named right now?) > You periodically > rewrite history in place (using `hg histedit`) and reparent the stack > against newer changesets (using `hg rebase`). This workflow can be > difficult because there is no obvious way to quickly see the current > "stack" nor its relation to other changesets. Figuring out arguments to > `hg rebase` can be difficult and may require highlighting and pasting > multiple changeset nodes to construct a command. > > The goal of this commit is to make stack based workflows simpler > by exposing a view of the current stack and its relationship to > other releant changesets, notably the parent of the base changeset > in the stack and newer heads that the stack could be rebased or merged > into. > > Introduced is the `hg show stack` view. Essentially, it finds all > mutable changesets from the working directory revision in both > directions, stopping at a merge or branch point. This limits the > revisions to a DAG linear range. Does this have some affordance so that other extensions (remote{names,branches} specifically come to mind) can redefine the stack boundary? On Mercurial itself just sniffing for draft changes will show a lot of things I don't care about anymore since they're pushed upstream. :) > > The stack is rendered as a concise list of changesets. Alongside the > stack is a visualization of the DAG, similar to `hg log -G`. Assuming this retains the same lack of BC that we're embracing in `hg show`, I'm happy to land something eagerly and iterate (my nagging concerns about noun choice aside.) [... elided entire implementation, which I didn't read yet ...] > diff --git a/tests/test-show-stack.t b/tests/test-show-stack.t > new file mode 100644 > --- /dev/null > +++ b/tests/test-show-stack.t > @@ -0,0 +1,235 @@ > + $ cat >> $HGRCPATH << EOF > + > [extensions] > + > show = > + > EOF > + > + $ hg init repo0 > + $ cd repo0 > + > +Empty repo / no checkout results in error > + > + $ hg show stack > + abort: stack view only available when there is a working directory > + [255] > + > +Stack displays single draft changeset as root revision > + > + $ echo 0 > foo > + $ hg -q commit -A -m 'commit 0' > + $ hg show stack > + @ 9f171 commit 0 > + > +Stack displays multiple draft changesets > + > + $ echo 1 > foo > + $ hg commit -m 'commit 1' > + $ echo 2 > foo > + $ hg commit -m 'commit 2' > + $ echo 3 > foo > + $ hg commit -m 'commit 3' > + $ echo 4 > foo > + $ hg commit -m 'commit 4' > + $ hg show stack > + @ 2737b commit 4 > + o d1a69 commit 3 > + o 128c8 commit 2 > + o 181cc commit 1 > + o 9f171 commit 0 > + > +Public parent of draft base is displayed, separated from stack > + > + $ hg phase --public -r 0 > + $ hg show stack > + @ 2737b commit 4 > + o d1a69 commit 3 > + o 128c8 commit 2 > + o 181cc commit 1 > + / (stack base) > + o 9f171 commit 0 > + > + $ hg phase --public -r 1 > + $ hg show stack > + @ 2737b commit 4 > + o d1a69 commit 3 > + o 128c8 commit 2 > + / (stack base) > + o 181cc commit 1 > + > +Draft descendants are shown > + > + $ hg -q up 2 > + $ hg show stack > + o 2737b commit 4 > + o d1a69 commit 3 > + @ 128c8 commit 2 > + / (stack base) > + o 181cc commit 1 > + > + $ hg -q up 3 > + $ hg show stack > + o 2737b commit 4 > + @ d1a69 commit 3 > + o 128c8 commit 2 > + / (stack base) > + o 181cc commit 1 > + > +working dir on public changeset should display special message > + > + $ hg -q up 1 > + $ hg show stack > + (empty stack; working directory is a published changeset) > + > +Branch point in descendants displayed at top of graph > + > + $ hg -q up 3 > + $ echo b > foo > + $ hg commit -m 'commit 5 (new dag branch)' > + created new head > + $ hg -q up 2 > + $ hg show stack > + \ / (multiple children) > + | > + o d1a69 commit 3 > + @ 128c8 commit 2 > + / (stack base) > + o 181cc commit 1 > + > + $ cd .. > + > +Base is stopped at merges > + > + $ hg init merge-base > + $ cd merge-base > + $ echo 0 > foo > + $ hg -q commit -A -m initial > + $ echo h1 > foo > + $ hg commit -m 'head 1' > + $ hg -q up 0 > + $ echo h2 > foo > + $ hg -q commit -m 'head 2' > + $ hg phase --public -r 0:tip > + $ hg -q up 1 > + $ hg merge -t :local 2 > + 0 files updated, 1 files merged, 0 files removed, 0 files unresolved > + (branch merge, don't forget to commit) > + $ hg commit -m 'merge heads' > + > +TODO doesn't yet handle case where wdir is a draft merge > + > + $ hg show stack > + @ 8ee90 merge heads > + / (stack base) > + o 59478 head 1 > + > + $ echo d1 > foo > + $ hg commit -m 'draft 1' > + $ echo d2 > foo > + $ hg commit -m 'draft 2' > + > + $ hg show stack > + @ 430d5 draft 2 > + o 787b1 draft 1 > + / (stack base) > + o 8ee90 merge heads > + > + $ cd .. > + > +Now move on to stacks when there are more commits after the base branchpoint > + > + $ hg init public-rebase > + $ cd public-rebase > + $ echo 0 > foo > + $ hg -q commit -A -m 'base' > + $ hg phase --public -r . > + $ echo d1 > foo > + $ hg commit -m 'draft 1' > + $ echo d2 > foo > + $ hg commit -m 'draft 2' > + $ hg -q up 0 > + $ echo 1 > foo > + $ hg commit -m 'new 1' > + created new head > + $ echo 2 > foo > + $ hg commit -m 'new 2' > + $ hg -q up 2 > + > +Newer draft heads don't impact output > + > + $ hg show stack > + @ eaffc draft 2 > + o 2b218 draft 1 > + / (stack base) > + o b66bb base > + > +Newer public heads are rendered > + > + $ hg phase --public -r '::tip' > + > + $ hg show stack > + (new heads) > + > + o baa4b new 2 > + / (2 commits ahead) > + : > + : (current stack) > + : > + : @ eaffc draft 2 > + : o 2b218 draft 1 > + :/ (stack base) > + o b66bb base > + > +If rebase is available, we show a hint how to rebase to that head > + > + $ hg --config extensions.rebase= show stack > + (new heads) > + > + o baa4b new 2 > + / (2 commits ahead) > + : hg rebase -s 2b218 -d baa4b > + : > + : (current stack) > + : > + : @ eaffc draft 2 > + : o 2b218 draft 1 > + :/ (stack base) > + o b66bb base > + > +Similar tests but for multiple heads > + > + $ hg -q up 0 > + $ echo h2 > foo > + $ hg -q commit -m 'new head 2' > + $ hg phase --public -r . > + $ hg -q up 2 > + > + $ hg show stack > + (new heads) > + > + o baa4b new 2 > + / (2 commits ahead) > + : o 9a848 new head 2 > + :/ (1 commits ahead) > + : > + : (current stack) > + : > + : @ eaffc draft 2 > + : o 2b218 draft 1 > + :/ (stack base) > + o b66bb base > + > + $ hg --config extensions.rebase= show stack > + (new heads) > + > + o baa4b new 2 > + / (2 commits ahead) > + : hg rebase -s 2b218 -d baa4b > + : o 9a848 new head 2 > + :/ (1 commits ahead) > + : hg rebase -s 2b218 -d 9a848 > + : > + : (current stack) > + : > + : @ eaffc draft 2 > + : o 2b218 draft 1 > + :/ (stack base) > + o b66bb base > _______________________________________________ > Mercurial-devel mailing list > Mercurial-devel@mercurial-scm.org > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Gregory Szorc <gregory.szorc@gmail.com> writes: > # HG changeset patch > # User Gregory Szorc <gregory.szorc@gmail.com> > # Date 1497726059 21600 > # Sat Jun 17 13:00:59 2017 -0600 > # Node ID c9a3d8cdbb925614419216d6e99d9a70f03d926d > # Parent 9fcb6df413c9ca475e705ecc15df07584dadb0c1 > show: implement "stack" view > > People often want to know what they are working on *now*. As part of > this, they also commonly want to know how that work is related to other > changesets in the repo so they can perform common actions like rebase, > histedit, and merge. > > `hg show work` made headway into this space. However, it is geared > towards a complete repo view as opposed to just the current line of > work. If you have a lot of in-flight work or the repo has many heads, > the output can be overwhelming. The closest thing Mercurial has to > "show me the current thing I'm working on" that doesn't require custom > revsets is `hg qseries`. And this requires MQ, which completely changes > workflows and repository behavior and has horrible performance on large > repos. But as sub-optimal as MQ is, it does some things right, such as > expose a model of the repo that is easy for people to reason about. > This simplicity is why I think a lot of people prefer to use MQ, despite > its shortcomings. > > One common development workflow is to author a series of linear > changesets, using bookmarks, branches, anonymous heads, or even topics > (3rd party extension). I'll call this a "stack." You periodically > rewrite history in place (using `hg histedit`) and reparent the stack > against newer changesets (using `hg rebase`). This workflow can be > difficult because there is no obvious way to quickly see the current > "stack" nor its relation to other changesets. Figuring out arguments to > `hg rebase` can be difficult and may require highlighting and pasting > multiple changeset nodes to construct a command. > > The goal of this commit is to make stack based workflows simpler > by exposing a view of the current stack and its relationship to > other releant changesets, notably the parent of the base changeset > in the stack and newer heads that the stack could be rebased or merged > into. > > Introduced is the `hg show stack` view. Essentially, it finds all > mutable changesets from the working directory revision in both > directions, stopping at a merge or branch point. This limits the > revisions to a DAG linear range. > > The stack is rendered as a concise list of changesets. Alongside the > stack is a visualization of the DAG, similar to `hg log -G`. > > Newer public heads from the branch point of the stack are rendered > above the stack. The presence of these heads helps people understand > the DAG model and the relationship between the stack and changes made > since the branch point of that stack. If the "rebase" command is > available, a `hg rebase` command is printed for each head so a user > can perform a simple copy and paste to perform a rebase. > > This patch is currently RFC quality. There are several TODOs. Most > are documented inline. Notable ones are: > > * Discuss visualization format. I've gone back and forth on whether to > connect the stack and heads DAGs. I think it looks nicer if they > are disconnected. But I also think disconnecting hurts DAG > comprehension. I would lean towards displaying the graph so that it's easier to visualize when your stack has diverged. > * Need to use templater (Yuya is refactoring and I didn't want to > introduce conflicts for the templater changes I want to make to > support this) How will a user customize the template? Will there be place holders? Or is it something to override? For example, I wanted to add remotename info to the output from 'show' but debated on how to do that exactly. > * Consider using graphmod or at least consult things like symbol > characters. Using graphmod explicitly would require a lot of new > features in graphmod. I'm not sure it is worth it for a single > consumer. > > My immediate goal is to hear "+1" on the command and the general > visualization. Then things can be formalized using proper techniques. > Remember, `hg show` isn't covered by BC guarantees. So perfect should > be the enemy of good. I ended up having to disable 'show' because I kept typing 'hg show REV' too much, for what it's worth.
On Fri, Jun 23, 2017 at 2:24 PM, Sean Farley <sean@farley.io> wrote: > Gregory Szorc <gregory.szorc@gmail.com> writes: > > > # HG changeset patch > > # User Gregory Szorc <gregory.szorc@gmail.com> > > # Date 1497726059 21600 > > # Sat Jun 17 13:00:59 2017 -0600 > > # Node ID c9a3d8cdbb925614419216d6e99d9a70f03d926d > > # Parent 9fcb6df413c9ca475e705ecc15df07584dadb0c1 > > show: implement "stack" view > > > > People often want to know what they are working on *now*. As part of > > this, they also commonly want to know how that work is related to other > > changesets in the repo so they can perform common actions like rebase, > > histedit, and merge. > > > > `hg show work` made headway into this space. However, it is geared > > towards a complete repo view as opposed to just the current line of > > work. If you have a lot of in-flight work or the repo has many heads, > > the output can be overwhelming. The closest thing Mercurial has to > > "show me the current thing I'm working on" that doesn't require custom > > revsets is `hg qseries`. And this requires MQ, which completely changes > > workflows and repository behavior and has horrible performance on large > > repos. But as sub-optimal as MQ is, it does some things right, such as > > expose a model of the repo that is easy for people to reason about. > > This simplicity is why I think a lot of people prefer to use MQ, despite > > its shortcomings. > > > > One common development workflow is to author a series of linear > > changesets, using bookmarks, branches, anonymous heads, or even topics > > (3rd party extension). I'll call this a "stack." You periodically > > rewrite history in place (using `hg histedit`) and reparent the stack > > against newer changesets (using `hg rebase`). This workflow can be > > difficult because there is no obvious way to quickly see the current > > "stack" nor its relation to other changesets. Figuring out arguments to > > `hg rebase` can be difficult and may require highlighting and pasting > > multiple changeset nodes to construct a command. > > > > The goal of this commit is to make stack based workflows simpler > > by exposing a view of the current stack and its relationship to > > other releant changesets, notably the parent of the base changeset > > in the stack and newer heads that the stack could be rebased or merged > > into. > > > > Introduced is the `hg show stack` view. Essentially, it finds all > > mutable changesets from the working directory revision in both > > directions, stopping at a merge or branch point. This limits the > > revisions to a DAG linear range. > > > > The stack is rendered as a concise list of changesets. Alongside the > > stack is a visualization of the DAG, similar to `hg log -G`. > > > > Newer public heads from the branch point of the stack are rendered > > above the stack. The presence of these heads helps people understand > > the DAG model and the relationship between the stack and changes made > > since the branch point of that stack. If the "rebase" command is > > available, a `hg rebase` command is printed for each head so a user > > can perform a simple copy and paste to perform a rebase. > > > > This patch is currently RFC quality. There are several TODOs. Most > > are documented inline. Notable ones are: > > > > * Discuss visualization format. I've gone back and forth on whether to > > connect the stack and heads DAGs. I think it looks nicer if they > > are disconnected. But I also think disconnecting hurts DAG > > comprehension. > > I would lean towards displaying the graph so that it's easier to > visualize when your stack has diverged. > > > * Need to use templater (Yuya is refactoring and I didn't want to > > introduce conflicts for the templater changes I want to make to > > support this) > > How will a user customize the template? Will there be place holders? Or > is it something to override? > > For example, I wanted to add remotename info to the output from 'show' > but debated on how to do that exactly. > I fixed `hg show work` a few days ago so it now displays names from all namespaces. Would need to do something similar for this view. > > > * Consider using graphmod or at least consult things like symbol > > characters. Using graphmod explicitly would require a lot of new > > features in graphmod. I'm not sure it is worth it for a single > > consumer. > > > > My immediate goal is to hear "+1" on the command and the general > > visualization. Then things can be formalized using proper techniques. > > Remember, `hg show` isn't covered by BC guarantees. So perfect should > > be the enemy of good. > > I ended up having to disable 'show' because I kept typing 'hg show REV' > too much, for what it's worth. > I think you'll appreciate the new commands.show.aliasprefix config option I added a few days ago. I now regularly use `hg swork` via aliasprefix=s.
On Sun, Jun 18, 2017 at 6:53 PM, Augie Fackler <raf@durin42.com> wrote: > On Sat, Jun 17, 2017 at 01:02:49PM -0600, Gregory Szorc wrote: > > # HG changeset patch > > # User Gregory Szorc <gregory.szorc@gmail.com> > > # Date 1497726059 21600 > > # Sat Jun 17 13:00:59 2017 -0600 > > # Node ID c9a3d8cdbb925614419216d6e99d9a70f03d926d > > # Parent 9fcb6df413c9ca475e705ecc15df07584dadb0c1 > > show: implement "stack" view > > > > People often want to know what they are working on *now*. As part of > > this, they also commonly want to know how that work is related to other > > changesets in the repo so they can perform common actions like rebase, > > histedit, and merge. > > > > `hg show work` made headway into this space. However, it is geared > > towards a complete repo view as opposed to just the current line of > > work. If you have a lot of in-flight work or the repo has many heads, > > the output can be overwhelming. The closest thing Mercurial has to > > "show me the current thing I'm working on" that doesn't require custom > > revsets is `hg qseries`. And this requires MQ, which completely changes > > workflows and repository behavior and has horrible performance on large > > repos. But as sub-optimal as MQ is, it does some things right, such as > > expose a model of the repo that is easy for people to reason about. > > This simplicity is why I think a lot of people prefer to use MQ, despite > > its shortcomings. > > > > One common development workflow is to author a series of linear > > changesets, using bookmarks, branches, anonymous heads, or even topics > > (3rd party extension). I'll call this a "stack." > > We don't use the term stack yet in Mercurial, other than (oddly > enough!) the help for MQ. I'm not wholly opposed to coming up with a > noun for this (stack might even be the right choice), but I think it > might be worth doing a survey of the existing terms of art for this in > our features (eg I know patchbomb calls it a series - that might be > the only other place this is explicitly named right now?) > "stack" and "series" are the only options that I can think of. I still like "stack" because: a) It is something you can visually relate to. This helps reinforce the concept of the DAG and that your commits are individual units of work building on top of each other. A stack is also likely envisioned as unidirectional - always going up. b) Durham is pushing a "restack" feature (which I really like) and I think there is room for `hg show stack` and it to complement each other. They could potentially agree on the same definition of stack boundaries. > > > You periodically > > rewrite history in place (using `hg histedit`) and reparent the stack > > against newer changesets (using `hg rebase`). This workflow can be > > difficult because there is no obvious way to quickly see the current > > "stack" nor its relation to other changesets. Figuring out arguments to > > `hg rebase` can be difficult and may require highlighting and pasting > > multiple changeset nodes to construct a command. > > > > The goal of this commit is to make stack based workflows simpler > > by exposing a view of the current stack and its relationship to > > other releant changesets, notably the parent of the base changeset > > in the stack and newer heads that the stack could be rebased or merged > > into. > > > > Introduced is the `hg show stack` view. Essentially, it finds all > > mutable changesets from the working directory revision in both > > directions, stopping at a merge or branch point. This limits the > > revisions to a DAG linear range. > > Does this have some affordance so that other extensions > (remote{names,branches} specifically come to mind) can redefine the > stack boundary? On Mercurial itself just sniffing for draft changes > will show a lot of things I don't care about anymore since they're > pushed upstream. :) > I think it makes sense to allow that to be defined, yes. We should be able to throw it into destutil, just like we have for desthistedit(). > > > > > The stack is rendered as a concise list of changesets. Alongside the > > stack is a visualization of the DAG, similar to `hg log -G`. > > Assuming this retains the same lack of BC that we're embracing in `hg > show`, I'm happy to land something eagerly and iterate (my nagging > concerns about noun choice aside.) > It would. No promise of BC guarantees in `hg show` yet. > > [... elided entire implementation, which I didn't read yet ...] > > > diff --git a/tests/test-show-stack.t b/tests/test-show-stack.t > > new file mode 100644 > > --- /dev/null > > +++ b/tests/test-show-stack.t > > @@ -0,0 +1,235 @@ > > + $ cat >> $HGRCPATH << EOF > > + > [extensions] > > + > show = > > + > EOF > > + > > + $ hg init repo0 > > + $ cd repo0 > > + > > +Empty repo / no checkout results in error > > + > > + $ hg show stack > > + abort: stack view only available when there is a working directory > > + [255] > > + > > +Stack displays single draft changeset as root revision > > + > > + $ echo 0 > foo > > + $ hg -q commit -A -m 'commit 0' > > + $ hg show stack > > + @ 9f171 commit 0 > > + > > +Stack displays multiple draft changesets > > + > > + $ echo 1 > foo > > + $ hg commit -m 'commit 1' > > + $ echo 2 > foo > > + $ hg commit -m 'commit 2' > > + $ echo 3 > foo > > + $ hg commit -m 'commit 3' > > + $ echo 4 > foo > > + $ hg commit -m 'commit 4' > > + $ hg show stack > > + @ 2737b commit 4 > > + o d1a69 commit 3 > > + o 128c8 commit 2 > > + o 181cc commit 1 > > + o 9f171 commit 0 > > + > > +Public parent of draft base is displayed, separated from stack > > + > > + $ hg phase --public -r 0 > > + $ hg show stack > > + @ 2737b commit 4 > > + o d1a69 commit 3 > > + o 128c8 commit 2 > > + o 181cc commit 1 > > + / (stack base) > > + o 9f171 commit 0 > > + > > + $ hg phase --public -r 1 > > + $ hg show stack > > + @ 2737b commit 4 > > + o d1a69 commit 3 > > + o 128c8 commit 2 > > + / (stack base) > > + o 181cc commit 1 > > + > > +Draft descendants are shown > > + > > + $ hg -q up 2 > > + $ hg show stack > > + o 2737b commit 4 > > + o d1a69 commit 3 > > + @ 128c8 commit 2 > > + / (stack base) > > + o 181cc commit 1 > > + > > + $ hg -q up 3 > > + $ hg show stack > > + o 2737b commit 4 > > + @ d1a69 commit 3 > > + o 128c8 commit 2 > > + / (stack base) > > + o 181cc commit 1 > > + > > +working dir on public changeset should display special message > > + > > + $ hg -q up 1 > > + $ hg show stack > > + (empty stack; working directory is a published changeset) > > + > > +Branch point in descendants displayed at top of graph > > + > > + $ hg -q up 3 > > + $ echo b > foo > > + $ hg commit -m 'commit 5 (new dag branch)' > > + created new head > > + $ hg -q up 2 > > + $ hg show stack > > + \ / (multiple children) > > + | > > + o d1a69 commit 3 > > + @ 128c8 commit 2 > > + / (stack base) > > + o 181cc commit 1 > > + > > + $ cd .. > > + > > +Base is stopped at merges > > + > > + $ hg init merge-base > > + $ cd merge-base > > + $ echo 0 > foo > > + $ hg -q commit -A -m initial > > + $ echo h1 > foo > > + $ hg commit -m 'head 1' > > + $ hg -q up 0 > > + $ echo h2 > foo > > + $ hg -q commit -m 'head 2' > > + $ hg phase --public -r 0:tip > > + $ hg -q up 1 > > + $ hg merge -t :local 2 > > + 0 files updated, 1 files merged, 0 files removed, 0 files unresolved > > + (branch merge, don't forget to commit) > > + $ hg commit -m 'merge heads' > > + > > +TODO doesn't yet handle case where wdir is a draft merge > > + > > + $ hg show stack > > + @ 8ee90 merge heads > > + / (stack base) > > + o 59478 head 1 > > + > > + $ echo d1 > foo > > + $ hg commit -m 'draft 1' > > + $ echo d2 > foo > > + $ hg commit -m 'draft 2' > > + > > + $ hg show stack > > + @ 430d5 draft 2 > > + o 787b1 draft 1 > > + / (stack base) > > + o 8ee90 merge heads > > + > > + $ cd .. > > + > > +Now move on to stacks when there are more commits after the base > branchpoint > > + > > + $ hg init public-rebase > > + $ cd public-rebase > > + $ echo 0 > foo > > + $ hg -q commit -A -m 'base' > > + $ hg phase --public -r . > > + $ echo d1 > foo > > + $ hg commit -m 'draft 1' > > + $ echo d2 > foo > > + $ hg commit -m 'draft 2' > > + $ hg -q up 0 > > + $ echo 1 > foo > > + $ hg commit -m 'new 1' > > + created new head > > + $ echo 2 > foo > > + $ hg commit -m 'new 2' > > + $ hg -q up 2 > > + > > +Newer draft heads don't impact output > > + > > + $ hg show stack > > + @ eaffc draft 2 > > + o 2b218 draft 1 > > + / (stack base) > > + o b66bb base > > + > > +Newer public heads are rendered > > + > > + $ hg phase --public -r '::tip' > > + > > + $ hg show stack > > + (new heads) > > + > > + o baa4b new 2 > > + / (2 commits ahead) > > + : > > + : (current stack) > > + : > > + : @ eaffc draft 2 > > + : o 2b218 draft 1 > > + :/ (stack base) > > + o b66bb base > > + > > +If rebase is available, we show a hint how to rebase to that head > > + > > + $ hg --config extensions.rebase= show stack > > + (new heads) > > + > > + o baa4b new 2 > > + / (2 commits ahead) > > + : hg rebase -s 2b218 -d baa4b > > + : > > + : (current stack) > > + : > > + : @ eaffc draft 2 > > + : o 2b218 draft 1 > > + :/ (stack base) > > + o b66bb base > > + > > +Similar tests but for multiple heads > > + > > + $ hg -q up 0 > > + $ echo h2 > foo > > + $ hg -q commit -m 'new head 2' > > + $ hg phase --public -r . > > + $ hg -q up 2 > > + > > + $ hg show stack > > + (new heads) > > + > > + o baa4b new 2 > > + / (2 commits ahead) > > + : o 9a848 new head 2 > > + :/ (1 commits ahead) > > + : > > + : (current stack) > > + : > > + : @ eaffc draft 2 > > + : o 2b218 draft 1 > > + :/ (stack base) > > + o b66bb base > > + > > + $ hg --config extensions.rebase= show stack > > + (new heads) > > + > > + o baa4b new 2 > > + / (2 commits ahead) > > + : hg rebase -s 2b218 -d baa4b > > + : o 9a848 new head 2 > > + :/ (1 commits ahead) > > + : hg rebase -s 2b218 -d 9a848 > > + : > > + : (current stack) > > + : > > + : @ eaffc draft 2 > > + : o 2b218 draft 1 > > + :/ (stack base) > > + o b66bb base > > _______________________________________________ > > Mercurial-devel mailing list > > Mercurial-devel@mercurial-scm.org > > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel >
Gregory Szorc <gregory.szorc@gmail.com> writes: > On Fri, Jun 23, 2017 at 2:24 PM, Sean Farley <sean@farley.io> wrote: > >> Gregory Szorc <gregory.szorc@gmail.com> writes: >> >> > # HG changeset patch >> > # User Gregory Szorc <gregory.szorc@gmail.com> >> > # Date 1497726059 21600 >> > # Sat Jun 17 13:00:59 2017 -0600 >> > # Node ID c9a3d8cdbb925614419216d6e99d9a70f03d926d >> > # Parent 9fcb6df413c9ca475e705ecc15df07584dadb0c1 >> > show: implement "stack" view >> > >> > People often want to know what they are working on *now*. As part of >> > this, they also commonly want to know how that work is related to other >> > changesets in the repo so they can perform common actions like rebase, >> > histedit, and merge. >> > >> > `hg show work` made headway into this space. However, it is geared >> > towards a complete repo view as opposed to just the current line of >> > work. If you have a lot of in-flight work or the repo has many heads, >> > the output can be overwhelming. The closest thing Mercurial has to >> > "show me the current thing I'm working on" that doesn't require custom >> > revsets is `hg qseries`. And this requires MQ, which completely changes >> > workflows and repository behavior and has horrible performance on large >> > repos. But as sub-optimal as MQ is, it does some things right, such as >> > expose a model of the repo that is easy for people to reason about. >> > This simplicity is why I think a lot of people prefer to use MQ, despite >> > its shortcomings. >> > >> > One common development workflow is to author a series of linear >> > changesets, using bookmarks, branches, anonymous heads, or even topics >> > (3rd party extension). I'll call this a "stack." You periodically >> > rewrite history in place (using `hg histedit`) and reparent the stack >> > against newer changesets (using `hg rebase`). This workflow can be >> > difficult because there is no obvious way to quickly see the current >> > "stack" nor its relation to other changesets. Figuring out arguments to >> > `hg rebase` can be difficult and may require highlighting and pasting >> > multiple changeset nodes to construct a command. >> > >> > The goal of this commit is to make stack based workflows simpler >> > by exposing a view of the current stack and its relationship to >> > other releant changesets, notably the parent of the base changeset >> > in the stack and newer heads that the stack could be rebased or merged >> > into. >> > >> > Introduced is the `hg show stack` view. Essentially, it finds all >> > mutable changesets from the working directory revision in both >> > directions, stopping at a merge or branch point. This limits the >> > revisions to a DAG linear range. >> > >> > The stack is rendered as a concise list of changesets. Alongside the >> > stack is a visualization of the DAG, similar to `hg log -G`. >> > >> > Newer public heads from the branch point of the stack are rendered >> > above the stack. The presence of these heads helps people understand >> > the DAG model and the relationship between the stack and changes made >> > since the branch point of that stack. If the "rebase" command is >> > available, a `hg rebase` command is printed for each head so a user >> > can perform a simple copy and paste to perform a rebase. >> > >> > This patch is currently RFC quality. There are several TODOs. Most >> > are documented inline. Notable ones are: >> > >> > * Discuss visualization format. I've gone back and forth on whether to >> > connect the stack and heads DAGs. I think it looks nicer if they >> > are disconnected. But I also think disconnecting hurts DAG >> > comprehension. >> >> I would lean towards displaying the graph so that it's easier to >> visualize when your stack has diverged. >> >> > * Need to use templater (Yuya is refactoring and I didn't want to >> > introduce conflicts for the templater changes I want to make to >> > support this) >> >> How will a user customize the template? Will there be place holders? Or >> is it something to override? >> >> For example, I wanted to add remotename info to the output from 'show' >> but debated on how to do that exactly. >> > > I fixed `hg show work` a few days ago so it now displays names from all > namespaces. Would need to do something similar for this view. I was more getting at how to change the view in a general way. Perhaps if that existed, then the logic wouldn't need to be duplicated for each view. Alternatively, the answer could be that users can't extend / change the view. Or that power users are offered a revset (e.g. hg log -r show_wip or something) and can provide their own template. >> > * Consider using graphmod or at least consult things like symbol >> > characters. Using graphmod explicitly would require a lot of new >> > features in graphmod. I'm not sure it is worth it for a single >> > consumer. >> > >> > My immediate goal is to hear "+1" on the command and the general >> > visualization. Then things can be formalized using proper techniques. >> > Remember, `hg show` isn't covered by BC guarantees. So perfect should >> > be the enemy of good. >> >> I ended up having to disable 'show' because I kept typing 'hg show REV' >> too much, for what it's worth. >> > > I think you'll appreciate the new commands.show.aliasprefix config option I > added a few days ago. I now regularly use `hg swork` via aliasprefix=s. To be honest, it didn't help. I think of 'show' as something that should take a view name or a revset. I'll probably just end up writing my own wrapper if need be.
Patch
diff --git a/hgext/show.py b/hgext/show.py --- a/hgext/show.py +++ b/hgext/show.py @@ -17,11 +17,13 @@ from __future__ import absolute_import from mercurial.i18n import _ from mercurial.node import nullrev from mercurial import ( cmdutil, + commands, error, formatter, graphmod, + phases, pycompat, registrar, revset, revsetlang, @@ -138,8 +140,165 @@ def showbookmarks(ui, repo, fm): fm.write('node', fm.hexfunc(node), fm.hexfunc(node)) fm.data(active=bm == active, longestbookmarklen=longestname) +@showview('stack') +def showstack(ui, repo, fm): + """Current line of work""" + wdirctx = repo['.'] + if wdirctx.rev() == nullrev: + raise error.Abort(_('stack view only available when there is a ' + 'working directory')) + + if wdirctx.phase() == phases.public: + ui.write(_('(empty stack; working directory is a published ' + 'changeset)\n')) + return + + # The stack is the wdir revision plus all mutable ancestors and + # descendants in a linear range. We could do the search as a revset. + # But it is simpler to just walk the DAG. + cl = repo.changelog + stackrevs = {wdirctx.rev(),} + + # Find relevant ancestors in the stack. + # TODO consider using histedit's baserev calculation for this, possibly + # supplemented if needed. + basectx = None + for rev in cl.ancestors([wdirctx.rev()]): + ctx = repo[rev] + + # Stop at merges. + if len(ctx.parents()) > 1: + basectx = ctx + break + + # Stop at public phase. + if ctx.phase() == phases.public: + basectx = ctx + break + + stackrevs.add(ctx.rev()) + + # And relevant descendants. + branchpointattip = False + + for rev in cl.descendants([wdirctx.rev()]): + ctx = repo[rev] + + # Will only happen if . is public. + if ctx.phase() == phases.public: + break + + stackrevs.add(ctx.rev()) + + if len(ctx.children()) > 1: + branchpointattip = True + break + + stackrevs = list(reversed(sorted(stackrevs))) + + # Find likely target heads for the current stack. These are likely + # merge or rebase targets. + if basectx: + # TODO make this customizable? + newheads = set(repo.revs('heads(%d::) - %ld - not public()', + basectx.rev(), stackrevs)) + else: + newheads = set() + + try: + cmdutil.findcmd('rebase', commands.table) + haverebase = True + except error.UnknownCommand: + haverebase = False + + # TODO use templating. + # TODO consider using graphmod. But it may not be necessary given + # our simplicity and the customizations required. + # TODO use proper graph symbols from graphmod + # TODO bikeshed over whether to draw DAG fully connected between heads + # and stack sections. + + # We write out new heads to aid in DAG awareness and to help with decision + # making on how the stack should be reconciled with commits made since the + # branch point. + if newheads: + # Calculate distance from base so we can render the count and so we can + # sort display order by commit distance. + revdistance = {} + for head in newheads: + # There is some redundancy in DAG traversal here and therefore + # room to optimize. + ancestors = cl.ancestors([head], stoprev=basectx.rev()) + revdistance[head] = len(list(ancestors)) + + sourcenode = repo[stackrevs[-1]].hex()[0:5] + + ui.write(_(' (new heads)\n\n')) + + sortedheads = sorted(newheads, key=lambda x: revdistance[x], + reverse=True) + + for i, rev in enumerate(sortedheads): + ctx = repo[rev] + shortnode = ctx.hex()[0:5] + + if i: + ui.write(': ') + else: + ui.write(' ') + + ui.write('o ', shortnode, ' ', + ctx.description().splitlines()[0], '\n') + + if i: + ui.write(':/') + else: + ui.write(' /') + + ui.write(_(' (%d commits ahead)\n' % revdistance[rev])) + if haverebase: + # TODO label so colored + # TODO may be able to omit -s in some scenarios + ui.write(': hg rebase -s %s -d %s\n' % ( + sourcenode, shortnode)) + + ui.write(':\n: ', _('(current stack)\n'), ':\n') + + if branchpointattip: + ui.write(' \\ / ', _('(multiple children)\n'), ' |\n') + + for rev in stackrevs: + ctx = repo[rev] + symbol = '@' if rev == wdirctx.rev() else 'o' + + if newheads: + ui.write(': ') + else: + ui.write(' ') + + ui.write(symbol, ' ', ctx.hex()[0:5], ' ', + ctx.description().splitlines()[0], + '\n') + + # TODO display histedit hint? + + if basectx: + # Vertically and horizontally separate stack base from parent + # to reinforce stack boundary. + if newheads: + ui.write(':/ ') + else: + ui.write(' / ') + + ui.write(_('(stack base)'), '\n') + ui.write('o ') + + ui.write(basectx.hex()[0:5], ' ', + basectx.description().splitlines()[0], + '\n') + @revsetpredicate('_underway([commitage[, headage]])') def underwayrevset(repo, subset, x): args = revset.getargsdict(x, 'underway', 'commitage headage') if 'commitage' not in args: diff --git a/tests/test-show-stack.t b/tests/test-show-stack.t new file mode 100644 --- /dev/null +++ b/tests/test-show-stack.t @@ -0,0 +1,235 @@ + $ cat >> $HGRCPATH << EOF + > [extensions] + > show = + > EOF + + $ hg init repo0 + $ cd repo0 + +Empty repo / no checkout results in error + + $ hg show stack + abort: stack view only available when there is a working directory + [255] + +Stack displays single draft changeset as root revision + + $ echo 0 > foo + $ hg -q commit -A -m 'commit 0' + $ hg show stack + @ 9f171 commit 0 + +Stack displays multiple draft changesets + + $ echo 1 > foo + $ hg commit -m 'commit 1' + $ echo 2 > foo + $ hg commit -m 'commit 2' + $ echo 3 > foo + $ hg commit -m 'commit 3' + $ echo 4 > foo + $ hg commit -m 'commit 4' + $ hg show stack + @ 2737b commit 4 + o d1a69 commit 3 + o 128c8 commit 2 + o 181cc commit 1 + o 9f171 commit 0 + +Public parent of draft base is displayed, separated from stack + + $ hg phase --public -r 0 + $ hg show stack + @ 2737b commit 4 + o d1a69 commit 3 + o 128c8 commit 2 + o 181cc commit 1 + / (stack base) + o 9f171 commit 0 + + $ hg phase --public -r 1 + $ hg show stack + @ 2737b commit 4 + o d1a69 commit 3 + o 128c8 commit 2 + / (stack base) + o 181cc commit 1 + +Draft descendants are shown + + $ hg -q up 2 + $ hg show stack + o 2737b commit 4 + o d1a69 commit 3 + @ 128c8 commit 2 + / (stack base) + o 181cc commit 1 + + $ hg -q up 3 + $ hg show stack + o 2737b commit 4 + @ d1a69 commit 3 + o 128c8 commit 2 + / (stack base) + o 181cc commit 1 + +working dir on public changeset should display special message + + $ hg -q up 1 + $ hg show stack + (empty stack; working directory is a published changeset) + +Branch point in descendants displayed at top of graph + + $ hg -q up 3 + $ echo b > foo + $ hg commit -m 'commit 5 (new dag branch)' + created new head + $ hg -q up 2 + $ hg show stack + \ / (multiple children) + | + o d1a69 commit 3 + @ 128c8 commit 2 + / (stack base) + o 181cc commit 1 + + $ cd .. + +Base is stopped at merges + + $ hg init merge-base + $ cd merge-base + $ echo 0 > foo + $ hg -q commit -A -m initial + $ echo h1 > foo + $ hg commit -m 'head 1' + $ hg -q up 0 + $ echo h2 > foo + $ hg -q commit -m 'head 2' + $ hg phase --public -r 0:tip + $ hg -q up 1 + $ hg merge -t :local 2 + 0 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -m 'merge heads' + +TODO doesn't yet handle case where wdir is a draft merge + + $ hg show stack + @ 8ee90 merge heads + / (stack base) + o 59478 head 1 + + $ echo d1 > foo + $ hg commit -m 'draft 1' + $ echo d2 > foo + $ hg commit -m 'draft 2' + + $ hg show stack + @ 430d5 draft 2 + o 787b1 draft 1 + / (stack base) + o 8ee90 merge heads + + $ cd .. + +Now move on to stacks when there are more commits after the base branchpoint + + $ hg init public-rebase + $ cd public-rebase + $ echo 0 > foo + $ hg -q commit -A -m 'base' + $ hg phase --public -r . + $ echo d1 > foo + $ hg commit -m 'draft 1' + $ echo d2 > foo + $ hg commit -m 'draft 2' + $ hg -q up 0 + $ echo 1 > foo + $ hg commit -m 'new 1' + created new head + $ echo 2 > foo + $ hg commit -m 'new 2' + $ hg -q up 2 + +Newer draft heads don't impact output + + $ hg show stack + @ eaffc draft 2 + o 2b218 draft 1 + / (stack base) + o b66bb base + +Newer public heads are rendered + + $ hg phase --public -r '::tip' + + $ hg show stack + (new heads) + + o baa4b new 2 + / (2 commits ahead) + : + : (current stack) + : + : @ eaffc draft 2 + : o 2b218 draft 1 + :/ (stack base) + o b66bb base + +If rebase is available, we show a hint how to rebase to that head + + $ hg --config extensions.rebase= show stack + (new heads) + + o baa4b new 2 + / (2 commits ahead) + : hg rebase -s 2b218 -d baa4b + : + : (current stack) + : + : @ eaffc draft 2 + : o 2b218 draft 1 + :/ (stack base) + o b66bb base + +Similar tests but for multiple heads + + $ hg -q up 0 + $ echo h2 > foo + $ hg -q commit -m 'new head 2' + $ hg phase --public -r . + $ hg -q up 2 + + $ hg show stack + (new heads) + + o baa4b new 2 + / (2 commits ahead) + : o 9a848 new head 2 + :/ (1 commits ahead) + : + : (current stack) + : + : @ eaffc draft 2 + : o 2b218 draft 1 + :/ (stack base) + o b66bb base + + $ hg --config extensions.rebase= show stack + (new heads) + + o baa4b new 2 + / (2 commits ahead) + : hg rebase -s 2b218 -d baa4b + : o 9a848 new head 2 + :/ (1 commits ahead) + : hg rebase -s 2b218 -d 9a848 + : + : (current stack) + : + : @ eaffc draft 2 + : o 2b218 draft 1 + :/ (stack base) + o b66bb base