changeset 980:64a2e940e1b2

docs: add concepts guide (the set theory behind obsolescence)
author Greg Ward <greg@gerg.ca>
date Fri, 06 Jun 2014 17:20:30 -0400
parents c7b2ccd99dab
children 34e4e51affd1
files docs/concepts.rst docs/index.rst
diffstat 2 files changed, 224 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/concepts.rst	Fri Jun 06 17:20:30 2014 -0400
@@ -0,0 +1,223 @@
+.. Copyright 2014 Greg Ward <greg@gerg.ca>
+
+----------------
+Evolve: Concepts
+----------------
+
+Getting the most out of software requires an accurate understanding of
+the concepts underlying it. For example, you cannot use Mercurial to
+its full potential without understanding the DAG (directed acyclic
+graph) of changesets and the meaning of parent/child relationships
+between nodes in that graph. Mercurial with changeset evolution adds
+some additional concepts to the graph of changesets. Understanding
+those concepts will make you an informed and empowered user of
+``evolve``.
+
+.. note:: This document contains math! If you have a pathological fear
+          of set theory and the associated notation, you might be
+          better off just reading the `user guide`_. But if you
+          appreciate the theoretical rigour underlying core Mercurial,
+          you will be happy to know that it continues right into
+          changeset evolution.
+
+.. note:: This document is incomplete! (The formatting of the math
+          isn't quite right yet, and the diagrams are missing for
+          malformatted.)
+
+This document follows standard set theory notation:
+
+  x ∈ A: x is a member of A
+
+  A ∪ B: union of A and B: { x | x ∈ A or x ∈ B }
+
+  A ∖ B: set difference: { x | x ∈ A and x ∉ B }
+
+  A ⊇ B: superset: if x ∈ B, then x ∈ A
+
+.. _`user guide`: user-guide.html
+
+Phases
+------
+
+First, every changeset in a Mercurial repository (since 2.3) has a
+*phase*. Phases are independent of ``evolve`` and they affect
+Mercurial usage with or without changeset evolution. However, they
+were implemented in order to support evolution, and are a critical
+foundation of ``evolve``.
+
+Phases are strictly ordered:
+
+  secret > draft > public
+
+Changesets generally only move from a higher phase to a lower phase.
+Typically, changesets start life in *draft* phase, and move to
+*public* phase when they are pushed to a public repository. (You can
+set the default phase of new commits in Mercurial configuration.)
+
+The purpose of phases is to prevent modifying published history.
+``evolve`` will therefore only let you rewrite changesets in one of
+the two *mutable* phases (secret or draft).
+
+Run ``hg help phases`` for more information on phases.
+
+Obsolete changesets
+-------------------
+
+*Obsolescence* is they key concept at the heart of changeset
+evolution. Everything else in this document depends on understanding
+obsolescence. So: what does it mean for a changeset to be obsolete?
+
+In implementation terms, there is an *obsolescence marker* associated
+with changesets: every changeset is either obsolete or not.
+
+The simplest way that a changeset becomes obsolete is by *pruning* it.
+The ``hg prune`` command simply marks the specified changesets
+obsolete, as long as they are mutable.
+
+More commonly, a changeset *A* becomes obsolete by *amending* it.
+Amendment creates a new changeset *A'* that replaces *A*, which is now
+obsolete. *A'* is the successor of *A*, and *A* the predecessor of *A'*:
+
+  [diagram: A and A' with pred/succ edge]
+
+The predecessor/successor relationship forms an additional
+*obsolescence graph* overlaid on top of the traditional DAG formed by
+changesets and their parent/child relationships. In fact, the
+obsolescence graph is second-order version control. Where the
+traditional parent/child DAG tracks changes to your source code, the
+obsolescence graph tracks changes to your changesets. It tracks the
+evolution of your changesets.
+
+(If you prefer a calculus metaphor to set theory, it might help to
+think of the traditional parent/child DAG as the first derivative of
+your source code, and the obsolescence DAG as the second derivative.)
+
+Troubled changesets (unstable, bumped, divergent)
+-------------------------------------------------
+
+Evolving history can introduce problems that need to be solved. For
+example, if you prune a changeset *P* but not its descendants, those
+descendants are now on thin ice. To push a changeset to another
+repository *R*, all of its ancestors must be present in *R* or pushed
+at the same time. But Mercurial does not push obsolete changesets like
+*P*, so it cannot push the descendants of *P*. Any non-obsolete
+changeset that is a descendant of an obsolete changeset is said to be
+*unstable*.
+
+  [diagram: obsolete cset with non-obsolete descendant]
+
+Another sort of trouble occurs when two developers, Alice and Bob,
+collaborate via a shared non-publishing repository. (This is how
+developers can safely `share mutable history`_.) Say Alice and Bob
+both start the day with changeset *C* in *draft* phase. If Alice
+pushes *C* to their public repository, then it is now published and
+therefore immutable. But Bob is working from a desert island and
+cannot pull this change in *C*'s phase. For Bob, *C* is still in draft
+phase and therefore mutable. So Bob amends *C*, which marks it
+obsolete and replaces it with *C'*. When he is back online and pulls
+from the public repository, Mercurial learns that *C* is public, which
+means it cannot be obsolete. We say that *C'* is *bumped*, since it is
+the successor of a public changeset.
+
+.. _`share mutable history`: sharing.html
+
+(Incidentally, the terminology here comes from airline overbooking: if
+two people have bought tickets for the same seat on a plane and they
+both show up at the airport, only one of them gets on the plane. The
+passenger who is left behind in the airport terminal has been
+"bumped".)
+
+The third sort of trouble is when Alice and Bob both amend the same
+changeset *C* to have different successors. When this happens, the
+successors are both called *divergent* (unless one of them is in
+public phase; only mutable changesets are divergent).
+
+The collective term for unstable, bumped, and divergent changeset is
+*troubled*:
+
+  troubled = unstable ∪ bumped ∪ divergent
+
+It is possible for a changeset to be in any of the troubled categories
+at the same time: it might be unstable and divergent, or bumped and
+divergent, or whatever.
+
+  [diagram: Venn diagram of troubled changesets, showing overlap]
+
+The presence of troubled changesets indicates the need to run ``hg
+evolve``.
+
+Hidden (and visible) changesets
+-------------------------------
+
+Some obsolete changesets are *hidden*: deliberately suppressed by
+Mercurial and usually not visible through the UI. (As of Mercurial
+2.9, there are still some commands that inadvertently reveal hidden
+changesets; these are bugs and will be fixed in due course.)
+
+All hidden changesets are obsolete, and all obsolete changesets are
+part of your repository. Mathematically speaking:
+
+  repo ⊇ obsolete ⊇ hidden
+
+Or, putting it visually:
+
+  [diagram: Venn diagram showing nested strict subsets]
+
+However, the presence of obsolete but not hidden changesets should be
+temporary. The desired end state for any history mutation operation is
+that all obsolete changesets are hidden, i.e.:
+
+  repo ⊇ obsolete, obsolete = hidden
+
+Visually:
+
+  [diagram: Venn diagram showing obsolete = hidden, subset of repo]
+
+
+Why is this changeset visible?
+------------------------------
+
+Any changeset which is not hidden is *visible*. That is,
+
+  visible = repo ∖ hidden
+
+(Recall that ∖ means set difference: *visible* is the set of
+changesets that are in *repo* but not in *hidden*.)
+
+After amending or pruning a changeset, you might expect it to be
+hidden. It doesn't always work out that way. The precise rules are:
+
+  hideable = obsolete
+  blockers = bookmarks ∪ parents(workingcopy) ∪ localtags
+  hidden = hideable ∖ ancestors((repo ∖ hideable) ∪ blockers)
+
+This will probably be clearer with a worked example. First, here's a
+repository with some obsolete changesets, some troubled changesets,
+one bookmark, a working copy, and some hidden changesets:
+
+        x-x
+       /
+  -o-o-o-o
+     \
+      x-x-o
+
+Here's the computation required to determine which changesets are
+hidden:
+
+  repo = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }
+
+  hideable = obsolete = { 2, 4, 5, 8 }
+
+  blockers = { 6 } ∪ { 4 } ∪ {}
+
+  blockers = { 4, 6 }
+
+  hidden = hideable ∖ ancestors((repo ∖ { 2, 4, 5, 8 }) ∪ { 4, 6 })
+
+  hidden = hideable ∖ ancestors({ 0, 1, 3, 6, 7 } ∪ { 4, 6 })
+
+  hidden = hideable ∖ ancestors({ 0, 1, 3, 4, 6, 7 })
+
+  hidden = { 2, 4, 5, 8 } ∖ { 0, 1, 2, 3, 4, 5, 6, 7 }
+
+  hidden = { 8 }
--- a/docs/index.rst	Fri Jun 06 17:19:17 2014 -0400
+++ b/docs/index.rst	Fri Jun 06 17:20:30 2014 -0400
@@ -96,7 +96,7 @@
     see the `user guide`_.
   * For more advanced tricks, see `sharing mutable history`_.
   * To learn about the concepts underlying ``evolve``, see `concepts`_
-    (coming soon).
+    (incomplete).
   * If you're coming from MQ, see the `MQ migration guide`_ (incomplete).
 
 .. _`user guide`: user-guide.html