Simple Usage

Creating Patch Branches

Setup Base Repo

We start with a plain base repo:

$ hg init main; cd main
$ cat >main-file-1 <<-eof
    One

    Two

    Three
eof
$ echo Two >main-file-2
$ hg commit --addremove --message base
adding main-file-1
adding main-file-2
$ cd ..

and clone this into a new repo where we do the patches:

$ hg clone main patches
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd patches

Create A Patch

Then we create our first patch branch. To begin with, we just do the changes that will be the initial version of the patch:

$ echo One >file-from-A
$ sed -i main-file-1 -e s/One/Eins/
$ hg add file-from-A

So this is what we would like our patch to be:

$ hg diff
diff --git a/file-from-A b/file-from-A
new file mode 100644
--- /dev/null
+++ b/file-from-A
@@ -0,0 +1,1 @@
+One
diff --git a/main-file-1 b/main-file-1
--- a/main-file-1
+++ b/main-file-1
@@ -1,1 +1,1 @@
-One
+Eins

To commit it as a new patch branch, we do:

hg pnew
$ hg pnew patchA
marked working directory as branch patchA
(branches are permanent and global, did you want a bookmark?)

This commits the patch to a new patch branch called patchA:

$ hg branches
patchA                         1:17a5f12cd1a7
default                        0:527edfea671a (inactive)

So the dirstate is clean now:

$ hg status

but we can still get our patch back using pdiff:

hg pdiff
$ hg pdiff
# HG changeset patch
# User john
# Date 0 0
_
diff --git a/file-from-A b/file-from-A
new file mode 100644
--- /dev/null
+++ b/file-from-A
@@ -0,0 +1,1 @@
+One
diff --git a/main-file-1 b/main-file-1
--- a/main-file-1
+++ b/main-file-1
@@ -1,1 +1,1 @@
-One
+Eins

The new patch is based on the branch we were on when we created it, so default in this case. Which is what pgraph tells us, too:

hg pgraph
$ hg pgraph
@  patchA
|
o  default

The summary also informs us that we are on a patch branch:

hg summary
$ hg summary
parent: 1:17a5f12cd1a7 tip
 start new patch on default
branch: patchA
commit: (clean)
update: (current)
pbranch: patchA (default)

Behind The Scenes

The pnew command does a number of things:

.hg/pgraph

The .hg/pgraph file lists the patch dependencies as we want them. You can edit it however you wish. Later, we shall do this. Right now, pnew added the new patch like this:

$ cat .hg/pgraph
patchA: default

This means we want patchA to be based on the default branch. The patch is now defined as the diff between default and patchA.

.hgpatchinfo/*.dep

The patch graph description defines the graph as we want it. However, in order to be able to revert to older patches (branch diffs) consistently, pbranch also embeds the current base revision in the patch branch itself, and commits this information:

$ hg manifest | grep hgpatch
.hgpatchinfo/patchA.dep

This is so we can later revert to a specific revision of the patch and get the exact diff back that was current then.

Add Commit Message

Let’s add a commit message to the patch, to be used when we finally submit the patch upstream:

$ hg peditmessage --text "a nifty patch" 

This is also committed to our patch branch:

$ hg log --rev tip
2    patchA: update patch description - john

We can get it back like this:

hg pmessage
$ hg pmessage
a nifty patch

The first line is also shown by the summary:

$ hg summary
parent: 2:4cac083bb95c tip
 update patch description
branch: patchA
commit: (clean)
update: (current)
pbranch: patchA (default)
 a nifty patch

The patch message info is written to .hgpatchinfo/patchA.desc:

$ hg diff --rev -2:-1
diff --git a/.hgpatchinfo/patchA.desc b/.hgpatchinfo/patchA.desc
new file mode 100644
--- /dev/null
+++ b/.hgpatchinfo/patchA.desc
@@ -0,0 +1,1 @@
+a nifty patch
\ No newline at end of file

You could also edit and commit this file by hand.

Create Second Patch

Let’s do this again for the second patch, this time adding the patch description right away:

$ echo Two >file-from-B
$ sed -i main-file-1 -e s/Two/Zwie/
$ hg add file-from-B
hg pnew—text
$ hg pnew --text "another patch" patchB
marked working directory as branch patchB
(branches are permanent and global, did you want a bookmark?)

Fix The Second Patch

However, there’s a typo. We spelled Zwie instead of Zwei:

$ hg pdiff | grep -F Zwie
+Zwie

Fixing this is very easy. Just change and commit:

$ sed -i main-file-1 -e s/Zwie/Zwei/
$ hg commit --message "second try in B" 
$ hg pdiff | grep -F Zwei
+Zwei

So to amend a patch, we simply commit again to its branch. This detailed history is what we want during patch evolution. But for the final submission to the main repo, we don’t want the details. So we use pdiff, which shows just the diff from the base to the tip of the patch branch (see also pexport and pemail).

Create Third Patch Manually

For the third patch, we are going to do pnew’s work by hand, so you can get a feeling for pbranch’s inner workings. First, we do the changes:

$ echo Three >file-from-C
$ sed -i main-file-1 -e s/Three/Drei/
$ hg add file-from-C

and commit them to a new branch patchC:

$ hg branch patchC
marked working directory as branch patchC
(branches are permanent and global, did you want a bookmark?)
$ hg commit --message "changes for C" 

If we try to get the patch now, we get an error:

$ hg pdiff
abort: branch patchC is not in the patch graph (missing pmerge? edit .hg/pgraph?)

So we add the branch to the patch graph:

$ echo "patchC: patchB" >>.hg/pgraph

Now, pgraph --status tells we still need to update the embedded base revision info:

$ hg pgraph --status
@  patchC
|   * needs update of diff base to tip of patchB
o  patchB
|
o  patchA
|
o  default

as is also indicated by the summary:

$ hg summary
parent: 5:54d7e24775b2 tip
 changes for C
branch: patchC
commit: (clean)
update: (current)
pbranch: patchC (patchB)
pmerge: 1 pending
pgraph: desired != tips

So we do this:

$ hg pmerge
patchC: updating dependencies

and finally update the commit message:

$ hg peditmessage --text "yet another patch" 

Viewing Patches

hg pdiff

As mentioned above, we use pdiff to view the current state of a patch. The user and date are taken from the last commit to the patch branch (unless overridden in the patch description). Here’s patchA:

$ hg pdiff patchA
# HG changeset patch
# User john
# Date 0 0
a nifty patch
_
diff --git a/file-from-A b/file-from-A
new file mode 100644
--- /dev/null
+++ b/file-from-A
@@ -0,0 +1,1 @@
+One
diff --git a/main-file-1 b/main-file-1
--- a/main-file-1
+++ b/main-file-1
@@ -1,1 +1,1 @@
-One
+Eins

then patchB:

$ hg pdiff patchB
# HG changeset patch
# User john
# Date 0 0
another patch
_
diff --git a/file-from-B b/file-from-B
new file mode 100644
--- /dev/null
+++ b/file-from-B
@@ -0,0 +1,1 @@
+Two
diff --git a/main-file-1 b/main-file-1
--- a/main-file-1
+++ b/main-file-1
@@ -3,1 +3,1 @@
-Two
+Zwei

and for patchC we can omit the patch name as its our current patch:

$ hg pdiff
# HG changeset patch
# User john
# Date 0 0
yet another patch
_
diff --git a/file-from-C b/file-from-C
new file mode 100644
--- /dev/null
+++ b/file-from-C
@@ -0,0 +1,1 @@
+Three
diff --git a/main-file-1 b/main-file-1
--- a/main-file-1
+++ b/main-file-1
@@ -5,1 +5,1 @@
-Three
+Drei
hg pgraph

To get an overview of the currently active patches, we do:

$ hg pgraph
@  patchC
|
o  patchB
|
o  patchA
|
o  default

We now save a copy of this repo so we can use it again later to play with different scenarios:

$ hg clone . ../scenario-base
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Modifying Earlier Patches

Modify The Earlier Patch

hg update

We can go back to an earlier patch to amend it. Since each patch is a branch, we just update to the desired branch:

$ hg update patchA
1 files updated, 0 files merged, 6 files removed, 0 files unresolved

and make our modifications there:

$ echo Later >>file-from-A
$ hg commit --message "second try in A" 
hg pstatus

This creates a new head as we have not yet merged the new change into patchB and patchC:

$ hg pstatus patchB
needs merge with patchA
needs update of diff base to tip of patchA
$ hg pstatus patchC
needs merge with patchA (through patchB)

Or to see all at a glance:

$ hg pgraph --status
o  patchC
|   * needs merge with patchA (through patchB)
o  patchB
|   * needs merge with patchA
|   * needs update of diff base to tip of patchA
@  patchA
|
o  default

The the dependent patches are, however, still shown as they were when we last committed to their patch branches (this is what the “needs update of diff base” is all about):

$ hg pdiff --tips patchB
# HG changeset patch
# User john
# Date 0 0
another patch
_
diff --git a/file-from-B b/file-from-B
new file mode 100644
--- /dev/null
+++ b/file-from-B
@@ -0,0 +1,1 @@
+Two
diff --git a/main-file-1 b/main-file-1
--- a/main-file-1
+++ b/main-file-1
@@ -3,1 +3,1 @@
-Two
+Zwei

We are also notified of the pending merge when we go back to patchC:

$ hg update patchC
8 files updated, 0 files merged, 0 files removed, 0 files unresolved
needs merge with patchA (through patchB)
use 'hg pmerge'

and again by the summary (while we’re at patchC):

$ hg summary
parent: 7:5761f57be712
 update patch description
branch: patchC
commit: (clean)
update: (current)
pbranch: patchC (patchB)
 yet another patch
pmerge: 1 pending

Merge Into Later Patches

hg pmerge

hg pmerge merges the new head from patchA first into patchB, and then into patchC:

$ hg pmerge
updating to patchA
2 files updated, 0 files merged, 6 files removed, 0 files unresolved
patchB: merging from patchA
marked working directory as branch patchB
(branches are permanent and global, did you want a bookmark?)
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
patchC: merging from patchB
marked working directory as branch patchC
(branches are permanent and global, did you want a bookmark?)
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg glog
@    10    patchC: merge of patchB - john
|\
| o    9    patchB: merge of patchA - john
| |\
| | o  8    patchA: second try in A - john
| | |
o | |  7    patchC: update patch description - john
| | |
o | |  6    patchC: update patch dependencies - john
| | |
o | |  5    patchC: changes for C - john
|/ /
o |  4    patchB: second try in B - john
| |
o |  3    patchB: start new patch on patchA - john
|/
o  2    patchA: update patch description - john
|
o  1    patchA: start new patch on default - john
|
o  0    : base - john

We can now make a change in patchC that depends on the change in patchA:

$ cat file-from-A
One
Later
$ sed -i file-from-A -e s/Later/EvenLater/
$ cat file-from-A
One
EvenLater
$ hg commit --message "dependent change in C" 

So:

$ hg glog --limit 5
@  11    patchC: dependent change in C - john
|
o    10    patchC: merge of patchB - john
|\
| o    9    patchB: merge of patchA - john
| |\
| o |  8    patchA: second try in A - john
| | |
o | |  7    patchC: update patch description - john
| | |

which results in the following final diffs:

$ hg pdiff patchA
# HG changeset patch
# User john
# Date 0 0
a nifty patch
_
diff --git a/file-from-A b/file-from-A
new file mode 100644
--- /dev/null
+++ b/file-from-A
@@ -0,0 +1,2 @@
+One
+Later
diff --git a/main-file-1 b/main-file-1
--- a/main-file-1
+++ b/main-file-1
@@ -1,1 +1,1 @@
-One
+Eins
$ hg pdiff patchC
# HG changeset patch
# User john
# Date 0 0
yet another patch
_
diff --git a/file-from-A b/file-from-A
--- a/file-from-A
+++ b/file-from-A
@@ -2,1 +2,1 @@
-Later
+EvenLater
diff --git a/file-from-C b/file-from-C
new file mode 100644
--- /dev/null
+++ b/file-from-C
@@ -0,0 +1,1 @@
+Three
diff --git a/main-file-1 b/main-file-1
--- a/main-file-1
+++ b/main-file-1
@@ -5,1 +5,1 @@
-Three
+Drei