Inserting and Deleting Patches

Inserting a Patch

Scenario

First, we pick up the base scenario:

$ hg clone scenario-base insert
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd insert
$ hg pgraph
created graph description from current tips
o  patch-C
|
o  patch-B
|
o  patch-A
|
@  default

Create New Patch

We want to insert a patch between patch-B and patch-C. So we update to patch-B and create a new patch branch with our change:

$ hg update patch-B
7 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ echo "B1" >b1
$ hg add b1
$ hg pnew --text "inserted patch" patch-B1
marked working directory as branch patch-B1
created new head

Now we update the dependency info for patch-C:

$ cat >.hg/pgraph <<-eof
    patch-C: patch-B1
    patch-B1: patch-B
    patch-B: patch-A
    patch-A: default
eof

Here’s the new graph:

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

So now we need to merge patch-B1 into patch-C to properly establish the new dependency (keeping the repo for another scenario):

$ hg update patch-C
4 files updated, 0 files merged, 3 files removed, 0 files unresolved
needs merge with patch-B1
needs update of diff base to tip of patch-B1
use 'hg pmerge'
$ hg pmerge
updating to patch-B1
4 files updated, 0 files merged, 3 files removed, 0 files unresolved
patch-C: merging from patch-B1
marked working directory as branch patch-C
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg clone . ../delete
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

And can commit something that depends on the file introduced by patch-B1 (again, keeping the repo):

$ echo "More" >>b1
$ hg commit --message "c based on b1" 
$ hg clone . ../delete-conflict
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Big Picture

$ hg pgraph
@  patch-C
|
o  patch-B1
|
o  patch-B
|
o  patch-A
|
o  default
$ hg glog --rev patch-B:tip
@  10    patch-C: c based on b1 - john
|
o    9    patch-C: merge of patch-B1 - john
|\
| o  8    patch-B1: start new patch on patch-B - 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
|

Results

$ hg pdiff patch-B1
# HG changeset patch
# User john
# Date 0 0
inserted patch

diff --git a/b1 b/b1
new file mode 100644
--- /dev/null
+++ b/b1
@@ -0,0 +1,1 @@
+B1
$ hg pdiff patch-C
# HG changeset patch
# User john
# Date 0 0
yet another patch

diff --git a/b1 b/b1
--- a/b1
+++ b/b1
@@ -2,0 +2,1 @@
+More
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

Folding a Patch Into Another

Let’s get rid of patch-B1 again, folding the changes it introduces into patch-C. This is very simple. We just make patch-C depend on patch-B again:

$ cat >.hg/pgraph <<-eof
    patch-C: patch-B
    patch-B: patch-A
    patch-A: default
eof

By not listing patch-B1 in the graph description anymore, we automatically demoted it from patch back to plain branch in our view of the patch graph. So it’s gone here:

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

But it’s still there as a branch, of course:

$ hg branches
patch-C                       10:22e5a4e588e0
patch-B1                       8:b2089dcc79c9 (inactive)
patch-B                        4:ece9ac1bb19f (inactive)
patch-A                        2:f74eb14d1d12 (inactive)
default                        0:527edfea671a (inactive)

There is no pending merge here, as patch-C is already fully merged with patch-B (formerly through patch-B1). But we do need to make the dependency change permanent:

$ hg pmerge
patch-C: updating dependencies

For this final history:

$ hg glog --rev patch-B:tip
@  11    patch-C: update patch dependencies - john
|
o  10    patch-C: c based on b1 - john
|
o    9    patch-C: merge of patch-B1 - john
|\
| o  8    patch-B1: start new patch on patch-B - 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
|

Since we already merged the changes introduced by patch-B1 into patch-C, they now automatically appear in patch-C (relative to patch-B):

$ hg pdiff patch-C b1
# HG changeset patch
# User john
# Date 0 0
yet another patch

diff --git a/b1 b/b1
new file mode 100644
--- /dev/null
+++ b/b1
@@ -0,0 +1,2 @@
+B1
+More

However, much as the patch is still there as a branch, it’s also still there in the patch graph inferred from all the branch tips:

$ hg pgraph --tips
o  patch-B1
|
| @  patch-C
|/
o  patch-B
|
o  patch-A
|
o  default

And this is what people will get as their default .hg/pgraph file when they pull from us. To get rid of the patch there as well, we simply close its branch:

$ hg up patch-B1
2 files updated, 0 files merged, 3 files removed, 0 files unresolved
$ hg commit --close-branch -m "closed" 
created new head

and get:

$ hg pgraph --tips
o  patch-C
|
o  patch-B
|
o  patch-A
|
o  default

Deleting a Patch

This time, let’s get rid of patch-B1, truly deleting the changes it introduces. It’s a multi-step process around patch-B1:

We first do this in the scenario with no conflicts (no changes depend on changes in patch-B1):

$ cd ../delete
$ hg pgraph # ensure the patch graph is up to date
created graph description from current tips
o  patch-C
|
o  patch-B1
|
o  patch-B
|
o  patch-A
|
@  default
$ hg update patch-B1
10 files updated, 0 files merged, 0 files removed, 0 files unresolved

Backout Changes

hg pbackout

We back out all the changes introduced by patch-B1:

$ hg pbackout
backing out to patch-B
created new head

This commits a backout changeset:

$ hg log --rev tip
10    patch-B1: backout patch branch patch-B1 - john

Aside: If we display the graph now, patch-B1 appears as a new root since we backed out its dependency info, too:

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

Merge Into Children

Then we merge the backout into patch-C:

$ hg update patch-C
5 files updated, 0 files merged, 0 files removed, 0 files unresolved
needs merge with patch-B1
needs update of diff base to tip of patch-B1
use 'hg pmerge'
$ hg pmerge
updating to patch-B1
1 files updated, 0 files merged, 4 files removed, 0 files unresolved
patch-C: merging from patch-B1
marked working directory as branch patch-C
4 files updated, 0 files merged, 0 files removed, 0 files unresolved

Remove From Patch Graph

And now we do as when folding the (now empty) patch-B1:

$ hg up patch-B1
1 files updated, 0 files merged, 3 files removed, 0 files unresolved
$ hg commit --close-branch -m "closed" 
created new head
$ cat >.hg/pgraph <<-eof
    patch-C: patch-B
    patch-B: patch-A
    patch-A: default
eof
$ hg pgraph --status
o  patch-C
|   * needs update of diff base to tip of patch-B
o  patch-B
|
o  patch-A
|
o  default
$ hg pmerge --all
updating to patch-C
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
patch-C: updating dependencies
$ hg pgraph --tips
@  patch-C
|
o  patch-B
|
o  patch-A
|
o  default

Results

Which leaves us with a diff for patch-C that does not contain the changes we had in patch-B1:

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

Big Picture

$ hg glog --rev patch-B:tip
@  13    patch-C: update patch dependencies - john
|
| o  12    patch-B1: closed - john
| |
o |  11    patch-C: merge of patch-B1 - john
|\|
| o  10    patch-B1: backout patch branch patch-B1 - john
| |
o |  9    patch-C: merge of patch-B1 - john
|\|
| o  8    patch-B1: start new patch on patch-B - 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
|

Deleting a Patch With Conflicts

We do this again, but this time with the version of patch-C that already depends on changes in patch-B1:

$ cd ../delete-conflict
$ hg pgraph # ensure the patch graph is up to date
created graph description from current tips
o  patch-C
|
o  patch-B1
|
o  patch-B
|
o  patch-A
|
@  default
$ hg update patch-B1
10 files updated, 0 files merged, 0 files removed, 0 files unresolved

Backout Changes

Again, we back out patch-B1, which works ok:

$ hg pbackout
backing out to patch-B
created new head

Merge Into Children

But the merge now prompts us for what to do about the conflict (we choose to keep the file b1):

$ hg update patch-C
5 files updated, 0 files merged, 0 files removed, 0 files unresolved
needs merge with patch-B1
needs update of diff base to tip of patch-B1
use 'hg pmerge'
$ hg pmerge
updating to patch-B1
1 files updated, 0 files merged, 4 files removed, 0 files unresolved
patch-C: merging from patch-B1
marked working directory as branch patch-C
remote changed b1 which local deleted
use (c)hanged version or leave (d)eleted? c
5 files updated, 0 files merged, 0 files removed, 0 files unresolved

etc. (as above).