I have the same question every time Pijul comes up on HN, and I have yet to get an answer:
Can someone give me a real world example (person a makes X change, person b makes y change etc. etc.) that would work better in Pijul than Git?
I am a complete believer on a sound underlying model producing better results for users at a high level, but I'm not clear on how it maps through for Pijul. I think that's what they're missing in the sell - the ability to explain to devs "it will make your life easier in the following specific ways".
For example when getting my employer moved from SVN to Git I could talk to people about how much easier it was to create and then merge a temporary branch for a feature in Git. Git understand the topology of the history, knew the merge base and had better merge algos so the only pain you had was when there was an actual conflict - two people editing the same file location. It also could track renames, which were incredibly painful to merge in SVN.
> Can someone give me a real world example (person a makes X change, person b makes y change etc. etc.) that would work better in Pijul than Git?
Simplified example:
Persons A and B check out master branch.
Person A adds a.txt, commits and pushes.
Person B adds b.txt, commits and tries to push and...
1) git will not accept the push because it's not on top of current master branch, person B needs to fetch and merge/rebase before pushing again.
2) pijul will accept the change (after a pull, but no rebase) because patches A and B are independent of each other and does not matter which order they are in the history (keyword: commutation).
The value of Pijul will only start to show when you get into big three way merge scenarios. Which git users avoid like the plague because they are so nasty to deal with. Demonstrating this would need a much larger example.
edit: clarification a pull is still needed in case 2, but no rebase or merge because there isn't one for commutative patches
> 1) git will not accept the push because it's not on top of current master branch, person B needs to fetch and merge/rebase before pushing again.
But is this not the right thing to do? A kernel is a complex piece of software. Changes in one place can have very non-obvious consequences in other places (think of changes that cause deadlock because locks are applied in the wrong order). Of course, it is theoretically nice if I know that a change to e.g. documentation or fixing a typo in a comment is not affecting the Ethernet driver or the virtual file system layer, but this is down to the architecture of the project - this is not something that a version control system can prove.
Given that, it seems desirable to me that the source tree has as few different variations, permutations how to get there, and so on, as possible, since this makes testing and things like bisecting for something like a broken lock or another invariant much easier.
You've never maintained a long-lived git feature branch, it seems :)
I maintain a project that is a slight modification of a very active upstream repo. The changes I maintain are rather invasive. Almost every upstream commit introduces a merge conflict with my changes.
To keep myself sane I only merge/rebase when upstream releases a new version, but it still ends up sucking up a few weeks of my time every year. During those weeks, I look enviously at pijul where the conflicts would resolve down to a handful of corrections in context at the point of divergence, instead of gigantic merge conflicts obscured by thousands of piled on patches.
You say that, but this applies to all cherry picking you can do between branches as well. As long as they don't conflict you're golden, and if there's a conflict you can commit a resolution that'll commute with your branch until you merge back into main.
It would enable so many nice and less strict workflows to actually work if it ever got momentum, I've still got hope.
The git behavior seems greatly preferable here. As mentioned in other threads, the notion of commutativity here is very weak and counterintuitive; it only seems to cover the applicability of an auto-merge heuristic, not any actual notion of correctness or semantics, so a human is needed to review the merge and re-test before anything can be known safe for pushing upstream. If anything, git is too lenient in allowing auto-merges to take place that could in principle change semantics, and it ought to enforce a manual review stage for any merge, regardless of whether the auto-merge heuristic succeeded or not.
> When the contents has a conflict, git and pijul behave similarly.
Not really: Pijul can record a conflict resolution as a patch, and apply it in a different context. Also, the conflict doesn't "come back", so you don't need extra hacks like rerere/jujutsu.
> Pijul just removes the manual work when there is no conflict in the contents but the history is different.
This is true, but could be confusing as our definition of conflicts isn't based on contents, but on operations, which is very different from Git (Git doesn't detect all conflicts).
> the notion of commutativity here is very weak and counterintuitive; it only seems to cover the applicability of an auto-merge heuristic
This is completely false: in Pijul, any patches that could have been produced independently can be applied in any order without changing the result. There are 0 heuristics in Pijul, unlike in Git where even random line reshuffling can happen (there are examples in the "Why Pijul" section of the Pijul manual).
Obviously, deciding whether a merge has the correct semantic is Turing-complete, and Pijul doesn't try to do any of that.
Merges are bad only when all parties involved edited the same code. There's no programmatic way to solve this problem. It's an administrative problem: someone has to decide whose code is the right one to use.
If changes coming from both sources are independent, then rebase in Git is trivial as well, and there's nothing to be afraid of.
Does that only apply to adding new files? The changes in commit A could affect the behavior of the changes in commit B even if they are different files.
No, it applies to everything. If adding patches A and B (same or different files) will lead to same result regardless of which order they are applied, they are called "commutative" and pijul won't care which order they are in your history.
It only tracks content of files, not semantic or behavior changes.
git can already do this as long as there isn't a conflict. Maybe pijul has better three way conflict resolution, but those can be risky, and avoiding those rare situations wouldn't offset the vast amount of tooling that got has.
Git would make you merge or rebase, but yes there wouldn't be a conflict. They're saying Pijul would let you directly push without having to deal with the diverging histories.
Which tbh is a bad thing. Just because change a doesn't textually touch change b doesn't mean they don't interact.
Unless your VCS is handling CI for integrating changes on push, you really need to pull down the upstream changes first and test them combined with your code before blindly pushing.
> Which tbh is a bad thing. Just because change a doesn't textually touch change b doesn't mean they don't interact.
A good example for this is code which grabs several locks and different functions have to do that in the same order, or a deadlock will result. A lot of interaction, even if changes might happen in completely different lines.
And I think that's generally true for complex software. Of course it is great if the compiler can prove that there are no data race conditions, but there will always be abstract invariants which have to be met by the changed code. In very complex code, it is essential to be able to do bisecting, and I think that works only if you have a defined linear order of changes in your artefact. Looking at the graphs of changes can only help to understand why some breakage happened, it cannot prevent it.
I have clarified the comment above. A pull is needed before pushing. That pull does not need a merge or a rebase like git does because the order of commits A nd B does not matter (iff they are commutative). This gets a lot more useful when there are more than two patches to consider.
pijul has a lot of theory and engineering to figure them out, dealing with them better than git is one of the major reasons it exists at all, and darcs before it.
What benefit is there to the VCS knowing that these histories are equal? That's valuable in verification or efficient binary patching, but I don't see how it matters in version control. When would I want to compare two repositories that were patched in different orders?
I'm guessing that it makes it easier to pick and choose between a bunch of patches. This is something I sometimes want to do in Git, but doing so requires a bit of planning. Have all the independent features branch from the same point, then, to 'assemble' them, do an octopus merge.
If the VCS knows about dependencies between patches intuitively, it could free me from having to explain it, which in the case of Git, requires following procedures that I'm unlikely to convince any of my coworkers to follow ("ohay, first, decide on the earliest point in the history from which this patch could make sense, rebase onto that....")
Imagine you are working on a patch heavy project. Like the Linux kernel. Where there are a lot of patchsets going around that are not in the mainline.
You and I, who are both working off of main, and who have both separate merged in a few patchsets that are relevant to our shared module of interest. We can merge and compare our branches, and the differences in terms of nursing patches without having to be rigorous about reconciling or histories.
That may be standard in some shops, but definitely not all.
I view anything that interferes with a push as a threat to the VCS, since it encourages developers to keep changes local and unavailable to their teammates. The only exception would be direct pushes to main.
> To me nothing of substance should happen in the repository,
The idea is that with pijul nothing of substance would happen on the server in this example, it is the same process that would happen if you were doing it all locally.
Hmm...so if something did need to happen, pijul would also reject and I would have to pull, do the local edits until everything is consistent and then push, just like git.
So it's a low-impact optimization of the fast path?
But actually, how does pijul know there are no conflicts? Textually?
Hmmm...yeah looks like it's purely textual. Er, no. There can be semantic conflicts that I need to resolve that do not conflict textually. The test suite needs to be green locally on my machine, and then we replace the Top of Tree wholesale with the code that passed the tests locally on my machine.
So to me this feature of pijul is clearly an anti-feature, a bug, and the git behavior is correct.
> Hmm...so if something did need to happen, pijul would also reject and I would have to pull, do the local edits until everything is consistent and then push, just like git.
Unlike git, pijul has first class conflicts, pijul unlike git does not reject in this example.
My knowledge is limited, but from my testing that means the conflict exists in the history, at least if your merge style allows for that(similar to how in git you can choose to always rebase or use merge commits).
The conflict is resolved with a new patch.
I did not spot much in the documentation with a quick search but on the man page there is a small blurb on first class conflicts
> First-class conflicts
In Pijul, conflicts are not modelled as a "failure to merge", but rather as the standard case. Specifically, conflicts happen between two changes, and are solved by one change. The resolution change solves the conflict between the same two changes, no matter if other changes have been made concurrently. Once solved, conflicts never come back.
I believe that even git would work in this scenario because a.txt and b.txt are separate and independent thus a rebase in git is not required.
The point at which this becomes an issue is when both person A and B try to make changes to the same file and specifically the same blob of text within that file.
I could be wrong but this should be simple enough to prove out because I've run into this situation before where I forgot to rebase before pushing my changes but git still accepted the changes as they were independent of the changes that person B made.
git wouldn't accept a push with conflicting changes on the remote but that's just because pushes are dumb (not bad, they just don't do anything fancy).
The solution would be to pull upstream changes (so you know what you are potentially pushing your changes into) and then push.
A rebase is never required in Git. (people/maintainers may disagre) but a merge will always do.
Having said that. In your scenario provided, a pull + merge/rebase will be required. It will then resolve automatically, and without conflicts. But a human has to be involved to provide a strict sequence/dag of the those commits.
The thing I like most is that cherry-picks are real, not simulated.
In git, a cherry-pick pulls the change you're interested in off a branch, making a copy in the process. If you later merge or rebase that branch, there are two versions of it in the history, this can have practical consequences, it's not uncommon for this to generate conflicts which wouldn't be there without the cherry-pick.
In pijul, a cherry-pick is just one way to apply a patch. It's the same patch in both branches, so the history doesn't contain two versions of the change, only one. So there is no difference in the result between 'branch, cherry-pick, merge' and 'branch, merge'. Ever.
> It also could track renames, which were incredibly painful to merge in SVN.
They are still a pain in Git as well. Rename a class and its usages in C# project, for example, and it randomly breaks based on some arcane heuristics.
As for actual question you posed - partial checkouts. Your artist don't need to checkout code, but can work on it art folder.
That is a distinction that only exists because a Git repo is an ordered sequence of snapshots of your code that get translated to and from patches for human consumption. As far as I can tell, a Pijul repo is more of a giant dependency graph of patches that when merged together form a consistent snapshot of your code.
sounds like rebase with force push remote. (well, every push is a force push in git. so just rebase+push. ...except on github if you have "protected branches")
any time you rebase the same feature branch onto current main and solve the same conflicts pijul could probably do much better without hacks like rerere (which most people also do not use, so...)
- There's an example in the "why Pijul" page of our manual where Git completely reshuffles your lines, and no "custom merge algorithm" could possibly solve it. I would be terrified if I were working on crypto/security code and I knew my VCS was doing that: https://pijul.org/manual/why_pijul.html
- Patch commutation makes all big instances small: no need for submodules, partial/shallow clones, etc. Patch commutation lets you work on a small part of the repo by cloning only the patches you're interested in, and submit patches that mechanically commute with all patches on other parts of the repo.
- Free cherry-picking: no need for strict disciplines, you can just introduce a quick fix on your local work branch, and push just that to production. When you're ready merging the rest, you won't have to solve conflicts again (no need for Git rerere/Jujutsu/…)
- Many uses of branches reduced to "just use patches": many people, especially on fast-moving projects and "early days", don't really know what they're working on, and are dragged onto solving problems they didn't plan initially. Well, Pijul lets you focus on your work, then make patches, and then separate them into branches, thanks to commutativity.
- Separation of contents and operations: this really feels like the CSS3/HTML5 of version control. In Pijul, patches have two "detachable" parts, a part describing what the patch does (as concise as "I introduced 1Tb of data", i.e. just a few bytes), and the contents (not concise: the 1Tb themselves). You don't need the data to apply a patch, so when working on large files, you can record 10 different versions, and your co-workers will only download the parts that are still alive after that. No LFS required!
- Precise modeling of conflicts: conflicts are stored in our model, not "recorded" or "artificially first class". They're literally the core of our model, the initial theoretical motivation. Patches are where you need a good tool the most, and we record them and store your precious resolutions as actual patches, so they don't come back (no "git rerere" needed, and conflict resolutions can be cherry-picked).
Now, we also have less game-changing things like:
- Accurate and super fast "blame" (which we call "credit", and which doesn't require Pijul to look at the entire history like Git does).
- Generic diffs, and therefore merge, i.e. not necessarily line-based. We haven't implemented them, but you could in theory implement AST-based diffs on top of Pijul.
Can someone give me a real world example (person a makes X change, person b makes y change etc. etc.) that would work better in Pijul than Git?
I am a complete believer on a sound underlying model producing better results for users at a high level, but I'm not clear on how it maps through for Pijul. I think that's what they're missing in the sell - the ability to explain to devs "it will make your life easier in the following specific ways".
For example when getting my employer moved from SVN to Git I could talk to people about how much easier it was to create and then merge a temporary branch for a feature in Git. Git understand the topology of the history, knew the merge base and had better merge algos so the only pain you had was when there was an actual conflict - two people editing the same file location. It also could track renames, which were incredibly painful to merge in SVN.