docs/tutorials/tutorial.t
author Pierre-Yves David <pierre-yves.david@logilab.fr>
Wed, 25 Jul 2012 17:53:17 +0200
changeset 377 1d6cc8c22cd9
parent 367 5bc3e5dc2637
parent 374 2c969d1b18e0
child 390 54582eebeb08
permissions -rw-r--r--
merge with stable fix and preparation


Initial setup
-------------

This Mercurial configuration example is used for testing.
.. Various setup

  $ cat >> $HGRCPATH << EOF
  > [ui]
  > logtemplate ="{node|short} ({phase}): {desc}\n"
  > [diff]
  > git = 1
  > [alias]
  > # "-d '0 0'" means that the new commit will be at January 1st 1970.
  > # This is used for stable hash during test
  > amend = amend -d '0 0'
  > [extensions]
  > hgext.graphlog=
  > EOF

  $ hg init local
  $ cat >> local/.hg/hgrc << EOF
  > [paths]
  > remote = ../remote
  > other = ../other
  > [ui]
  > user = Babar the King
  > EOF

  $ hg init remote
  $ cat >> remote/.hg/hgrc << EOF
  > [paths]
  > local = ../local
  > [ui]
  > user = Celestine the Queen
  > EOF

  $ hg init other
  $ cat >> other/.hg/hgrc << EOF
  > [ui]
  > user = Princess Flore
  > EOF


This tutorial use the following configuration for Mercurial:

A compact log template with phase data:

  $ hg showconfig ui
  ui.slash=True
  ui.logtemplate="{node|short} ({phase}): {desc}\n"

Improved git format diff:

  $ hg showconfig diff
  diff.git=1

And the graphlog extension
  $ hg showconfig extensions
  extensions.hgext.graphlog=

And of course, we anabled the experimental extensions for mutable history:

  $ $(dirname $TESTDIR)/enable.sh >> $HGRCPATH 2> /dev/null


-----------------------
Single Developer Usage
-----------------------

This tutorial shows how to use evolution to rewrite history locally.


Fixing mistake with `hg amend`
--------------------------------

We are versionning a shopping list

  $ cd local
  $ cat  >> shopping << EOF
  > Spam
  > Whizzo butter
  > Albatross
  > Rat (rather a lot)
  > Jugged fish
  > Blancmange
  > Salmon mousse
  > EOF
  $ hg commit -A -m "Monthy Python Shopping list"
  adding shopping

Its first version is shared with the outside.

  $ hg push remote
  pushing to $TESTTMP/remote
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 1 changesets with 1 changes to 1 files

Later I add additional item to my list

  $ cat >> shopping << EOF
  > Egg
  > Suggar
  > Vinegar
  > Oil
  > EOF
  $ hg commit -m "adding condiment"
  $ cat >> shopping << EOF
  > Bananos
  > Pear
  > Apple
  > EOF
  $ hg commit -m "adding fruit"

This history is very linear

  $ hg glog
  @  d85de4546133 (draft): adding fruit
  |
  o  4d5dc8187023 (draft): adding condiment
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

But a typo was made in Babanas!

  $ hg export tip
  # HG changeset patch
  # User test
  # Date 0 0
  # Node ID d85de4546133030c82d257bbcdd9b1b416d0c31c
  # Parent  4d5dc81870237d492284826e21840b2ca00e26d1
  adding fruit
  
  diff --git a/shopping b/shopping
  --- a/shopping
  +++ b/shopping
  @@ -9,3 +9,6 @@
   Suggar
   Vinegar
   Oil
  +Bananos
  +Pear
  +Apple

The faulty changeset is in the "draft" phase because he was not exchanged with
the outside. The first one have been exchanged and is an immutable public
changeset.

  $ hg glog
  @  d85de4546133 (draft): adding fruit
  |
  o  4d5dc8187023 (draft): adding condiment
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

hopefully. I can use hg amend to rewrite my faulty changeset!

  $ sed -i'' -e s/Bananos/Banana/ shopping
  $ hg diff
  diff --git a/shopping b/shopping
  --- a/shopping
  +++ b/shopping
  @@ -9,6 +9,6 @@
   Suggar
   Vinegar
   Oil
  -Bananos
  +Banana
   Pear
   Apple
  $ hg amend

A new changeset with the right diff replace the wrong one.

  $ hg glog
  @  0cacb48f4482 (draft): adding fruit
  |
  o  4d5dc8187023 (draft): adding condiment
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  
  $ hg export tip
  # HG changeset patch
  # User test
  # Date 0 0
  # Node ID 0cacb48f44828d2fd31c4e45e18fde32a5b2f07b
  # Parent  4d5dc81870237d492284826e21840b2ca00e26d1
  adding fruit
  
  diff --git a/shopping b/shopping
  --- a/shopping
  +++ b/shopping
  @@ -9,3 +9,6 @@
   Suggar
   Vinegar
   Oil
  +Banana
  +Pear
  +Apple

Getting Ride of branchy history
----------------------------------

While I was working on my list. someone help made a change remotly.

  $ cd ../remote
  $ hg up -q
  $ sed -i'' -e 's/Spam/Spam Spam Spam/' shopping
  $ hg ci -m 'SPAM'
  $ cd ../local

I'll get this remote changeset when pulling

  $ hg pull remote
  pulling from $TESTTMP/remote
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 1 changesets with 1 changes to 1 files (+1 heads)
  (run 'hg heads .' to see heads, 'hg merge' to merge)

I now have a new heads. Note that this remote head is immutable

  $ hg log -G
  o  9ca060c80d74 (public): SPAM
  |
  | @  0cacb48f4482 (draft): adding fruit
  | |
  | o  4d5dc8187023 (draft): adding condiment
  |/
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

instead of merging my head with the new one. I'm going to rebase my work

  $ hg diff
  $ hg rebase -d 9ca060c80d74 -s 4d5dc8187023
  merging shopping
  merging shopping


My local work is now rebased on the remote one.

  $ hg log -G
  @  387187ad9bd9 (draft): adding fruit
  |
  o  dfd3a2d7691e (draft): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

Removing changeset
------------------------

I add new item to my list

  $ cat >> shopping << EOF
  > car
  > bus
  > plane
  > boat
  > EOF
  $ hg ci -m 'transport'
  $ hg log -G
  @  d58c77aa15d7 (draft): transport
  |
  o  387187ad9bd9 (draft): adding fruit
  |
  o  dfd3a2d7691e (draft): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

I have a new commit but I realize that don't want it. (transport shop list does
not fit well in my standard shopping list)

  $ hg prune . # . is for working directory parent
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  working directory now at 387187ad9bd9

The silly changeset is gone.

  $ hg log -G
  @  387187ad9bd9 (draft): adding fruit
  |
  o  dfd3a2d7691e (draft): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

Reordering changeset
------------------------


We create two changesets.


  $ cat >> shopping << EOF
  > Shampoo
  > Toothbrush
  > ... More bathroom stuff to come
  > Towel
  > Soap
  > EOF
  $ hg ci -m 'bathroom stuff' -q # XXX remove the -q

  $ sed -i'' -e 's/Spam/Spam Spam Spam/g' shopping
  $ hg ci -m 'SPAM SPAM'
  $ hg log -G
  @  c48f32fb1787 (draft): SPAM SPAM
  |
  o  8d39a843582d (draft): bathroom stuff
  |
  o  387187ad9bd9 (draft): adding fruit
  |
  o  dfd3a2d7691e (draft): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

.. note: don't amend changeset 7e82d3f3c2cb or 9ca060c80d74 as they are immutable.

I now want to push to remote all my change but the bathroom one that i'm not
totally happy with yet. To be able to push "SPAM SPAM" I need a version of "SPAM SPAM" not children of
"bathroom stuff"

You can use 'rebase -r' or 'graft -O' for that:

  $ hg up 'p1(8d39a843582d)' # going on "bathroom stuff" parent
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ hg graft -O c48f32fb1787 # moving "SPAM SPAM" to the working directory parent
  grafting revision 10
  merging shopping
  $ hg log -G
  @  a2fccc2e7b08 (draft): SPAM SPAM
  |
  | o  8d39a843582d (draft): bathroom stuff
  |/
  o  387187ad9bd9 (draft): adding fruit
  |
  o  dfd3a2d7691e (draft): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

We have a new SPAM SPAM version without the bathroom stuff

  $ grep Spam shopping  # enouth spam
  Spam Spam Spam Spam Spam Spam Spam Spam Spam
  $ grep Toothbrush shopping # no Toothbrush
  [1]
  $ hg export .
  # HG changeset patch
  # User test
  # Date 0 0
  # Node ID a2fccc2e7b08bbce6af7255b989453f7089e4cf0
  # Parent  387187ad9bd9d8f9a00a9fa804a26231db547429
  SPAM SPAM
  
  diff --git a/shopping b/shopping
  --- a/shopping
  +++ b/shopping
  @@ -1,4 +1,4 @@
  -Spam Spam Spam
  +Spam Spam Spam Spam Spam Spam Spam Spam Spam
   Whizzo butter
   Albatross
   Rat (rather a lot)

To make sure I do not push unready changeset by mistake I set the "bathroom
stuff" changeset in the secret phase.

  $ hg phase --force --secret 8d39a843582d

we can now push our change:

  $ hg push remote
  pushing to $TESTTMP/remote
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 3 changesets with 3 changes to 1 files

for simplicity shake we get the bathroom change in line again

  $ hg rebase -Dr 8d39a843582d -d a2fccc2e7b08
  merging shopping
  $ hg phase --draft .
  $ hg log -G
  @  8a79ae8b029e (draft): bathroom stuff
  |
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  



Splitting change
------------------

To be done (currently achieve with "two commit + debugobsolete")

Collapsing change
------------------

To be done (currently achieve with "revert + debugobsolete" or "rebase --collapse")






-----------------------
Collaboration
-----------------------


sharing mutable changeset
----------------------------

To share mutable changeset with other just check that the repo you interact
with is "not publishing". Otherwise you will get the previously observe
behavior where exchanged changeset are automatically published.

  $ cd ../remote
  $ hg -R ../local/ showconfig phases

the localrepo does not have any specific configuration for `phases.publish`. It
is ``true`` by default.

  $ hg pull local
  pulling from $TESTTMP/local
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 1 changesets with 1 changes to 1 files
  (run 'hg update' to get a working copy)
  $ hg log -G
  o  8a79ae8b029e (public): bathroom stuff
  |
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  @  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  



We do not want to publish the "bathroom changeset". Let's rollback the last transaction

  $ hg rollback
  repository tip rolled back to revision 4 (undo pull)
  $ hg log -G
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  @  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

Let's make the local repo "non publishing"

  $ echo '[phases]' >> ../local/.hg/hgrc
  $ echo 'publish=false' >> ../local/.hg/hgrc
  $ echo '[phases]' >> .hg/hgrc
  $ echo 'publish=false' >> .hg/hgrc
  $ hg showconfig phases
  phases.publish=false
  $ hg -R ../local/ showconfig phases
  phases.publish=false


I can now exchange mutable changeset between "remote" and "local" repository.

  $ hg pull local
  pulling from $TESTTMP/local
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 1 changesets with 1 changes to 1 files
  (run 'hg update' to get a working copy)
  $ hg log -G
  o  8a79ae8b029e (draft): bathroom stuff
  |
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  @  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

Rebasing unstable change after pull
----------------------------------------------

Remotely someone add a new changeset on top of the mutable "bathroom" on.

  $ hg up 8a79ae8b029e -q
  $ cat >> shopping << EOF
  > Giraffe
  > Rhino
  > Lion
  > Bear
  > EOF
  $ hg ci -m 'animals'

But at the same time, locally, this same "bathroom changeset" was updated.

  $ cd ../local
  $ hg up 8a79ae8b029e -q
  $ sed -i'' -e 's/... More bathroom stuff to come/Bath Robe/' shopping
  $ hg amend
  $ hg log -G
  @  ffa278c50818 (draft): bathroom stuff
  |
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  


When we pull from remote again we get an unstable state!


  $ hg pull remote
  pulling from $TESTTMP/remote
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 1 changesets with 1 changes to 1 files (+1 heads)
  (run 'hg heads .' to see heads, 'hg merge' to merge)
  1 new unstables changesets


The new changeset "animal" is based one an old changeset of "bathroom". You can
see both version showing up in the log.

  $ hg log -G
  o  9ac5d0e790a2 (draft): animals
  |
  | @  ffa278c50818 (draft): bathroom stuff
  | |
  x |  8a79ae8b029e (draft): bathroom stuff
  |/
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

The older version 8a79ae8b029e never ceased to exist in the local repo. It was
jsut hidden and excluded from pull and push.

.. note:: In hgview there is a nice doted relation highlighting ffa278c50818 as a new version of 8a79ae8b029e. this is not yet ported to graphlog.

Their is **unstable** changeset in this history now. Mercurial will refuse to
share it with the outside:

  $ hg push other
  pushing to $TESTTMP/other
  searching for changes
  abort: push includes an unstable changeset: 9ac5d0e790a2!
  (use 'hg stabilize' to get a stable history (or --force to proceed))
  [255]
 



To resolve this unstable state, you need to rebase 9ac5d0e790a2 onto
ffa278c50818 the "hg stabilize" command will make this for you.

It has a --dry-run option to only suggest the next move.

  $ hg stabilize --dry-run
  move:[15] animals
  atop:[14] bathroom stuff
  hg rebase -Dr 9ac5d0e790a2 -d ffa278c50818

Let's do it

  $ hg rebase -Dr 9ac5d0e790a2 -d ffa278c50818
  merging shopping

The old version of bathroom is hidden again.

  $ hg log -G
  @  437efbcaf700 (draft): animals
  |
  o  ffa278c50818 (draft): bathroom stuff
  |
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  


We can push this evolution to remote

  $ hg push remote
  pushing to $TESTTMP/remote
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 2 changesets with 2 changes to 1 files (+1 heads)

remote get a warning that current working directory is based on an obsolete changeset

  $ cd ../remote
  $ hg pull local # we up again to trigger the warning. it was displayed during the push
  pulling from $TESTTMP/local
  searching for changes
  no changes found
  Working directory parent is obsolete

  $ hg up 437efbcaf700
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Relocating unstable change after prune
----------------------------------------------

The remote guy keep working

  $ sed -i'' -e 's/Spam/Spam Spam Spam Spam/g' shopping
  $ hg commit -m "SPAM SPAM SPAM"

I'm pulling its work locally.

  $ cd ../local
  $ hg pull remote
  pulling from $TESTTMP/remote
  searching for changes
  adding changesets
  adding manifests
  adding file changes
  added 1 changesets with 1 changes to 1 files
  (run 'hg update' to get a working copy)
  $ hg log -G
  o  ae45c0c3092a (draft): SPAM SPAM SPAM
  |
  @  437efbcaf700 (draft): animals
  |
  o  ffa278c50818 (draft): bathroom stuff
  |
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

In the mean time I noticed you can't buy animals in a super market and I prune the animal changeset:

  $ hg prune 437efbcaf700
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  working directory now at ffa278c50818
  1 new unstables changesets


The animals changeset is still displayed because the "SPAM SPAM SPAM" changeset
is neither dead or obsolete.  My repository is in an unstable state again.

  $ hg log -G
  o  ae45c0c3092a (draft): SPAM SPAM SPAM
  |
  x  437efbcaf700 (draft): animals
  |
  @  ffa278c50818 (draft): bathroom stuff
  |
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  

  $ hg log -r 'unstable()'
  ae45c0c3092a (draft): SPAM SPAM SPAM

# XXX make prune stabilization works
#  $ hg stabilize --any
#  merging shopping

  $ hg graft -O ae45c0c3092a
  grafting revision 17
  merging shopping

  $ hg log -G
  @  20de1fb1cec5 (draft): SPAM SPAM SPAM
  |
  o  ffa278c50818 (draft): bathroom stuff
  |
  o  a2fccc2e7b08 (public): SPAM SPAM
  |
  o  387187ad9bd9 (public): adding fruit
  |
  o  dfd3a2d7691e (public): adding condiment
  |
  o  9ca060c80d74 (public): SPAM
  |
  o  7e82d3f3c2cb (public): Monthy Python Shopping list
  


Handling Conflicting amend
----------------------------------------------

We can detect that multiple diverging//conflicting amend have been made. There
will be a "evol-merge" command to merge conflicting amend

This command is not ready yet.