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  patchC
|
o  patchB
|
o  patchA
|
@  default

Create New Patch

We want to insert a patch between patchB and patchC. So we update to patchB and create a new patch branch with our change:

$ hg update patchB
7 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ echo "B1" >b1
$ hg add b1
$ hg pnew --text "inserted patch" patchB1
marked working directory as branch patchB1
(branches are permanent and global, did you want a bookmark?)
created new head

Now we update the dependency info for patchC:

$ cat >.hg/pgraph <<-eof
    patchC: patchB1
    patchB1: patchB
    patchB: patchA
    patchA: default
eof

Here’s the new graph:

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

So now we need to merge patchB1 into patchC to properly establish the new dependency (keeping the repo for another scenario):

$ hg update patchC
4 files updated, 0 files merged, 3 files removed, 0 files unresolved
needs merge with patchB1
needs update of diff base to tip of patchB1
use 'hg pmerge'
$ hg pmerge
updating to patchB1
4 files updated, 0 files merged, 3 files removed, 0 files unresolved
patchC: merging from patchB1
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 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 patchB1 (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
@  patchC
|
o  patchB1
|
o  patchB
|
o  patchA
|
o  default
$ hg glog --rev patchB:tip
@  10    patchC: c based on b1 - john
|
o    9    patchC: merge of patchB1 - john
|\
| o  8    patchB1: start new patch on patchB - 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
|

Results

$ hg pdiff patchB1
# 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 patchC
# HG changeset patch
# User john
# Date 0 0
yet another patch
_
diff --git a/b1 b/b1
--- a/b1
+++ b/b1
@@ -1,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 patchB1 again, folding the changes it introduces into patchC. This is very simple. We just make patchC depend on patchB again:

$ cat >.hg/pgraph <<-eof
    patchC: patchB
    patchB: patchA
    patchA: default
eof

By not listing patchB1 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
@  patchC
|   * needs update of diff base to tip of patchB
o  patchB
|
o  patchA
|
o  default

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

$ hg branches
patchC                        10:c5b4d9f3c70a
patchB1                        8:bf69d431636e (inactive)
patchB                         4:572fc1ab3a17 (inactive)
patchA                         2:4cac083bb95c (inactive)
default                        0:527edfea671a (inactive)

There is no pending merge here, as patchC is already fully merged with patchB (formerly through patchB1). But we do need to make the dependency change permanent:

$ hg pmerge
patchC: updating dependencies

For this final history:

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

Since we already merged the changes introduced by patchB1 into patchC, they now automatically appear in patchC (relative to patchB):

$ hg pdiff patchC 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
@  patchC
|
| o  patchB1
|/
o  patchB
|
o  patchA
|
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 patchB1
2 files updated, 0 files merged, 3 files removed, 0 files unresolved
$ hg commit --close-branch -m "closed" 

and get:

$ hg pgraph --tips
o  patchC
|
o  patchB
|
o  patchA
|
o  default

Deleting a Patch

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

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

$ cd ../delete
$ hg pgraph # ensure the patch graph is up to date
created graph description from current tips
o  patchC
|
o  patchB1
|
o  patchB
|
o  patchA
|
@  default
$ hg update patchB1
10 files updated, 0 files merged, 0 files removed, 0 files unresolved

Backout Changes

hg pbackout

We back out all the changes introduced by patchB1:

$ hg pbackout
backing out to patchB
created new head

This commits a backout changeset:

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

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

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

Merge Into Children

Then we merge the backout into patchC:

$ hg update patchC
5 files updated, 0 files merged, 0 files removed, 0 files unresolved
needs merge with patchB1
needs update of diff base to tip of patchB1
use 'hg pmerge'
$ hg pmerge
updating to patchB1
1 files updated, 0 files merged, 4 files removed, 0 files unresolved
patchC: merging from patchB1
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

Remove From Patch Graph

And now we do as when folding the (now empty) patchB1:

$ hg up patchB1
1 files updated, 0 files merged, 3 files removed, 0 files unresolved
$ hg commit --close-branch -m "closed" 
$ cat >.hg/pgraph <<-eof
    patchC: patchB
    patchB: patchA
    patchA: default
eof
$ hg pgraph --status
o  patchC
|   * needs update of diff base to tip of patchB
o  patchB
|
o  patchA
|
o  default
$ hg pmerge --all
updating to patchC
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
patchC: updating dependencies
$ hg pgraph --tips
@  patchC
|
o  patchB
|
o  patchA
|
o  default

Results

Which leaves us with a diff for patchC that does not contain the changes we had in patchB1:

$ hg pdiff patchC
# 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 patchB:tip
@  13    patchC: update patch dependencies - john
|
| o  12    patchB1: closed - john
| |
o |  11    patchC: merge of patchB1 - john
|\|
| o  10    patchB1: backout patch branch patchB1 - john
| |
o |  9    patchC: merge of patchB1 - john
|\|
| o  8    patchB1: start new patch on patchB - 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
|

Deleting a Patch With Conflicts

We do this again, but this time with the version of patchC that already depends on changes in patchB1:

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

Backout Changes

Again, we back out patchB1, which works ok:

$ hg pbackout
backing out to patchB
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 patchC
5 files updated, 0 files merged, 0 files removed, 0 files unresolved
needs merge with patchB1
needs update of diff base to tip of patchB1
use 'hg pmerge'
$ hg pmerge
updating to patchB1
1 files updated, 0 files merged, 4 files removed, 0 files unresolved
patchC: merging from patchB1
marked working directory as branch patchC
(branches are permanent and global, did you want a bookmark?)
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).