Some time ago, I got excited about a new VCS called Pijul whose
distinctive feature is that it's based on a theoretical foundation around
patches (paper).
I tried to read the paper at the time and failed miserably. I have extremely
limited knowledge of category theory.
The fascinating property of Pijul in my opinion is that merge conflicts have an
actual representation in the way it represents files with contents in disks. If
you think of a file in terms of lines with a partial ordering, then for regular
files the ordering is also total: any two lines have an order in the file. Files
with merge conflicts however do _not_ have a total ordering of their lines, as
there are at least two lines (from different changes) where the VCS cannot
immediately infer which should come before the other.
This is something that's been worked on extensively in Darcs.
Here I want to retrace an existing demonstration of the "bad merge" and perform
the equivalent steps with Pijul side-by-side and look at the end result.
## Git
Let's recreate a simple [bad merge][badmerge-simple] with Git:
$ git --version
git version 2.35.1
$ git init merge-git
$ cd merge-git
# Create and record our initial change (a)
$ python -c "print('\n'.join(c for c in 'ABCDE'))" > file.txt
$ git add file.txt
$ git commit -m 'a'
# Creates changes b1 and b2
$ git checkout -b b
# Change b1
$ python -c "print('\n'.join(c for c in 'GGGABCDE'))" > file.txt
$ git commit -a -m 'b1'
# Change b1
$ python -c "print('\n'.join(c for c in 'ABCDEGGGABCDE'))" > file.txt
$ git commit -a -m 'b2'
# Create change c1
$ git checkout -b c master
$ sed -i -e 's/C/X/' file.txt
$ git commit -a -m 'c1'
# Now we merge c1 into b2
$ git checkout -b merge b
$ git merge c
Auto-merging file.txt
Merge made by the 'ort' strategy.
file.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ cat file.txt
A
B
X
D
E
G
G
G
A
B
C
D
E
## Pijul
Let's try the same sequence with Pijul.
$ pijul --version
pijul 1.0.0-beta.2
$ pijul init merge-pijul
$ cd merge-pijul
$ python -c "print('\n'.join(c for c in 'ABCDE'))" > file.txt
$ pijul add file.txt
$ pijul record -m 'a'
$ pijul fork b
$ pijul channel switch b
$ python -c "print('\n'.join(c for c in 'GGGABCDE'))" > file.txt
$ pijul record -m b1
$ python -c "print('\n'.join(c for c in 'ABCDEGGGABCDE'))" > file.txt
$ pijul record -m b2
$ pijul fork c --channel main
$ pijul channel switch c
$ sed -i -e 's/C/X/' file.txt
$ pijul record -m 'c1'
$ pijul fork merge --channel b
$ pijul channel switch merge
$ pijul apply "$(pijul log --channel c |head -1 | cut -d' ' -f2)"
$ cat file.txt
A
B
C
D
E
G
G
G
A
B
X
D
E
See the X towards the bottom of the file? Here's the diff:
$ diff -Naur merge-git/file.txt merge-pijul/file.txt
--- merge-git/file.txt 2022-08-04 12:09:49.054364728 +0200
+++ merge-pijul/file.txt 2022-08-04 11:56:16.251548521 +0200
@@ -1,6 +1,6 @@
A
B
-X
+C
D
E
G
@@ -8,6 +8,6 @@
G
A
B
-C
+X
D
E
With Git, the X ended up towards the top of the file, but for Pijul it was able
to track that the modification with X applied to the lines at the bottom. This
outcome is not affected by the merge direction, so merging `c` into `b` is not
different from merging `b` into `c` (although the output from "git merge" will
look different).
If you, like me, is a day-to-day Git user, I've written up a rough command
translation table:
pijul log --channel bar (to find hashes to apply)
pijul apply hash
Pijul doesn't operate with _commits_, _branches_, or _merges_, instead
the terminology is _changes_ (or _patches_), _channels_ and _apply_. The
implies in particular that you will never see something like a "merge
conflict"—if the application of one change introduces a conflict, Pijul will
warn about it just move on. The conflict is tracked internally, but it doesn't
prevent you from applying or recording other unrelated changes.
## References
1. [badmerge -- abstract version][badmerge-simple]
2. [badmerge -- concrete version with bad semantics](https://tahoe-lafs.org/~zooko/badmerge/concrete-bad-semantics.html)
3. [Merging and patches][jneem-merging] on "A new version" blog
4. [Merging, patches, and Pijul][jneem-pijul] on "A new version" blog
[Pijul]: https://pijul.org/
[catpatches]: https://arxiv.org/abs/1311.3903
[jneem-merging]: https://jneem.github.io/merging/
[jneem-pijul]: https://jneem.github.io/pijul/
[badmerge-simple]: https://tahoe-lafs.org/~zooko/badmerge/simple.html