New user guide for evolve
Angel Ezquerra
angel.ezquerra at gmail.com
Sun Apr 27 19:46:45 UTC 2014
On Sun, Apr 27, 2014 at 1:30 AM, Greg Ward <greg at gerg.ca> wrote:
> And the new user-guide.rst:
>
> """
> .. Copyright 2014 Greg Ward <greg at gerg.ca>
>
> ------------------
> Evolve: User Guide
> ------------------
>
> Add a changeset: ``commit``
> ---------------------------
>
> To create a new changeset, simply run ``hg commit`` as usual.
> ``evolve`` does not change the behaviour of ``commit`` at all.
>
> However, it's important to understand that new changesets are in the
> *draft* phase: they are mutable. This means that they can be modified
> by Mercurial's existing history-editing commands (``rebase``,
> ``histedit``, etc.), and also by ``evolve``. Generally speaking,
> changesets remain in *draft* phase until they are pushed to another
> repository, at which point they enter *public* phase. ::
Perhaps it is too early in the guide to say so, but maybe we should
explicitly say "until they are pushed to another publishing
repository"?
> $ hg commit -m"implement feature X"
> $ hg phase -r .
> 1: draft
>
> Modify (rewrite) a changeset: ``commit --amend``
> ------------------------------------------------
>
> Imagine you've just committed a new changeset, and then you discover a
> mistake. Maybe you forgot to run the tests and a failure slipped in.
> You want to modify history so that you push one perfect changeset,
> rather than one flawed changeset followed by an "oops" commit. (Or
> perhaps you made a typo in the commit message—this is really feature
> *Y*, not feature X. You can't fix that with a followup commit.)
>
> This is actually trivial with plain vanilla Mercurial since 2.2: fix
> your mistake and run ::
>
> $ hg commit --amend -m"implement feature Y"
>
> to create a new, amended changeset. The drawback of doing this with
> vanilla Mercurial is that your original, flawed, changeset is lost
> forever. This is *unsafe* history editing. It's probably not too
> serious if all you did was fix a syntax error, but still.
>
> [figure UG1: DAG with unsafe commit --amend]
>
> The difference when using ``evolve`` is subtle: outwardly, things look
> the same::
>
> $ hg commit --amend -m"implement feature Y"
>
> (Alternately, ``hg amend`` is nearly synonymous with ``hg commit
> --amend``. The difference is that ``hg amend`` uses the existing
> commit message by default, whereas ``hg commit --amend`` will run your
> editor if you don't pass ``-m`` or ``-l``.)
>
> But under the hood, Mercurial has simply marked the old changeset
> *obsolete*, replacing it with the new one. If it turns out that your
> trivial patch to get the tests passing again was a terrible idea, you
> can recover the original changeset and try again (see below, under
> "Recovering an obsolete changeset"). Thus, this is *safe* history
> modification.
>
> It's important to understand that Mercurial tracks the second-order
> relationship between your flawed changeset and the improved version:
> the new improved changeset is the *successor*, and the original (now
> obsolete) changeset is its *predecessor*.
>
> [figure UG2: DAG with safe amend and successor/predecessor]
>
> A final note: in this case, the obsolete changeset is also *hidden*.
> That is the usual end state for obsolete changesets. But many
> scenarios result in obsolete changesets that are still visible, which
> indicates your history modification work is not yet done. We'll see an
> example of that next.
>
> Modify an older changeset
> -------------------------
>
> Sometimes you don't notice your mistakes until after you have
> committed some new changesets on top of them. Traditionally, this
> looks something like ::
>
> $ hg commit -m"fix bug 17" # rev 4 (mistake here)
> $ hg commit -m"cleanup" # rev 5
> $ hg commit -m"feature 23" # rev 6
> $ hg commit -m"oops" # rev 7 (fix mistake)
>
> The trouble with this, of course, is that it makes you look bad. You
> made a mistake, and the record of that mistake is recorded in history
> for all eternity. More subtly, there now exist changesets that are
> *worse* than what came before—the code no longer builds, the tests
> don't pass, or similar. Anyone reviewing these patches will waste time
> noticing the error in the earlier patch, and then the correction later
> on.
>
> You can avoid all this by amending and evolving. Here's how it works,
> assuming you have just committed revision 7 and noticed the mistake
> in revision 4::
>
> $ hg update 4
> [fix mistake]
> $ hg amend
>
> At this point, revision 4 is *obsolete* and revisions 5 and 6—the
> descendants of 4—are in a funny state: they are *unstable*.
>
> [figure UG3: obsolete and unstable changesets]
>
> All non-obsolete descendants of an obsolete changeset are unstable. An
> interesting consequence of this is that revision 4 is still visible,
> even though it is obsolete. Obsolete changesets with non-obsolete
> descendants are not hidden.
>
> The fix is to *evolve* history::
>
> $ hg evolve --all
>
> This is a separate step, not automatically part of ``hg amend``,
> because there might be conflicts. If your evolved changeset modifies a
> file that one of its descendants modified, Mercurial has to fire up
> your merge tool to resolve the conflict. More importantly, you have to
> switch contexts from "writing code" to "resolving conflicts". That can
> be an expensive context switch, so Mercurial lets you decide when to
> do it.
>
> The end state, after ``evolve`` finishes, is that revisions 4-6 are
> obsolete and hidden. Their successor revisions (8–10) replace them.
> (In case you're wondering why there's a gap at revision 7: current
> versions of ``evolve`` create a "temporary amend commit" every time
> you amend a changeset with descendants. You'll see them if you run
> ``hg --hidden log``. This is an undesirable feature which will be
> fixed in a future version.)
>
> [figure UG4: obsolete revs with successors]
Very, very nice write up :-)
> Remove unwanted changes
> -----------------------
>
> Sometimes you make a change, and then decide it was such a bad idea
> that you don't want anyone to know about it. Or maybe it was a
> debugging hack that you needed to keep around for a while, but do not
> intend to ever push publicly.
>
> In either case, ``hg prune`` is the answer. ``prune`` simply marks
> changesets obsolete without creating any new changesets to replace
> them. Let's say you've just committed the following changesets::
>
> $ hg commit -m"useful work" # rev 11
> $ hg commit -m"debug hack" # rev 12
> $ hg commit -m"more work" # rev 13
>
> You want to drop revision 12, but keep 11 and 13. No problem::
>
> $ hg prune 12
>
> As above, this leaves your repository in a funny intermediate state:
> revision 13 is the non-obsolete descendant of obsolete revision 12.
> That is, revision 13 is unstable.
>
> [figure UG5: 12 obsolete, 13 non-obsolete/unstable]
>
> So you need to evolve your repository::
>
> $ hg evolve --all
>
> This rebases revision 13 on top of 11 as the new revision 14, leaving
> 12 and 13 obsolete and hidden:
>
> [figure UG6: new 14, 12 and 13 obsolete and hidden]
>
>
> Uncommitting files, version 1: immediate fix
> --------------------------------------------
>
> Occasionally you commit more than you intended to: perhaps you only
> meant to commit certain files, and forgot to specify those files on
> the ``commit`` command line, so Mercurial commits everything. This is
> easy to fix with ``uncommit``. Here's an example::
>
> $ hg commit -m"fix bug 234" # rev 15
>
> Say you meant to commit only ``file1.c``, but accidentally committed
> ``file2.c`` as well. If you immediately notice your mistake, fix it
> with ::
>
> $ hg uncommit file2.c
>
> This obsoletes your previous changeset and replaces it with a new one:
>
> [figure UG7: rev 15 obsolete, rev 16 successor]
>
> The unrelated changes to ``file2.c`` are left in your working
> directory, for you to deal with as you see fit::
>
> $ hg status
> M file2.c
>
> Uncommitting files, version 2: fix later with revert
> ----------------------------------------------------
>
> Say you don't notice your mistake immediately, and do something like
> this::
>
> $ hg commit -m"fix bug 234" # rev 15 (too many files)
> $ hg commit -m"fix bug 641" # rev 16
>
> To fix this, you need to travel back in time and amend revision 15,
> leaving your changes to ``file2.c`` back in the working directory::
>
> $ hg update 15
> 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> $ hg uncommit file2.c
> 1 new unstable changesets
> $ hg status
> M file2.c
>
> At this point your repository needs to be evolved, since it has
> unstable changesets. But first, you need to decide what to do with
> ``file2.c``, since ``hg evolve`` requires a clean working directory
> for resolving merge conflicts.
>
> If the change to ``file2.c`` was a temporary debugging hack, we can
> discard it and immediately evolve the unstable changeset::
>
> $ hg revert file2.c
> $ hg evolve --all
> move:[16] fix bug 641
> atop:[17] fix bug 234
>
> This results in a slightly more complicated picture:
>
> [figure UG8: rev 15 and 16 obsolete, successors 17 and 18]
>
> Uncommitting files, version 3: fix later with commit
> ----------------------------------------------------
>
> If your change to ``file2.c`` is actually valuable enough to commit,
> things get a bit more complicated. As above, we backup to revision 15
> and uncommit, leaving a modified ``file2.c`` in the working
> directory::
>
> $ hg update -q 15
> $ hg uncommit -q file2.c
> $ hg status
> M file2.c
>
> Now let's save those valuable changes before evolving::
>
> $ hg commit -m'unrelated change to file2.c'
>
> This creates a new changeset, revision 18, which is a child of
> revision 17. And lurking in the background, we still have to worry
> about unstable revision 16, which was destabilized by amending
> revision 15 with ``hg uncommit``::
>
> [figure UG9: rev 15 obsolete, rev 16 unstable, rev 17 and 18 normal]
>
> This is where things get complicated.
Is this really that complicated? It feels obvious that you created a
new head in doing this.
The only issue is that this new head will not really be obvious until
you evolve, but you updated to an older revision and committed.
Nothing strange happened IMHO. In any case I think that the
explanation that follows is very good so this is just a small quibble.
Everything else seems great to me.
Cheers,
Angel
More information about the Evolve-testers
mailing list