Pijul

How to cherry pick?

This started as a blog post that became a twitter conversation (https://twitter.com/bdmurdock/status/1189017881075302400?s=20) and then I realized there was probably an “official” place to discuss pijul. Sorry about not realizing that.

I found a discussion about the first question I had here: Equivalent of checking out an old commit? Any more discussion about that ought to continue there, I’d guess.

The second question I have is how to cherry pick. If you read the blog post and the twitter conversation, the real question I have is, how do I cherry pick just one patch out of the middle of a string of patches that depend on each other. Somehow git lets you do it. It generally creates a merge conflict, but using kdiff3, for example, it’s not generally a hard one to resolve. Because of that, it seems like pijul should be able to do it too. Any thoughts or techniques?

A response I got on twitter:

Sure, weird merges sometimes work with Git. In Pijul we couldn’t possibly “figure it out”, since that kind of merge is not even defined. But “normal” merges always work (unlike in Git, where they sometimes fail).

In fact, I can’t quite reproduce the git cherry-pick behavior that I saw before this. Now it tells me there’s a conflict but when I run git mergetool in order to resolve the conflict it tells me no files need merging. Go figure :roll_eyes: . It has, however, edited my file the way I would have liked the cherry-pick to edit it. I guess if I want this behavior I need to be able to explain it somehow. Hrm.

Have you got Git’s rerere feature turned on? Maybe what you are seeing is it is presenting the same conflict as before, but it is reusing the conflict resolution you previously committed.

https://git-scm.com/docs/git-rerere

1 Like

That’s it! Thanks, I had forgotten about rerere.

OK, here’s a quick example (without rerere enabled):

mkdir a.git
cd a.git
git init
echo "line 1" >> file
git add file
git commit -m "first line"
echo "line 2" >> file
git commit -am "second line"
echo "line 3" >> file
git commit -am "third line"
git checkout master~~ # go back to first commit
git checkout -b foo
git cherry-pick master

Here’s what git says when you do the cherry pick:

error: could not apply 6aaa959... third line
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

Here’s what file now looks like:

line 1
<<<<<<< HEAD
=======
line 2
line 3
>>>>>>> 6aaa959... third line

6aaa959 is the commit we cherry-picked (we just called in master). Now, running git mergetool, we see:

Merging:
file

Normal merge conflict for 'file':
  {local}: modified file
  {remote}: modified file
file seems unchanged.
Was the merge successful [y/n]? y

And file is now:

line 1
line 3

Here’s the diff representation of the commit that was created from the cherry-pick:

diff --git a/file b/file
index 89b24ec..884971c 100644
--- a/file
+++ b/file
@@ -1 +1,2 @@
line 1
+line 3

So, is git doing a 3-way merge behind the scenes when you do a cherry-pick? Is it just using the line numbers in the diff to make a good guess? Could pijul do anything similar to that?

The spirit of this project is to be the first sound version control system, where “sound” means that a number of important axioms are satisfied on the patches, in all cases. The axioms we have at the moment are:

  • associativity: applying patch C on top of patches A and B does the same as applying B and C on top of A. This sounds trivial, but Git doesn’t even have that.
  • commutativity: two patches that could be produced independently always commute (even if they conflict).
  • reversibility: each patch A has an anti-patch A^-1, such that applying A^-1 on top of A yields a contents equivalent to applying none of them (the repository after AA^-1 is different from the repository before AA^-1, but the two repositories are semantically equivalent).

A sound theory has lots of benefits, the most important of them being to be predictable and intuitive. “Sound” often means “unexciting”, as opposed to things that try to be clever.

So, it might be the case that this particular crazy thing Git tries to do can fit in a sound theory, but I’d say it’s quite unlikely: if it’s not immediately intuitive/boring to someone who’s been using version control for a while, then probably not.

But even if we can’t do that, I still think our merge algorithm is far superior to Git’s, since you almost never need to do what you’re doing in your example (which amounts to testing rerere), but the merges you actually need in real life will be 100% consistent and unsurprising.

I agree that my little toy example might not be very realistic or helpful. I really hope that the soundness of this system will actually make it more intuitive and more powerful than git (which would not be boring).

I’m a little worried from my (admittedly small amount of) testing that in practice you won’t have a lot of patches that don’t depend on other patches. Right now at least, those dependencies seem to make the promise of associativity and commutativity a little less exciting in practice.

Distributed revision control has opened up new ways of using revision control that I don’t even the initial creators of the tools envisioned. Being able to commit things locally, willy nilly, and then having the ability to edit the commits, reorder them, fix up commit messages, combine them, split them apart, etc. before sharing them with the public (heck, even after sharing them with the public) is distributed version control’s killer feature. Associativity, commutativity, and reversibility should all make that editing of commits/patches work even more smoothly. Right?

I’m not too sure about that: when two patches touch different files, or different parts of the same file, they are independent. In short, every time you would branch in Git, you get independent patches.

For example, those very large monorepos won’t need things like LFS in Pijul (with the caveat that the current Pijul can’t handle super large repositories, that’s going to change soon), since you can clone only patches that touch one part of the repository.

Well, that’s the hope at least! We’ll see how it works in practice. So far the experience on Pijul itself has been relatively smooth, except for some really hard bugs, which needed to be fixed in order to make or apply the patches that fix them.

Cool. I will definitely have to try it out more on some real code to see how it plays out.