Collaboration

Sequential Changes

Setup Base Repo

This is the simple case: if people make sequential changes, we don’t ever get merge conflicts. Let’s first set up a repo with two patches:

$ hg init mine
$ cd mine
$ echo 1 >one
$ echo 1 >conflict # need this later
$ hg ci -Am one1
adding conflict
adding one

Patch p1:

$ echo 2 >>one
$ hg pnew p1
marked working directory as branch p1
(branches are permanent and global, did you want a bookmark?)

Patch p2:

$ echo 3 >>one
$ hg pnew p2
marked working directory as branch p2
(branches are permanent and global, did you want a bookmark?)

Yielding:

$ hg pgraph
@  p2
|
o  p1
|
o  default

Initial Clone

Let’s now see what happens if someone clones from us:

$ cd ..
$ hg clone mine collab
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd collab
$ cat >>.hg/hgrc <<-eof
    [ui]
    username = collab
eof

He sees a bunch of branches:

$ hg branches
p2                             2:d0e98bfc8352
p1                             1:297e6182bd2b (inactive)
default                        0:3d6f92b7a7da (inactive)

But he does not have a patch graph file (because we don’t commit this file):

$ ls .hg/pgraph
ls: cannot access .hg/pgraph: No such file or directory

But he can see the patch graph, as inferred from the recorded dependencies:

$ hg pgraph
created graph description from current tips
o  p2
|
o  p1
|
@  default

and pbranch has automatically created the patch graph file from the inferred graph now:

$ cat .hg/pgraph
p2: p1
p1: default

Updates

He can now update the patches. Here, he updates p1:

$ hg up p1
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ echo 1 >>two
$ hg ci -Am two1
adding two

Then pmerges the change forward:

$ hg up p2
2 files updated, 0 files merged, 1 files removed, 0 files unresolved
needs merge with p1
needs update of diff base to tip of p1
use 'hg pmerge'
$ hg pmerge
updating to p1
2 files updated, 0 files merged, 1 files removed, 0 files unresolved
p2: merging from p1
marked working directory as branch p2
(branches are permanent and global, did you want a bookmark?)
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

So p1 now contains an additional file two:

$ hg pdiff p1 | grep -F two
diff --git a/two b/two
+++ b/two

And p2 does not (meaning it has the merge):

$ hg pdiff p2 | grep -F two

Now we pull his updates:

$ cd ../mine
$ hg pull ../collab
pulling from ../collab
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files
(run 'hg update' to get a working copy)
$ hg update tip
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

our patch status is clean:

$ hg pgraph --status
@  p2
|
o  p1
|
o  default

and the diffs look OK:

$ hg pdiff p1 | grep -F two
diff --git a/two b/two
+++ b/two
$ hg pdiff p2 | grep -F two

New Patches

What if he adds a patch? Let’s see:

$ cd ../collab

He introduces a new branch p3 based on p1:

$ hg up p1
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ echo 4 >>one
$ hg pnew p3
marked working directory as branch p3
(branches are permanent and global, did you want a bookmark?)
created new head

yielding:

$ hg pgraph
o  p2
|
| @  p3
|/
o  p1
|
o  default
$ hg pdiff p3
# HG changeset patch
# User collab
# Date 0 0
_
diff --git a/one b/one
--- a/one
+++ b/one
@@ -2,0 +3,1 @@
+4

When we pull this:

$ cd ../mine
$ hg pull ../collab
pulling from ../collab
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 2 changes to 2 files (+1 heads)
(run 'hg heads' to see heads)
inferred graph has changed; use 'hg pgraph --tips' to view

we get a hint that the inferred patch graph changed. As we can verify:

hg pgraph—tips
$ hg pgraph --tips
@  p2
|
| o  p3
|/
o  p1
|
o  default

But we don’t have p3 in our .hg/pgraph yet:

$ cat .hg/pgraph
p1: default
p2: p1

which means our default graph does not list p3:

$ hg pgraph
@  p2
|
o  p1
|
o  default

The reason is .hg/pgraph is a file designed for editing by hand, comments and all. So pbranch never updates it automatically.

We could now add p3 to .hg/pgraph manually, or just start over from the inferred graph:

hg pgraph—as-text
$ hg pgraph --tips --as-text >.hg/pgraph
$ cat .hg/pgraph
p2: p1
p3: p1
p1: default

to get the desired outcome:

$ hg pgraph --status
@  p2
|
| o  p3
|/
o  p1
|
o  default

and:

$ hg pdiff p3
# HG changeset patch
# User collab
# Date 0 0
_
diff --git a/one b/one
--- a/one
+++ b/one
@@ -2,0 +3,1 @@
+4

Parallel Changes

Let’s see what happens when two people update the same base patch in parallel. This raises issues beyond plain merge conflicts in that patches based on the modified patch will have merge conflicts in their .hgpatchinfo/*.dep files. So let’s see how pbranch handles this.

Make Conflicting Changes To p1

First, we modify p1:

$ cd ../mine
$ hg up p1
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ echo mine >>conflict
$ hg ci -m one-mine

and merge this into the other patches:

$ hg pmerge --all
p3: merging from p1
marked working directory as branch p3
(branches are permanent and global, did you want a bookmark?)
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
updating to p1
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
p2: merging from p1
marked working directory as branch p2
(branches are permanent and global, did you want a bookmark?)
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Now, our collaborator does something similar:

$ cd ../collab
$ hg up p1
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ echo collab >>conflict
$ hg ci -m one-collab
$ hg pmerge --all
p3: merging from p1
marked working directory as branch p3
(branches are permanent and global, did you want a bookmark?)
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
updating to p1
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
p2: merging from p1
marked working directory as branch p2
(branches are permanent and global, did you want a bookmark?)
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Pull Conflicting Change

We pull this:

$ cd ../mine
$ hg pull ../collab
pulling from ../collab
searching for changes
adding changesets
adding manifests
adding file changes
added 3 changesets with 3 changes to 3 files (+2 heads)
(run 'hg heads .' to see heads, 'hg merge' to merge)

and look at the patch status:

$ hg pgraph --status
@  p2
|   * needs merge of 2 heads
|   * needs merge with p1
|   * needs update of diff base to tip of p1
| o  p3
|/    * needs merge of 2 heads
|     * needs merge with p1
|     * needs update of diff base to tip of p1
o  p1
|   * needs merge of 2 heads
o  default

p1 now has two heads. This is expected as we did parallel changes in p1. But p2 and p3 each have two heads as well. These latter are caused by the forward merges of the conflicting change in both repos by hg pmerge.

Merge Resolving Conflict

The heads in p1 we therefore resolve normally:

$ hg up p1
2 files updated, 0 files merged, 1 files removed, 0 files unresolved
needs merge of 2 heads
use 'hg pmerge'
$ hg pmerge
p1: merging from alternate head ea2bb5cf4441
merging conflict
warning: conflicts during merge.
merging conflict incomplete! (edit conflicts, then use 'hg resolve --mark')
0 files updated, 0 files merged, 0 files removed, 1 files unresolved
abort: use 'hg resolve' to retry unresolved file merges, then do 'hg pmerge' again
$ hg resolve --list
U conflict
$ echo "resolved" >conflict
$ hg resolve     --mark conflict
$ hg ci -m resolved

yielding this status:

$ hg pgraph --status
o  p2
|   * needs merge of 2 heads
|   * needs merge with p1
|   * needs update of diff base to tip of p1
| o  p3
|/    * needs merge of 2 heads
|     * needs merge with p1
|     * needs update of diff base to tip of p1
@  p1
|
o  default

Carry The Merge Forward

If we run pmerge now, the following happens:

for p in [p2, p3]:
    merge p1 into tip of p # so we get the merge of "conflict" we already did
    merge heads
    resolve the conflict in ".hgpatchinfo/%s.dep" % p by overwriting the .dep

Here it is:

$ hg pmerge --all
p3: merging from p1
marked working directory as branch p3
(branches are permanent and global, did you want a bookmark?)
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
p3: merging from alternate head 5482c93964fa
merging .hgpatchinfo/p3.dep
warning: conflicts during merge.
merging .hgpatchinfo/p3.dep incomplete! (edit conflicts, then use 'hg resolve --mark')
merging conflict
warning: conflicts during merge.
merging conflict incomplete! (edit conflicts, then use 'hg resolve --mark')
0 files updated, 0 files merged, 0 files removed, 2 files unresolved
resolving conflict; already merged in rev 13
p3: resolving .hgpatchinfo/p3.dep
updating to p1
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
p2: merging from p1
marked working directory as branch p2
(branches are permanent and global, did you want a bookmark?)
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
p2: merging from alternate head 7d89066a5a9c
merging .hgpatchinfo/p2.dep
warning: conflicts during merge.
merging .hgpatchinfo/p2.dep incomplete! (edit conflicts, then use 'hg resolve --mark')
merging conflict
warning: conflicts during merge.
merging conflict incomplete! (edit conflicts, then use 'hg resolve --mark')
0 files updated, 0 files merged, 0 files removed, 2 files unresolved
resolving conflict; already merged in rev 15
p2: resolving .hgpatchinfo/p2.dep

And we get the desired state:

$ hg pgraph --status
@  p2
|
| o  p3
|/
o  p1
|
o  default

And diffs:

$ hg pdiff --tips p1
# HG changeset patch
# User john
# Date 0 0
_
diff --git a/conflict b/conflict
--- a/conflict
+++ b/conflict
@@ -1,1 +1,1 @@
-1
+resolved
diff --git a/one b/one
--- a/one
+++ b/one
@@ -1,0 +2,1 @@
+2
diff --git a/two b/two
new file mode 100644
--- /dev/null
+++ b/two
@@ -0,0 +1,1 @@
+1
$ hg pdiff --tips p2
# HG changeset patch
# User john
# Date 0 0
_
diff --git a/one b/one
--- a/one
+++ b/one
@@ -2,0 +3,1 @@
+3
$ hg pdiff --tips p3
# HG changeset patch
# User john
# Date 0 0
_
diff --git a/one b/one
--- a/one
+++ b/one
@@ -2,0 +3,1 @@
+4