Mercurial > evolve
changeset 979:c7b2ccd99dab
docs: add guide to sharing mutable history
author | Greg Ward <greg@gerg.ca> |
---|---|
date | Fri, 06 Jun 2014 17:19:17 -0400 |
parents | 8328337d23b2 |
children | 64a2e940e1b2 |
files | docs/index.rst docs/sharing.rst tests/test-sharing.t |
diffstat | 3 files changed, 776 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/docs/index.rst Thu Jun 05 22:11:04 2014 -0400 +++ b/docs/index.rst Fri Jun 06 17:19:17 2014 -0400 @@ -94,7 +94,7 @@ Next steps: * For a practical guide to using ``evolve`` in a single repository, see the `user guide`_. - * For more advanced tricks, see `sharing mutable history`_ (coming soon). + * For more advanced tricks, see `sharing mutable history`_. * To learn about the concepts underlying ``evolve``, see `concepts`_ (coming soon). * If you're coming from MQ, see the `MQ migration guide`_ (incomplete).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/sharing.rst Fri Jun 06 17:19:17 2014 -0400 @@ -0,0 +1,461 @@ +.. Copyright © 2014 Greg Ward <greg@gerg.ca> + +------------------------------ +Evolve: Shared Mutable History +------------------------------ + +Once you have mastered the art of mutable history in a single +repository, you might want to move up to the next level: *shared* +mutable history. ``evolve`` lets you push and pull draft changesets +between repositories along with their obsolescence markers. This opens +up a number of interesting possibilities. + +The most common scenario is a single developer working across two +computers. Say you're working on code that must be tested on a remote +test server, probably in a rack somewhere, only accessible by SSH, and +running an “enterprise-grade” (out-of-date) OS. But you probably +prefer to write code locally: everything is setup the way you like it, +and you can use your preferred editor, IDE, merge/diff tools, etc. + +Traditionally, your options are limited: either + + * (ab)use your source control system by committing half-working code + in order to get it onto the remote test server, or + * go behind source control's back by using ``rsync`` (or similar) to + transfer your code back-and-forth until it is ready to commit + +The former is less bad with distributed version control systems like +Mercurial, but it's still far from ideal. (One important version +control “best practice” is that every commit should make things just a +little bit better, i.e. you should never commit code that is worse +than what came before.) The latter, avoiding version control entirely, +means that you're walking a tightrope without a safety net. One +accidental ``rsync`` in the wrong direction could destroy hours of +work. + +Using Mercurial with ``evolve`` to share mutable history solves all of +these problems. As with single-repository ``evolve``, you can commit +whenever the code is demonstrably better, even if all the tests aren't +passing yet—just ``hg amend`` when they are. And you can transfer +those half-baked changesets between repositories to try things out on +your test server before anything is carved in stone. + +A less common scenario is multiple developers sharing mutable history. +(This is in fact how Mercurial itself is developed.) We'll cover this +scenario later. But first, single-user sharing. + +Publishing and non-publishing repositories +------------------------------------------ + +The key to shared mutable history is to keep your changesets in +*draft* phase as you pass them around. Recall that by default, ``hg +push`` promotes changesets from *draft* to *public*, and public +changesets are immutable. You can change this behaviour by +reconfiguring the *target* repository so that it is non-publishing. +(Short version: set ``phases.publish`` to ``false``. Long version +follows.) + +Setting things up +----------------- + +We'll work an example with three local repositories, although in the +real world they'd most likely be on three different computers. First, +the public repository is where tested, polished changesets live, and +it is where you push/pull changesets to/from the rest of your team. :: + + $ hg init public + +We'll need two clones where work gets done:: + + $ hg clone -q public test-repo + $ hg clone -q test-repo dev-repo + +``dev-repo`` is your local machine, with GUI merge tools and IDEs and +everything configured just the way you like it. ``test-repo`` is the +test server in a rack somewhere behind SSH. So for the most part, +we'll develop in ``dev-repo``, push to ``test-repo``, test and polish +there, and push to ``public``. + +The key to making this whole thing work is to make ``test-repo`` +non-publishing:: + + $ cat >> test-repo/.hg/hgrc <<EOF + [phases] + publish = false + EOF + +We also have to configure ``evolve`` in both ``test-repo`` and +``dev-repo``, so that we can amend and evolve in both of them. :: + + $ cat >> test-repo/.hg/hgrc <<EOF + [extensions] + rebase = + evolve = /path/to/mutable-history/hgext/evolve.py + EOF + $ cat >> dev-repo/.hg/hgrc <<EOF + [extensions] + rebase = + evolve = /path/to/mutable-history/hgext/evolve.py + EOF + +Keep in mind that in real life, these repositories would probably be +on separate computers, so you'd have to login to each one to configure +each repository. + +To start things off, let's make one public, immutable changeset:: + + $ cd test-repo + $ echo 'my new project' > file1 + $ hg add file1 + $ hg commit -m 'create new project' + $ hg push -q + +and pull that into the development repository:: + + $ cd ../dev-repo + $ hg pull -u + +Amending a shared changeset +--------------------------- + +Everything you learned in the `user guide`_ applies to work done in +``dev-repo``. You can commit, amend, uncommit, evolve, and so forth +just as before. + +.. _`user guide`: user-guide.html + +Things get different when you push changesets to ``test-repo``. Or +rather, things stay the same, which *is* different: because we +configured ``test-repo`` to be non-publishing, draft changesets stay +draft when we push them to ``test-repo``. Importantly, they're also +draft (mutable) in ``test-repo``. + +Let's commit a preliminary change and push it to ``test-repo`` for +testing. :: + + $ echo 'fix fix fix' > file1 + $ hg commit -m 'prelim change' + $ hg push ../test-repo + +At this point, ``dev-repo`` and ``test-repo`` have the same changesets +in the same phases: + + [figure SG01: rev 0:0dc9 public, rev 1:f649 draft, same on both repos] + +(You may notice a change in notation from the user guide: now +changesets are labelled with their revision number and the first four +digits of the 40-digit hexadecimal changeset ID. Mercurial revision +numbers are never stable when working across repositories, especially +when obsolescence is involved. We'll see why shortly.) + +Now let's switch to ``test-repo`` to test our change:: + + $ cd ../test-repo + $ hg update + +Don't forget to ``hg update``! Pushing only adds changesets to a +remote repository; it does not update the working directory (unless +you have a hook that updates for you). + +Now let's imagine the tests failed because we didn't use proper +punctuation and capitalization (oops). Let's amend our preliminary fix +(and fix the lame commit message while we're at it):: + + $ echo 'Fix fix fix.' > file1 + $ hg amend -m 'fix bug 37' + +Now we're in a funny intermediate state (figure 2): revision 1:f649 is +obsolete in ``test-repo``, having been replaced by revision 3:60ff +(revision 2:2a03 is another one of those temporary amend commits that +we saw in the user guide)—but ``dev-repo`` knows nothing of these +recent developments. + + [figure SG02: rev 0:0dc9 public, rev 1:f649, 2:2a03 obsolete, rev 3:60ff draft -- but dev-repo same as in SG01] + +Let's resynchronize:: + + $ cd ../dev-repo + $ hg pull -u + +As seen in figure 3, this transfers the new changeset *and* the +obsolescence marker for revision 1. However, it does *not* transfer +the temporary amend commit, because it is obsolete. Push and pull +transfer obsolesence markers between repositories, but they do not +normally transfer obsolete changesets. + + [figure SG03: dev-repo grows new rev 2:60ff, marks 1:f649 obsolete] + +Because of this deliberately incomplete synchronization, revision +numbers in ``test-repo`` and ``dev-repo`` are no longer consistent. We +*must* use changeset IDs. + +Amend again, locally +-------------------- + +This process can repeat. Perhaps you figure out a more elegant fix to +the bug, and want to mutate history so nobody ever knows you had a +less-than-perfect idea. We'll implement it locally in ``dev-repo`` and +push to ``test-repo``:: + + $ echo 'Fix, fix, and fix.' > file1 + $ hg amend + $ hg push + +This time around, the temporary amend commit is in ``dev-repo``, and +it is not transferred to ``test-repo``—the same as before, just in the +opposite direction. Figure 4 shows the two repositories after amending +in ``dev-repo`` and pushing to ``test-repo``. + + [figure SG04: each repo has one temporary amend commit, but they're different in each one] + +Let's hop over to ``test-repo`` to test the more elegant fix:: + + $ cd ../test-repo + $ hg update -q + +This time, all the tests pass, so no further amendment is required. +This bug fix is finished, so we push it to the public repository:: + + $ hg push + [...] + added 1 changesets with 1 changes to 1 files + +Note that only one changeset—the final version, after two +amendments—was actually pushed. Again, Mercurial normally doesn't +transfer obsolete changesets on push and pull. (Specifically, it +doesn't transfer *hidden* changesets: roughly speaking, obsolete +changesets with no non-obsolete descendants. If you're curious, see +the `concept guide`_ for the precise definition of hidden.) + +.. _`concept guide`: concepts.html + +So the picture in ``public`` is much simpler than in either +``dev-repo`` or ``test-repo``. None of our missteps or amendments are +visible publicly, just the final, beautifully polished changeset: + + [figure SG05: public repo with rev 0:0dc9, 1:de61, both public] + +There is one important step left to do. Because we pushed from +``test-repo`` to ``public``, the pushed changeset is in *public* phase +in those two repositories. But ``dev-repo`` knows nothing of this: +that changeset is still *draft* there. If we're not careful, we might +mutate history in ``dev-repo``, obsoleting a changeset that is already +public. Let's avoid that situation for now by pulling from +``test-repo`` down to ``dev-repo``:: + + $ cd ../dev-repo + $ hg pull -u + +Getting into trouble +-------------------- + +Mercurial with ``evolve`` is a powerful tool, and using powerful tools +can have consequences. (You can cut yourself badly with a sharp knife, +but every competent chef keeps several around. Ever try to chop onions +with a spoon?) + +In the user guide, we saw examples of *unstable* changesets, which are +the most common type of troubled changeset. (Recall that a +non-obsolete changeset with obsolete ancestors is unstable.) + +Two other types of trouble can crop up: *bumped* and *divergent* +changesets. Both are more likely with shared mutable history, +especially mutable history shared by multiple developers. + +To demonstrate, let's start with the ``public`` repository as we left +it in the last example, with two immutable changesets (figure 5 +above). Two developers, Alice and Bob, start working from this point:: + + $ hg clone -q public alice + $ hg clone -q public bob + +We need to configure Alice's and Bob's working repositories similar to +``test-repo``, i.e. make them non-publishing and enable ``evolve``:: + + $ cat >> alice/.hg/hgrc <<EOF + [phases] + publish = false + [extensions] + rebase = + evolve = /path/to/mutable-history/hgext/evolve.py + EOF + $ cp alice/.hg/hgrc bob/.hg/hgrc + +Bumped changesets: only one gets on the plane +--------------------------------------------- + +If two people show up at the airport with tickets for the same seat on +the same plane, only one of them gets on the plane. The would-be +traveller left behind in the airport terminal is said to have been +*bumped*. + +Similarly, if Alice and Bob are collaborating on some mutable +changesets, it's possible to get into a situation where an otherwise +worthwhile changeset cannot be pushed to the public repository; it is +bumped by an alternative changeset that happened to get there first. +Let's demonstrate one way this could happen. + +It starts with Alice committing a bug fix. Right now, we don't yet +know if this bug fix is good enough to push to the public repository, +but it's good enough for Alice to commit. :: + + $ cd alice + $ echo 'fix' > file2 + $ hg commit -q -A -m 'fix bug 15' + +Now Bob has a bad idea: he decides to pull whatever Alice is working +on and tweak her bug fix to his taste:: + + $ cd ../bob + $ hg pull -q -u ../alice + $ echo 'Fix.' > file2 + $ hg amend -q -A -m 'fix bug 15 (amended)' + +(Note the lack of communication between Alice and Bob. Failing to +communicate with your colleagues is a good way to get into trouble. +Nevertheless, ``evolve`` can usually sort things out, as we will see.) + + [figure SG06: Bob's repo with one amendment] + +After some testing, Alice realizes her bug fix is just fine as it is: +no need for further polishing and amending, this changeset is ready to +publish. :: + + $ cd ../alice + $ hg push -q + +This introduces a contradiction: in Bob's repository, changeset 2:e011 +(his copy of Alice's fix) is obsolete, since Bob amended it. But in +Alice's repository (and ``public``), that changeset is public: it is +immutable, carved in stone for all eternity. No changeset can be both +obsolete and public, so Bob is in for a surprise the next time he +pulls from ``public``:: + + $ cd ../bob + $ hg pull -q -u + 1 new bumped changesets + +Figure 7 shows what just happened to Bob's repository: changeset +2:e011 is now public, so it can't be obsolete. When that changeset was +obsolete, it made perfect sense for it to have a successor, namely +Bob's amendment of Alice's fix (changeset 4:fe88). But it's illogical +for a public changeset to have a successor, so 4:fe88 is in trouble: +it has been *bumped*. + + [figure SG07: 2:e011 now public not obsolete, 4:fe88 now bumped] + +As usual when there's trouble in your repository, the solution is to +evolve it:: + + $ hg evolve --all + +Figure 8 illustrate's Bob's repository after evolving away the bumped +changeset. Ignoring the obsolete changesets, Bob now has a nice, +clean, simple history. His amendment of Alice's bug fix lives on, as +changeset 5:227d—albeit with a software-generated commit message. (Bob +should probably amend that changeset to improve the commit message.) +But the important thing is that his repository no longer has any +troubled changesets, thanks to ``evolve``. + + [figure SG08: 5:227d is new, formerly bumped changeset 4:fe88 now hidden] + +Divergent changesets +-------------------- + +In addition to *unstable* and *bumped*, there is a third kind of +troubled changeset: *divergent*. When an obsolete changeset has two +successors, those successors are divergent. + +To illustrate, let's start Alice and Bob at the same +point—specifically, the point where Alice's repository currently +stands. Bob's repository is a bit of a mess, so we'll throw it away +and start him off with a copy of Alice's repository:: + + $ cd .. + $ rm -rf bob + $ cp -rp alice bob + +Now we'll have Bob commit a bug fix that could still be improved:: + + $ cd bob + $ echo 'pretty good fix' >> file1 + $ hg commit -u bob -m 'fix bug 24 (v1)' + +This time, Alice meddles with her colleague's work (still a bad +idea):: + + $ cd ../alice + $ hg pull -q -u ../bob + $ echo 'better (alice)' >> file1 + $ hg amend -u alice -m 'fix bug 24 (v2 by alice)' + +Here's where things change from the "bumped" scenario above: this +time, the original author (Bob) decides to amend his changeset too. :: + + $ cd ../bob + $ echo 'better (bob)' >> file1 + $ hg amend -u bob -m 'fix bug 24 (v2 by bob)' + +At this point, the divergence exists, but only in theory: Bob's +original changeset, 3:fe81, is obsolete and has two successors. But +those successors are in different repositories, so the trouble is not +visible to anyone yet. It will be as soon as one of our players pulls +from the other's repository. Let's make Bob the victim again:: + + $ hg pull -q -u ../alice + not updating: not a linear update + (merge or update --check to force update) + 2 new divergent changesets + +The “not a linear update” is our first hint that something is wrong, +but of course “2 new divergent changesets” is the real problem. Figure +9 shows both problems. + + [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] + +Now we need to get out of trouble. Unfortunately, a `bug`_ in +``evolve`` means that the usual answer (run ``hg evolve --all``) does +not work. Bob has to figure out the solution on his own: in this case, +merge. To avoid distractions, we'll set ``HGMERGE`` to make Mercurial +resolve any conflicts in favour of Bob. :: + + $ HGMERGE=internal:local hg merge + $ hg commit -m merge + +.. _`bug`: https://bitbucket.org/marmoute/mutable-history/issue/48/ + +This is approximately what ``hg evolve`` would do in this +circumstance, if not for that bug. One annoying difference is that +Mercurial thinks the two divergent changesets are still divergent, +which you can see with a simple revset query:: + + $ hg log -q -r 'divergent()' + 5:fc16901f4d7a + 6:694fd0f6b503 + +(That annoyance should go away when the bug is fixed.) + +Conclusion +---------- + +Mutable history is a powerful tool. Like a sharp knife, an experienced +user can do wonderful things with it, much more wonderful than with a +dull knife (never mind a rusty spoon). At the same time, an +inattentive or careless user can do harm to himself or others. +Mercurial with ``evolve`` goes to great lengths to limit the harm you +can do by trying to handle all possible types of “troubled” +changesets. But having a first-aid kit nearby does not excuse you from +being careful with sharp knives. + +Mutable history shared across multiple repositories by a single +developer is a natural extension of this model. Once you are used to +using a single sharp knife on its own, it's pretty straightforward to +chop onions and mushrooms using the same knife, or to alternate +between two chopping boards with different knives. + +Mutable history shared by multiple developers is a scary place to go. +Imagine a professional kitchen full of expert chefs tossing their +favourite knives back and forth, with the occasional axe or chainsaw +thrown in to spice things up. If you're confident that you *and your +colleagues* can do it without losing a limb, go for it. But be sure to +practice a lot first before you rely on it!
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-sharing.t Fri Jun 06 17:19:17 2014 -0400 @@ -0,0 +1,314 @@ +Test script based on sharing.rst: ensure that all scenarios in that +document work as advertised. + +Setting things up + + $ cat >> $HGRCPATH <<EOF + > [alias] + > shortlog = log --template '{rev}:{node|short} {phase} {desc|firstline}\n' + > [extensions] + > rebase = + > EOF + $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH + $ hg init public + $ hg clone -q public test-repo + $ hg clone -q test-repo dev-repo + $ cat >> test-repo/.hg/hgrc <<EOF + > [phases] + > publish = false + > EOF + +To start things off, let's make one public, immutable changeset:: + + $ cd test-repo + $ echo 'my new project' > file1 + $ hg add file1 + $ hg commit -m'create new project' + $ hg push -q + +and pull that into the development repository:: + + $ cd ../dev-repo + $ hg pull -q -u + +Let's commit a preliminary change and push it to ``test-repo`` for +testing. :: + + $ echo 'fix fix fix' > file1 + $ hg commit -m'prelim change' + $ hg push -q ../test-repo + +Figure SG01 (roughly) + $ hg shortlog -G + @ 1:f6490818a721 draft prelim change + | + o 0:0dc9c9f6ab91 public create new project + +Now let's switch to test-repo to test our change and amend:: + $ cd ../test-repo + $ hg update -q + $ echo 'Fix fix fix.' > file1 + $ hg amend -m'fix bug 37' + +Figure SG02 + $ hg shortlog --hidden -G + @ 3:60ffde5765c5 draft fix bug 37 + | + | x 2:2a039763c0f4 draft temporary amend commit for f6490818a721 + | | + | x 1:f6490818a721 draft prelim change + |/ + o 0:0dc9c9f6ab91 public create new project + +Pull into dev-repo: obsolescence markers are transferred, but not +the new obsolete changeset. + $ cd ../dev-repo + $ hg pull -q -u + +Figure SG03 + $ hg shortlog --hidden -G + @ 2:60ffde5765c5 draft fix bug 37 + | + | x 1:f6490818a721 draft prelim change + |/ + o 0:0dc9c9f6ab91 public create new project + +Amend again in dev-repo + $ echo 'Fix, fix, and fix.' > file1 + $ hg amend + $ hg push -q + +Figure SG04 (dev-repo) + $ hg shortlog --hidden -G + @ 4:de6151c48e1c draft fix bug 37 + | + | x 3:ad19d3570adb draft temporary amend commit for 60ffde5765c5 + | | + | x 2:60ffde5765c5 draft fix bug 37 + |/ + | x 1:f6490818a721 draft prelim change + |/ + o 0:0dc9c9f6ab91 public create new project + +Figure SG04 (test-repo) + $ cd ../test-repo + $ hg update -q + $ hg shortlog --hidden -G + @ 4:de6151c48e1c draft fix bug 37 + | + | x 3:60ffde5765c5 draft fix bug 37 + |/ + | x 2:2a039763c0f4 draft temporary amend commit for f6490818a721 + | | + | x 1:f6490818a721 draft prelim change + |/ + o 0:0dc9c9f6ab91 public create new project + +This bug fix is finished. We can push it to the public repository. + $ hg push + pushing to $TESTTMP/public + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + OBSEXC: computing relevant nodes + OBSEXC: computing markers relevant to 2 nodes + OBSEXC: pushing 4 markers (341 bytes) + OBSEXC: DONE + +Figure SG05 + $ hg -R ../public shortlog -G + o 1:de6151c48e1c public fix bug 37 + | + o 0:0dc9c9f6ab91 public create new project + +Oops, still have draft changesets in dev-repo. + $ cd ../dev-repo + $ hg shortlog -r 'draft()' + 4:de6151c48e1c draft fix bug 37 + $ hg pull -q -u + $ hg shortlog -r 'draft()' + +Sharing by Alice and Bob to demonstrate bumped and divergent changesets. +First, setup repos for them. + + $ cd .. + $ hg clone -q public alice + $ hg clone -q public bob + $ cat >> alice/.hg/hgrc <<EOF + > [phases] + > publish = false + > EOF + $ cp alice/.hg/hgrc bob/.hg/hgrc + +Alice commits a bug fix. + $ cd alice + $ echo 'fix' > file2 + $ hg commit -q -A -u alice -m 'fix bug 15' + +Bob pulls and amends Alice's fix. + $ cd ../bob + $ hg pull -q -u ../alice + $ echo 'Fix.' > file2 + $ hg amend -q -A -u bob -m 'fix bug 15 (amended)' + +Figure SG06: Bob's repository after amending Alice's fix. +(Nothing new here; we could have seen this in the user guide. + $ hg --hidden shortlog -G + @ 4:fe884dfac355 draft fix bug 15 (amended) + | + | x 3:0376cac226f8 draft temporary amend commit for e011baf925da + | | + | x 2:e011baf925da draft fix bug 15 + |/ + o 1:de6151c48e1c public fix bug 37 + | + o 0:0dc9c9f6ab91 public create new project + + +But in the meantime, Alice decides the fix is just fine and publishes it. + $ cd ../alice + $ hg push -q + +Which means that Bob now has an formerly obsolete changeset that is +also public (2:6e83). As soon as he pulls its phase change, he's got +trouble: the successors of that formerly obsolete changeset are +bumped. + + $ cd ../bob + $ hg --hidden shortlog -r 'obsolete()' + 2:e011baf925da draft fix bug 15 + 3:0376cac226f8 draft temporary amend commit for e011baf925da + $ hg pull -q -u + 1 new bumped changesets + $ hg --hidden shortlog -r 'obsolete()' + 3:0376cac226f8 draft temporary amend commit for e011baf925da + $ hg shortlog -r 'bumped()' + 4:fe884dfac355 draft fix bug 15 (amended) + +Figure SG07: Bob's repo with one bumped changeset (rev 4:c02d) + $ hg --hidden shortlog -G + @ 4:fe884dfac355 draft fix bug 15 (amended) + | + | x 3:0376cac226f8 draft temporary amend commit for e011baf925da + | | + | o 2:e011baf925da public fix bug 15 + |/ + o 1:de6151c48e1c public fix bug 37 + | + o 0:0dc9c9f6ab91 public create new project + + +Bob gets out of trouble by evolving the repository. + $ hg evolve --all + recreate:[4] fix bug 15 (amended) + atop:[2] fix bug 15 + computing new diff + committed as 227d860d9ad0 + +Figure SG08 + $ hg --hidden shortlog -G + @ 5:227d860d9ad0 draft bumped update to e011baf925da: + | + | x 4:fe884dfac355 draft fix bug 15 (amended) + | | + +---x 3:0376cac226f8 draft temporary amend commit for e011baf925da + | | + o | 2:e011baf925da public fix bug 15 + |/ + o 1:de6151c48e1c public fix bug 37 + | + o 0:0dc9c9f6ab91 public create new project + + +Throw away Bob's messy repo and start over. + $ cd .. + $ rm -rf bob + $ cp -rp alice bob + +Bob commits a pretty good fix that both he and Alice will amend, +leading to divergence. + $ cd bob + $ echo 'pretty good fix' >> file1 + $ hg commit -u bob -m 'fix bug 24 (v1)' + +Alice pulls Bob's fix and improves it. + $ cd ../alice + $ hg pull -q -u ../bob + $ echo 'better (alice)' >> file1 + $ hg amend -u alice -m 'fix bug 24 (v2 by alice)' + +Likewise, Bob amends his own fix. Now we have an obsolete changeset +with two successors, although the successors are in different repos. + $ cd ../bob + $ echo 'better (bob)' >> file1 + $ hg amend -u bob -m 'fix bug 24 (v2 by bob)' + +Bob pulls from Alice's repo and discovers the trouble: divergent changesets! + $ hg pull -q -u ../alice + not updating: not a linear update + (merge or update --check to force update) + 2 new divergent changesets + $ hg shortlog -r 'divergent()' + 5:fc16901f4d7a draft fix bug 24 (v2 by bob) + 6:694fd0f6b503 draft fix bug 24 (v2 by alice) + +Figure SG09 + $ hg --hidden shortlog -G + o 6:694fd0f6b503 draft fix bug 24 (v2 by alice) + | + | @ 5:fc16901f4d7a draft fix bug 24 (v2 by bob) + |/ + | x 4:162612d3335b draft temporary amend commit for fe81d904ed08 + | | + | x 3:fe81d904ed08 draft fix bug 24 (v1) + |/ + o 2:e011baf925da public fix bug 15 + | + o 1:de6151c48e1c public fix bug 37 + | + o 0:0dc9c9f6ab91 public create new project + +Merge the trouble away. + $ HGMERGE=internal:local hg merge + 0 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -m merge + $ hg shortlog -G + @ 7:b1d30ba26e44 draft merge + |\ + | o 6:694fd0f6b503 draft fix bug 24 (v2 by alice) + | | + o | 5:fc16901f4d7a draft fix bug 24 (v2 by bob) + |/ + o 2:e011baf925da public fix bug 15 + | + o 1:de6151c48e1c public fix bug 37 + | + o 0:0dc9c9f6ab91 public create new project + + $ hg log -q -r 'divergent()' + 5:fc16901f4d7a + 6:694fd0f6b503 + +# XXX hg evolve does not solve this trouble! bug in evolve? +#Evolve the trouble away. +# $ HGMERGE=internal:local hg evolve --all +# merge:[5] fix bug 24 (v2 by bob) +# with: [6] fix bug 24 (v2 by alice) +# base: [3] fix bug 24 (v1) +# 0 files updated, 1 files merged, 0 files removed, 0 files unresolved +# $ hg status +# $ hg shortlog -G +# o 6:694fd0f6b503 draft fix bug 24 (v2 by alice) +# | +# | @ 5:fc16901f4d7a draft fix bug 24 (v2 by bob) +# |/ +# o 2:e011baf925da public fix bug 15 +# | +# o 1:de6151c48e1c public fix bug 37 +# | +# o 0:0dc9c9f6ab91 public create new project +# +# $ hg --hidden shortlog -G