Git is pretty nice, but I'm sure there is something much better waiting to he invented. The CLI in particular could use a ton of improvements.
And I feel it in my bones that there is a revolutionary GUI waiting to be invented. Why can't I drag a commit or set of commits from one branch to another? With safe, easy undo (reflog doesn't count) and super smooth conflict resolution? Etc etc.
And of course there is the interesting rabbit hole of semanitc / language aware diff. Line diffs suck in many ways.
It's one of the hundreds of of problems that I'd love to work on one day, but probably won't get a chance to. Sigh... :)
This tool is based on strong mathematical theory of patches, instead of snapshot/commit-based. It seems simpler to reason with, but we’d have to unlearn a lot from Git.
It’s not suitable for big projects yet, but it’s already used by Pijul itself and other Rust components. And it already have its own „Github” called the Nest (because pijul is a bird). Pretty promising imho.
> This tool is based on strong mathematical theory of patches
Is this a good thing? What practical problems does a strong mathematical theory of patches solve that git doesn’t? And what’s the difference between a commit and a patch? Aren’t git commits stored as patches?
I’m a math lover, but my gut reaction to that idea is that it sounds off-putting. I don’t mean that as a judgement or insult; I’m admitting my own assumption and bias here, jumping to unwarranted conclusion, not saying anything is wrong with pijul. But when the elevator sales pitch is “strong math”, it immediately makes me assume it’s too technical for a normal programmer and focused on academic ideals rather than getting practical work done as easily as possible.
The FAQ even says, “Pijul is trivial for whoever knows category theory.” Is that question really asked frequently? Words like that might convince me to never try it. ;)
No, they aren't, git commits are stored as snapshots. Each git commit has one tree, which is a snapshot of the state after the commit; zero or more parent commits; a pair of authors with corresponding timestamps; a commit message; and nothing more. Any patch you see in git is an illusion, made by comparing the commit's tree with the parent commit's tree.
Thank you. Sheesh I feel like I should have known this about git. That would make diffing very far apart changes super fast.
So what's the advantage to explicit storage in patches over snapshots? Are diffs between snapshots not able to capture the same information that explicit patches have?
The trouble with patches is that you can't get engineers to reliably declare whenever they copy or move code and where it came from. This is part of why svn merging was such a trainwreck, undeclared copies and moves (which look like delete+insert) almost always conflicted.
I'm one of the authors. Our goal is to make something much much easier to use than Git, and we're already succeeding in doing that.
The math can be completely ignored by the user, it is just a way to guarantee that the tool will always match the basic intuition of version control. For instance:
- "Associativity" is a concept of algebra, but when applied to version control, it just means the following: let's Alice makes a commit A, and Bob makes two commits B and C. If Alice pulls B, and then pulls C, associativity means that she'll get the same as pulling B and C together. Pijul is associative, but Git is not!
- "Commutativity" is also a concept of algebra, but in practice in version control, it means that you don't have to think about feature branches. In Pijul, two patches that you could produce on different branches are "independent" anyway. Applying them in any order will always yield the same result, so you can push any of them, no matter the order in which you made them. This property means that "rebasing" becomes much simpler: it's just the action of applying and unapplying patches. Also, cherry-picking becomes the default, you don't even have to think about it.
- "Inverses": in Pijul, all patches have inverses. Sure, it sounds like `git revert`, except that `git revert` can sometimes screw things up very badly (if you `git revert` a merge commit, for instance).
Also, in Pijul, conflicts are the normal state, so you don't need anything like `git rerere`, and two users with the same patches (even in different orders) will always see the same conflicts. If you have a patch solving a conflict between two other patches, then the conflict is solved forever, and doesn't "come back".
As a conclusion, I'd say Git might be too technical for a normal programmer (which is why we have giant threads like this one on HN), but Pijul is the exact opposite.
It is as powerful as Git, but beginner-friendly, infinitely faster to learn, and more flexible. Since there can be no "bad merge", you don't have to think about your version control system anymore, and can focus on your work.
That said, there are still a few problems:
- it's not yet as efficient as Git for storage, but we're working on it. We just released the first full implementation a few weeks ago.
- it will never be as good as Git for detecting file moves, because Git is happy with a soup of blobs, whereas Pijul needs more structure (but OTOH that extra structure makes Pijul much better at "blame").
Thanks for the reply and explanation. You’re convincing me to at least try it.
I want to understand first-hand by using Pijul what you mean by not needing rerere and having no such thing as a bad merge. Do you mean it’s not possible to make merge mistakes, or just that the default choices made by the version control system are never wrong? The only times I’ve ever needed rerere are when I made mistakes rebasing or resolving merge conflict, and I had to roll back and do a bunch of them again. In that case, I chose to manually undo my merge, so rerere is just saving time from having to repeat all of my decisions when only a few were wrong.
I like the idea of Pijul being better at blame. Of course the main thing that needs to happen is tooling. Git’s blame has been fine but the UI for it stinks. This is maybe the feature I miss from Perforce the most; the blame UI in P4V is superlative compared to git.
It's indeed not possible to make merge mistakes in Pijul, in the sense that the order between lines is always preserved: if line A comes before B in one repository, A will be before B in all repositories that have these two lines.
Moreover, merge is associative, which is the intuitive idea of a "good merge", in the sense that merging your patches one by one is the same as merging them all at once (this is false in Git).
In Pijul you can't "make mistakes rebasing". A repository is a set of patches (set as in maths), you can add patches or remove patches from the set, and that's about it. Two repositories with the same set of patches (possibly applied in different orders) are totally equivalent (in particular they have the same file contents).
So, rebasing in this case would be just the operation of adding some patches and removing others, and hence you can't make mistakes doing that, because there is no manual merge operation needed (of course you still need to solve your conflicts).
> The FAQ even says, “Pijul is trivial for whoever knows category theory.” Is that question really asked frequently? Words like that might convince me to never try it. ;)
There have been a few blog posts after we first announced it saying "Oh, it's just category theory, so I could rediscover it independently in Haskell in just a few hours".
I really like the promise of pijul, but it is early to say if it is better. Conventions and tooling will be a decisive factor in the ease of use. That said I do share your optimism.
CLI is fine until you acquire a habit of committing things chunk-wise and line-wise. E.g., I have these 20 files modified, but I'll quickly skim through and add/commit the lines/chunks that are done, and then continue working on the rest. CLI promotes a different kind of workflow, like "ok, I'm done, add/commit it all, and then move on".
What do you feel the UI does better than git add -p? The main concession I have is picking arbitrary files, but I feel like I've achieved 90%+ of what I want with '.cs' or 'Controller*' etc.
It's more streamlined, it's easier to scroll through staged and unstaged changes fast and flip lines/hunks/whatever between the two as you go, and you don't have to fire an external editor upon each hunk because you're already in one - emacs or vim (if using spacemacs).
(Also, whether it's good or not, rebasing in all of its forms becomes your second nature because it becomes so easy...)
If you're selecting by hunk, you don't need to fire up the editor. I also find the hunk selection in the terminal to be excellent. It's lightning fast and I can use it blind.
Also, firing up the editor for things like editing diffs when doing git add -p or when editing operations when doing git rebase -i, is not that big of a deal if you keep your editor light. Vim, for me, loads in an instant.
Agreed, selecting by hunk is ok in the terminal, I do it too sometimes when sshed someplace distant.
But you noted the two main differences yourself: (1) you don’t have to think in terms of git hunks if you don’t want to, lines or any blocks are as easy to deal with and (2) you don’t have to think about whether it would be quick or not to fire an editor because you don’t have to fire an editor.
This, plus everything’s lightning fast due to shortcuts. I use Magit in spacemacs and rebasing is very easy. I’ve done it so many hundreds of times that I can tell it from memory - e.g., fixup-merging the last two commits to the third one: “lljjriff,,” (maybe there’s a quicker way) - that’s less than typing git rebase -i.
Not trying to preach, just sharing why magit users are so happy about it.
Don't you have that backwards? With `git add -p`'s `e`(dit) option, I can (and occasionally do) add just some of the changes of a single line. I can freely edit the diff that dictates what goes in the index. With magit, I believe I'm limited to just selecting whole hunks or maybe whole lines.
It’s possible via ediff if you need it; magit devs didn’t want to implement it within magit itself IIUC because it’s fragile and changes in a temporary buffer may be lost, or something like that.
If you don’t like using ediff and really need to edit tons of hunks before staging, you could always commit/stash your dirty copy, revert back, edit files, check them in the way you want, possibly in multiple commits, then rebase your previously staged working copy on a new head and soft reset the head (it’s easier than it sounds) - which is the way I would do it in the terminal too if there was a lot to edit since it’s more fail-proof.
You're missing out on the lightning fast pull, interactive rebase, staging and commit capabilities of magit-status. The key bindings are intuitive and excellent.
They are intuitive and excellent, but I don't think they make things that much faster if at all. There's not much difference in speed between doing `C-x g s c -a c` and typing `git ci -a`.
I feel like Git's concepts got tied into a mess in its cli tools. Like the devs maybe tried to imagine what would be the proper interface for a person not deeply familiar with the innards, but failed, and e.g. `git-show` is the result.
So I get this vague feeling that someone could build a better human-oriented suite of CLI tools on top of Git's internals―with the internals better mapped to people's more casual understanding of the concepts and their aims.
Sure but I've never found a GUI or editor that lets me resolve conflicts in the way I want.
For example say I change one line of code in a big function. I rebase and that function has moved. Conflict!
I want a tool that says "here's what you changed, and here's the current state of the source". None of them do that though - they all just show the conflicts that git writes to disk - the code after you changed it, and the current code.
You basically get two copies of the function, one with your change and one without and you have to manually (visually) diff them to work out what you changed (or go back and look at your commit) and then reapply that change to the moved function.
It's really awkward and could definitely be better.
What you, and also I, want is a syntax-aware merge tool, i.e. one that can tell you the meaning of the change. If a function got moved to another place, or if 2 fields swapped places, I don't want it to have the same importance as na actual change in behavior. No importance, even.
Git's only sense of syntax is recognizing lines, and it's just not enough in too many cases.
Sadly in my (short) research everything I have found is either closed source or I can't afford to pay for it. It's even more saddening that git already has built-in bits for syntax related diffs.
> And I feel it in my bones that there is a revolutionary GUI waiting to be invented. Why can't I drag a commit or set of commits from one branch to another? With safe, easy undo (reflog doesn't count) and super smooth conflict resolution? Etc etc.
The model of git feels very nice and intuitive once you grasp the central architecture -- an immutable append-only content-addressed file system. The changes mentioned here in particular just don't really fit the model. You can certainly argue that we need a better model, but in order to move commits between branches in anything other than a mechanical manner, you would have to deal with the fact that often in source code repositories there are semantic connections between unrelated changes -- one file has a change to update its interface, the other file changes to use the old interface, and now you have something that no general-purpose version control software can reasonable reconcile or even identify as an issue.
Arguable there's a space for merging tools that build on the git model and contain language semantic information, so it can actually highlight conflicts that are deeper than patch-based, but until then I'd prefer that the version control system not try to be too smart and let me repair things after it does its mechanical actions.
> The CLI in particular ... Line diffs suck in many ways
These are changes that I think are in line with what I see in the future of git -- because git just contains a series of snapshots of a hierarchical tree, diffs are a secondary thing that an external tool can be brought to bear on very easily -- rather than trying to merge patches to create a diff, you actually have two fully realized files, together, potentially, with their common ancestor, which is all the information necessary to create a fully semantics-aware diff between files or trees.
I would almost want git to double-down on the grammar of tree changes; commands like "checkout" and "reset" are very basic manipulations with fairly clear idioms. But since those words are also English words that carry a whole bunch of connotations, it makes it confusing when you use "reset" to do something that does not involve returning something to its original state.
Having a "worse" command line interface, with some commands like "git make-my-working-tree-look-like-this-ref" and "git point-this-ref-to-this-other-ref" would break the connection with traditional version control systems while allowing us to use the full power of the git model.
I don't think you can drag commits because a commit itself is a snapshot that describes changes to a branch so it is the branch itself that can be merged, rebased, pushed, etc. by drag-and-drop.
The Git support in the Jetbrains IDEs is very good. I'll often fire up IntelliJ if I'm too tired to try and figure out how to figure out what's going on.
I agree. I did find one GUI that at least lets you drag & drop branch labels (GitX) but it's a bit buggy and Mac-only. It doesn't let you rebase via drag & drop, or cherry-pick via copy & paste. Those are pretty obvious mappings and would make Git way more obvious.
swarmdb has CRDT-based merge and causal branching which is "super smooth".
Lets you recombine changesets rather freely, or at least supposed to.
https://github.com/gritzko/ron-cxx/tree/master/db (the project is in its early stages)
Regarding language-aware AST-based diffs, I know of one serious full-time effort. Even for Java, it turned way too complex, so guys gave up.
And I feel it in my bones that there is a revolutionary GUI waiting to be invented. Why can't I drag a commit or set of commits from one branch to another? With safe, easy undo (reflog doesn't count) and super smooth conflict resolution? Etc etc.
And of course there is the interesting rabbit hole of semanitc / language aware diff. Line diffs suck in many ways.
It's one of the hundreds of of problems that I'd love to work on one day, but probably won't get a chance to. Sigh... :)