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 enable the experimental extensions for mutable history:
$ cat >> $HGRCPATH <<EOF
> [extensions]
> rebase =
> obsolete = $TESTDIR/../hgext/obsolete.py
> evolve = $TESTDIR/../hgext/evolve.py
> EOF
-----------------------
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 unstable 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 evolve' to get a stable history or --force to ignore warnings)
[255]
To resolve this unstable state, you need to rebase 9ac5d0e790a2 onto
ffa278c50818 the "hg evolve" command will make this for you.
It has a --dry-run option to only suggest the next move.
$ hg evolve --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 unstable 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
$ hg evolve --any
move:[17] SPAM SPAM SPAM
atop:[14] bathroom stuff
merging shopping
$ hg log -G
@ d6717f710962 (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.