docs/sharing.rst
changeset 979 c7b2ccd99dab
child 1186 0b66826f081c
equal deleted inserted replaced
978:8328337d23b2 979:c7b2ccd99dab
       
     1 .. Copyright © 2014 Greg Ward <greg@gerg.ca>
       
     2 
       
     3 ------------------------------
       
     4 Evolve: Shared Mutable History
       
     5 ------------------------------
       
     6 
       
     7 Once you have mastered the art of mutable history in a single
       
     8 repository, you might want to move up to the next level: *shared*
       
     9 mutable history. ``evolve`` lets you push and pull draft changesets
       
    10 between repositories along with their obsolescence markers. This opens
       
    11 up a number of interesting possibilities.
       
    12 
       
    13 The most common scenario is a single developer working across two
       
    14 computers. Say you're working on code that must be tested on a remote
       
    15 test server, probably in a rack somewhere, only accessible by SSH, and
       
    16 running an “enterprise-grade” (out-of-date) OS. But you probably
       
    17 prefer to write code locally: everything is setup the way you like it,
       
    18 and you can use your preferred editor, IDE, merge/diff tools, etc.
       
    19 
       
    20 Traditionally, your options are limited: either
       
    21 
       
    22   * (ab)use your source control system by committing half-working code
       
    23     in order to get it onto the remote test server, or
       
    24   * go behind source control's back by using ``rsync`` (or similar) to
       
    25     transfer your code back-and-forth until it is ready to commit
       
    26 
       
    27 The former is less bad with distributed version control systems like
       
    28 Mercurial, but it's still far from ideal. (One important version
       
    29 control “best practice” is that every commit should make things just a
       
    30 little bit better, i.e. you should never commit code that is worse
       
    31 than what came before.) The latter, avoiding version control entirely,
       
    32 means that you're walking a tightrope without a safety net. One
       
    33 accidental ``rsync`` in the wrong direction could destroy hours of
       
    34 work.
       
    35 
       
    36 Using Mercurial with ``evolve`` to share mutable history solves all of
       
    37 these problems. As with single-repository ``evolve``, you can commit
       
    38 whenever the code is demonstrably better, even if all the tests aren't
       
    39 passing yet—just ``hg amend`` when they are. And you can transfer
       
    40 those half-baked changesets between repositories to try things out on
       
    41 your test server before anything is carved in stone.
       
    42 
       
    43 A less common scenario is multiple developers sharing mutable history.
       
    44 (This is in fact how Mercurial itself is developed.) We'll cover this
       
    45 scenario later. But first, single-user sharing.
       
    46 
       
    47 Publishing and non-publishing repositories
       
    48 ------------------------------------------
       
    49 
       
    50 The key to shared mutable history is to keep your changesets in
       
    51 *draft* phase as you pass them around. Recall that by default, ``hg
       
    52 push`` promotes changesets from *draft* to *public*, and public
       
    53 changesets are immutable. You can change this behaviour by
       
    54 reconfiguring the *target* repository so that it is non-publishing.
       
    55 (Short version: set ``phases.publish`` to ``false``. Long version
       
    56 follows.)
       
    57 
       
    58 Setting things up
       
    59 -----------------
       
    60 
       
    61 We'll work an example with three local repositories, although in the
       
    62 real world they'd most likely be on three different computers. First,
       
    63 the public repository is where tested, polished changesets live, and
       
    64 it is where you push/pull changesets to/from the rest of your team. ::
       
    65 
       
    66   $ hg init public
       
    67 
       
    68 We'll need two clones where work gets done::
       
    69 
       
    70   $ hg clone -q public test-repo
       
    71   $ hg clone -q test-repo dev-repo
       
    72 
       
    73 ``dev-repo`` is your local machine, with GUI merge tools and IDEs and
       
    74 everything configured just the way you like it. ``test-repo`` is the
       
    75 test server in a rack somewhere behind SSH. So for the most part,
       
    76 we'll develop in ``dev-repo``, push to ``test-repo``, test and polish
       
    77 there, and push to ``public``.
       
    78 
       
    79 The key to making this whole thing work is to make ``test-repo``
       
    80 non-publishing::
       
    81 
       
    82   $ cat >> test-repo/.hg/hgrc <<EOF
       
    83   [phases]
       
    84   publish = false
       
    85   EOF
       
    86 
       
    87 We also have to configure ``evolve`` in both ``test-repo`` and
       
    88 ``dev-repo``, so that we can amend and evolve in both of them. ::
       
    89 
       
    90   $ cat >> test-repo/.hg/hgrc <<EOF
       
    91   [extensions]
       
    92   rebase =
       
    93   evolve = /path/to/mutable-history/hgext/evolve.py
       
    94   EOF
       
    95   $ cat >> dev-repo/.hg/hgrc <<EOF
       
    96   [extensions]
       
    97   rebase =
       
    98   evolve = /path/to/mutable-history/hgext/evolve.py
       
    99   EOF
       
   100 
       
   101 Keep in mind that in real life, these repositories would probably be
       
   102 on separate computers, so you'd have to login to each one to configure
       
   103 each repository.
       
   104 
       
   105 To start things off, let's make one public, immutable changeset::
       
   106 
       
   107   $ cd test-repo
       
   108   $ echo 'my new project' > file1
       
   109   $ hg add file1
       
   110   $ hg commit -m 'create new project'
       
   111   $ hg push -q
       
   112 
       
   113 and pull that into the development repository::
       
   114 
       
   115   $ cd ../dev-repo
       
   116   $ hg pull -u
       
   117 
       
   118 Amending a shared changeset
       
   119 ---------------------------
       
   120 
       
   121 Everything you learned in the `user guide`_ applies to work done in
       
   122 ``dev-repo``. You can commit, amend, uncommit, evolve, and so forth
       
   123 just as before.
       
   124 
       
   125 .. _`user guide`: user-guide.html
       
   126 
       
   127 Things get different when you push changesets to ``test-repo``. Or
       
   128 rather, things stay the same, which *is* different: because we
       
   129 configured ``test-repo`` to be non-publishing, draft changesets stay
       
   130 draft when we push them to ``test-repo``. Importantly, they're also
       
   131 draft (mutable) in ``test-repo``.
       
   132 
       
   133 Let's commit a preliminary change and push it to ``test-repo`` for
       
   134 testing. ::
       
   135 
       
   136   $ echo 'fix fix fix' > file1
       
   137   $ hg commit -m 'prelim change'
       
   138   $ hg push ../test-repo
       
   139 
       
   140 At this point, ``dev-repo`` and ``test-repo`` have the same changesets
       
   141 in the same phases:
       
   142 
       
   143   [figure SG01: rev 0:0dc9 public, rev 1:f649 draft, same on both repos]
       
   144 
       
   145 (You may notice a change in notation from the user guide: now
       
   146 changesets are labelled with their revision number and the first four
       
   147 digits of the 40-digit hexadecimal changeset ID. Mercurial revision
       
   148 numbers are never stable when working across repositories, especially
       
   149 when obsolescence is involved. We'll see why shortly.)
       
   150 
       
   151 Now let's switch to ``test-repo`` to test our change::
       
   152 
       
   153   $ cd ../test-repo
       
   154   $ hg update
       
   155 
       
   156 Don't forget to ``hg update``! Pushing only adds changesets to a
       
   157 remote repository; it does not update the working directory (unless
       
   158 you have a hook that updates for you).
       
   159 
       
   160 Now let's imagine the tests failed because we didn't use proper
       
   161 punctuation and capitalization (oops). Let's amend our preliminary fix
       
   162 (and fix the lame commit message while we're at it)::
       
   163 
       
   164   $ echo 'Fix fix fix.' > file1
       
   165   $ hg amend -m 'fix bug 37'
       
   166 
       
   167 Now we're in a funny intermediate state (figure 2): revision 1:f649 is
       
   168 obsolete in ``test-repo``, having been replaced by revision 3:60ff
       
   169 (revision 2:2a03 is another one of those temporary amend commits that
       
   170 we saw in the user guide)—but ``dev-repo`` knows nothing of these
       
   171 recent developments.
       
   172 
       
   173   [figure SG02: rev 0:0dc9 public, rev 1:f649, 2:2a03 obsolete, rev 3:60ff draft -- but dev-repo same as in SG01]
       
   174 
       
   175 Let's resynchronize::
       
   176 
       
   177   $ cd ../dev-repo
       
   178   $ hg pull -u
       
   179 
       
   180 As seen in figure 3, this transfers the new changeset *and* the
       
   181 obsolescence marker for revision 1. However, it does *not* transfer
       
   182 the temporary amend commit, because it is obsolete. Push and pull
       
   183 transfer obsolesence markers between repositories, but they do not
       
   184 normally transfer obsolete changesets.
       
   185 
       
   186   [figure SG03: dev-repo grows new rev 2:60ff, marks 1:f649 obsolete]
       
   187 
       
   188 Because of this deliberately incomplete synchronization, revision
       
   189 numbers in ``test-repo`` and ``dev-repo`` are no longer consistent. We
       
   190 *must* use changeset IDs.
       
   191 
       
   192 Amend again, locally
       
   193 --------------------
       
   194 
       
   195 This process can repeat. Perhaps you figure out a more elegant fix to
       
   196 the bug, and want to mutate history so nobody ever knows you had a
       
   197 less-than-perfect idea. We'll implement it locally in ``dev-repo`` and
       
   198 push to ``test-repo``::
       
   199 
       
   200   $ echo 'Fix, fix, and fix.' > file1
       
   201   $ hg amend
       
   202   $ hg push
       
   203 
       
   204 This time around, the temporary amend commit is in ``dev-repo``, and
       
   205 it is not transferred to ``test-repo``—the same as before, just in the
       
   206 opposite direction. Figure 4 shows the two repositories after amending
       
   207 in ``dev-repo`` and pushing to ``test-repo``.
       
   208 
       
   209   [figure SG04: each repo has one temporary amend commit, but they're different in each one]
       
   210 
       
   211 Let's hop over to ``test-repo`` to test the more elegant fix::
       
   212 
       
   213   $ cd ../test-repo
       
   214   $ hg update -q
       
   215 
       
   216 This time, all the tests pass, so no further amendment is required.
       
   217 This bug fix is finished, so we push it to the public repository::
       
   218 
       
   219   $ hg push
       
   220   [...]
       
   221   added 1 changesets with 1 changes to 1 files
       
   222 
       
   223 Note that only one changeset—the final version, after two
       
   224 amendments—was actually pushed. Again, Mercurial normally doesn't
       
   225 transfer obsolete changesets on push and pull. (Specifically, it
       
   226 doesn't transfer *hidden* changesets: roughly speaking, obsolete
       
   227 changesets with no non-obsolete descendants. If you're curious, see
       
   228 the `concept guide`_ for the precise definition of hidden.)
       
   229 
       
   230 .. _`concept guide`: concepts.html
       
   231 
       
   232 So the picture in ``public`` is much simpler than in either
       
   233 ``dev-repo`` or ``test-repo``. None of our missteps or amendments are
       
   234 visible publicly, just the final, beautifully polished changeset:
       
   235 
       
   236   [figure SG05: public repo with rev 0:0dc9, 1:de61, both public]
       
   237 
       
   238 There is one important step left to do. Because we pushed from
       
   239 ``test-repo`` to ``public``, the pushed changeset is in *public* phase
       
   240 in those two repositories. But ``dev-repo`` knows nothing of this:
       
   241 that changeset is still *draft* there. If we're not careful, we might
       
   242 mutate history in ``dev-repo``, obsoleting a changeset that is already
       
   243 public. Let's avoid that situation for now by pulling from
       
   244 ``test-repo`` down to ``dev-repo``::
       
   245 
       
   246   $ cd ../dev-repo
       
   247   $ hg pull -u
       
   248 
       
   249 Getting into trouble
       
   250 --------------------
       
   251 
       
   252 Mercurial with ``evolve`` is a powerful tool, and using powerful tools
       
   253 can have consequences. (You can cut yourself badly with a sharp knife,
       
   254 but every competent chef keeps several around. Ever try to chop onions
       
   255 with a spoon?)
       
   256 
       
   257 In the user guide, we saw examples of *unstable* changesets, which are
       
   258 the most common type of troubled changeset. (Recall that a
       
   259 non-obsolete changeset with obsolete ancestors is unstable.)
       
   260 
       
   261 Two other types of trouble can crop up: *bumped* and *divergent*
       
   262 changesets. Both are more likely with shared mutable history,
       
   263 especially mutable history shared by multiple developers.
       
   264 
       
   265 To demonstrate, let's start with the ``public`` repository as we left
       
   266 it in the last example, with two immutable changesets (figure 5
       
   267 above). Two developers, Alice and Bob, start working from this point::
       
   268 
       
   269   $ hg clone -q public alice
       
   270   $ hg clone -q public bob
       
   271 
       
   272 We need to configure Alice's and Bob's working repositories similar to
       
   273 ``test-repo``, i.e. make them non-publishing and enable ``evolve``::
       
   274 
       
   275   $ cat >> alice/.hg/hgrc <<EOF
       
   276   [phases]
       
   277   publish = false
       
   278   [extensions]
       
   279   rebase =
       
   280   evolve = /path/to/mutable-history/hgext/evolve.py
       
   281   EOF
       
   282   $ cp alice/.hg/hgrc bob/.hg/hgrc
       
   283 
       
   284 Bumped changesets: only one gets on the plane
       
   285 ---------------------------------------------
       
   286 
       
   287 If two people show up at the airport with tickets for the same seat on
       
   288 the same plane, only one of them gets on the plane. The would-be
       
   289 traveller left behind in the airport terminal is said to have been
       
   290 *bumped*.
       
   291 
       
   292 Similarly, if Alice and Bob are collaborating on some mutable
       
   293 changesets, it's possible to get into a situation where an otherwise
       
   294 worthwhile changeset cannot be pushed to the public repository; it is
       
   295 bumped by an alternative changeset that happened to get there first.
       
   296 Let's demonstrate one way this could happen.
       
   297 
       
   298 It starts with Alice committing a bug fix. Right now, we don't yet
       
   299 know if this bug fix is good enough to push to the public repository,
       
   300 but it's good enough for Alice to commit. ::
       
   301 
       
   302   $ cd alice
       
   303   $ echo 'fix' > file2
       
   304   $ hg commit -q -A -m 'fix bug 15'
       
   305 
       
   306 Now Bob has a bad idea: he decides to pull whatever Alice is working
       
   307 on and tweak her bug fix to his taste::
       
   308 
       
   309   $ cd ../bob
       
   310   $ hg pull -q -u ../alice
       
   311   $ echo 'Fix.' > file2
       
   312   $ hg amend -q -A -m 'fix bug 15 (amended)'
       
   313 
       
   314 (Note the lack of communication between Alice and Bob. Failing to
       
   315 communicate with your colleagues is a good way to get into trouble.
       
   316 Nevertheless, ``evolve`` can usually sort things out, as we will see.)
       
   317 
       
   318   [figure SG06: Bob's repo with one amendment]
       
   319 
       
   320 After some testing, Alice realizes her bug fix is just fine as it is:
       
   321 no need for further polishing and amending, this changeset is ready to
       
   322 publish. ::
       
   323 
       
   324   $ cd ../alice
       
   325   $ hg push  -q
       
   326 
       
   327 This introduces a contradiction: in Bob's repository, changeset 2:e011
       
   328 (his copy of Alice's fix) is obsolete, since Bob amended it. But in
       
   329 Alice's repository (and ``public``), that changeset is public: it is
       
   330 immutable, carved in stone for all eternity. No changeset can be both
       
   331 obsolete and public, so Bob is in for a surprise the next time he
       
   332 pulls from ``public``::
       
   333 
       
   334   $ cd ../bob
       
   335   $ hg pull -q -u
       
   336   1 new bumped changesets
       
   337 
       
   338 Figure 7 shows what just happened to Bob's repository: changeset
       
   339 2:e011 is now public, so it can't be obsolete. When that changeset was
       
   340 obsolete, it made perfect sense for it to have a successor, namely
       
   341 Bob's amendment of Alice's fix (changeset 4:fe88). But it's illogical
       
   342 for a public changeset to have a successor, so 4:fe88 is in trouble:
       
   343 it has been *bumped*.
       
   344 
       
   345   [figure SG07: 2:e011 now public not obsolete, 4:fe88 now bumped]
       
   346 
       
   347 As usual when there's trouble in your repository, the solution is to
       
   348 evolve it::
       
   349 
       
   350   $ hg evolve --all
       
   351 
       
   352 Figure 8 illustrate's Bob's repository after evolving away the bumped
       
   353 changeset. Ignoring the obsolete changesets, Bob now has a nice,
       
   354 clean, simple history. His amendment of Alice's bug fix lives on, as
       
   355 changeset 5:227d—albeit with a software-generated commit message. (Bob
       
   356 should probably amend that changeset to improve the commit message.)
       
   357 But the important thing is that his repository no longer has any
       
   358 troubled changesets, thanks to ``evolve``.
       
   359 
       
   360   [figure SG08: 5:227d is new, formerly bumped changeset 4:fe88 now hidden]
       
   361 
       
   362 Divergent changesets
       
   363 --------------------
       
   364 
       
   365 In addition to *unstable* and *bumped*, there is a third kind of
       
   366 troubled changeset: *divergent*. When an obsolete changeset has two
       
   367 successors, those successors are divergent.
       
   368 
       
   369 To illustrate, let's start Alice and Bob at the same
       
   370 point—specifically, the point where Alice's repository currently
       
   371 stands. Bob's repository is a bit of a mess, so we'll throw it away
       
   372 and start him off with a copy of Alice's repository::
       
   373 
       
   374   $ cd ..
       
   375   $ rm -rf bob
       
   376   $ cp -rp alice bob
       
   377 
       
   378 Now we'll have Bob commit a bug fix that could still be improved::
       
   379 
       
   380   $ cd bob
       
   381   $ echo 'pretty good fix' >> file1
       
   382   $ hg commit -u bob -m 'fix bug 24 (v1)'
       
   383 
       
   384 This time, Alice meddles with her colleague's work (still a bad
       
   385 idea)::
       
   386 
       
   387   $ cd ../alice
       
   388   $ hg pull -q -u ../bob
       
   389   $ echo 'better (alice)' >> file1
       
   390   $ hg amend -u alice -m 'fix bug 24 (v2 by alice)'
       
   391 
       
   392 Here's where things change from the "bumped" scenario above: this
       
   393 time, the original author (Bob) decides to amend his changeset too. ::
       
   394 
       
   395   $ cd ../bob
       
   396   $ echo 'better (bob)' >> file1
       
   397   $ hg amend -u bob -m 'fix bug 24 (v2 by bob)'
       
   398 
       
   399 At this point, the divergence exists, but only in theory: Bob's
       
   400 original changeset, 3:fe81, is obsolete and has two successors. But
       
   401 those successors are in different repositories, so the trouble is not
       
   402 visible to anyone yet. It will be as soon as one of our players pulls
       
   403 from the other's repository. Let's make Bob the victim again::
       
   404 
       
   405   $ hg pull -q -u ../alice
       
   406   not updating: not a linear update
       
   407   (merge or update --check to force update)
       
   408   2 new divergent changesets
       
   409 
       
   410 The “not a linear update” is our first hint that something is wrong,
       
   411 but of course “2 new divergent changesets” is the real problem. Figure
       
   412 9 shows both problems.
       
   413 
       
   414   [figure SG09: bob's repo with 2 heads for the 2 divergent changesets, 5:fc16 and 6:694f; wc is at 5:fc16, hence update refused; both are successors of obsolete 3:fe81, hence divergence]
       
   415 
       
   416 Now we need to get out of trouble. Unfortunately, a `bug`_ in
       
   417 ``evolve`` means that the usual answer (run ``hg evolve --all``) does
       
   418 not work. Bob has to figure out the solution on his own: in this case,
       
   419 merge. To avoid distractions, we'll set ``HGMERGE`` to make Mercurial
       
   420 resolve any conflicts in favour of Bob. ::
       
   421 
       
   422   $ HGMERGE=internal:local hg merge
       
   423   $ hg commit -m merge
       
   424 
       
   425 .. _`bug`: https://bitbucket.org/marmoute/mutable-history/issue/48/
       
   426 
       
   427 This is approximately what ``hg evolve`` would do in this
       
   428 circumstance, if not for that bug. One annoying difference is that
       
   429 Mercurial thinks the two divergent changesets are still divergent,
       
   430 which you can see with a simple revset query::
       
   431 
       
   432   $ hg log -q -r 'divergent()'
       
   433   5:fc16901f4d7a
       
   434   6:694fd0f6b503
       
   435 
       
   436 (That annoyance should go away when the bug is fixed.)
       
   437 
       
   438 Conclusion
       
   439 ----------
       
   440 
       
   441 Mutable history is a powerful tool. Like a sharp knife, an experienced
       
   442 user can do wonderful things with it, much more wonderful than with a
       
   443 dull knife (never mind a rusty spoon). At the same time, an
       
   444 inattentive or careless user can do harm to himself or others.
       
   445 Mercurial with ``evolve`` goes to great lengths to limit the harm you
       
   446 can do by trying to handle all possible types of “troubled”
       
   447 changesets. But having a first-aid kit nearby does not excuse you from
       
   448 being careful with sharp knives.
       
   449 
       
   450 Mutable history shared across multiple repositories by a single
       
   451 developer is a natural extension of this model. Once you are used to
       
   452 using a single sharp knife on its own, it's pretty straightforward to
       
   453 chop onions and mushrooms using the same knife, or to alternate
       
   454 between two chopping boards with different knives.
       
   455 
       
   456 Mutable history shared by multiple developers is a scary place to go.
       
   457 Imagine a professional kitchen full of expert chefs tossing their
       
   458 favourite knives back and forth, with the occasional axe or chainsaw
       
   459 thrown in to spice things up. If you're confident that you *and your
       
   460 colleagues* can do it without losing a limb, go for it. But be sure to
       
   461 practice a lot first before you rely on it!