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 patch-A
marked working directory as branch patch-A

This commits the patch to a new patch branch called patch-A:

$ hg branches
patch-A                        1:cdf5d3a110f6
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
@  patch-A
|
o  default

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

hg summary
$ hg summary
parent: 1:cdf5d3a110f6 tip
 start new patch on default
branch: patch-A
commit: (clean)
update: (current)
pbranch: patch-A (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
patch-A: default

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

.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/patch-A.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    patch-A: 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:f74eb14d1d12 tip
 update patch description
branch: patch-A
commit: (clean)
update: (current)
pbranch: patch-A (default)
 a nifty patch

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

$ hg diff --rev -2:-1
diff --git a/.hgpatchinfo/patch-A.desc b/.hgpatchinfo/patch-A.desc
new file mode 100644
--- /dev/null
+++ b/.hgpatchinfo/patch-A.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" patch-B
marked working directory as branch patch-B

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 patch-C:

$ hg branch patch-C
marked working directory as branch patch-C
$ hg commit --message "changes for C" 

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

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

So we add the branch to the patch graph:

$ echo "patch-C: patch-B" >>.hg/pgraph

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

$ hg pgraph --status
@  patch-C
|   * needs update of diff base to tip of patch-B
o  patch-B
|
o  patch-A
|
o  default

as is also indicated by the summary:

$ hg summary
parent: 5:68bf29f4e1a6 tip
 changes for C
branch: patch-C
commit: (clean)
update: (current)
pbranch: patch-C (patch-B)
pmerge: 1 pending
pgraph: desired != tips

So we do this:

$ hg pmerge
patch-C: 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 patch-A:

$ hg pdiff patch-A
# 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 patch-B:

$ hg pdiff patch-B
# 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 patch-C 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
@  patch-C
|
o  patch-B
|
o  patch-A
|
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 patch-A
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" 
created new head
hg pstatus

This creates a new head as we have not yet merged the new change into patch-B and patch-C:

$ hg pstatus patch-B
needs merge with patch-A
needs update of diff base to tip of patch-A
$ hg pstatus patch-C
needs merge with patch-A (through patch-B)

Or to see all at a glance:

$ hg pgraph --status
o  patch-C
|   * needs merge with patch-A (through patch-B)
o  patch-B
|   * needs merge with patch-A
|   * needs update of diff base to tip of patch-A
@  patch-A
|
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 patch-B
# 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 patch-C:

$ hg update patch-C
8 files updated, 0 files merged, 0 files removed, 0 files unresolved
needs merge with patch-A (through patch-B)
use 'hg pmerge'

and again by the summary (while we’re at patch-C):

$ hg summary
parent: 7:10526cbf6ae4
 update patch description
branch: patch-C
commit: (clean)
update: (current)
pbranch: patch-C (patch-B)
 yet another patch
pmerge: 1 pending

Merge Into Later Patches

hg pmerge

hg pmerge merges the new head from patch-A first into patch-B, and then into patch-C:

$ hg pmerge
updating to patch-A
2 files updated, 0 files merged, 6 files removed, 0 files unresolved
patch-B: merging from patch-A
marked working directory as branch patch-B
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
patch-C: merging from patch-B
marked working directory as branch patch-C
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg glog
@    10    patch-C: merge of patch-B - john
|\
| o    9    patch-B: merge of patch-A - john
| |\
| | o  8    patch-A: second try in A - john
| | |
o | |  7    patch-C: update patch description - john
| | |
o | |  6    patch-C: update patch dependencies - john
| | |
o | |  5    patch-C: changes for C - john
|/ /
o |  4    patch-B: second try in B - john
| |
o |  3    patch-B: start new patch on patch-A - john
|/
o  2    patch-A: update patch description - john
|
o  1    patch-A: start new patch on default - john
|
o  0    : base - john

We can now make a change in patch-C that depends on the change in patch-A:

$ 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    patch-C: dependent change in C - john
|
o    10    patch-C: merge of patch-B - john
|\
| o    9    patch-B: merge of patch-A - john
| |\
| | o  8    patch-A: second try in A - john
| | |
o | |  7    patch-C: update patch description - john
| | |

which results in the following final diffs:

$ hg pdiff patch-A
# 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 patch-C
# 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