In git, commits play competing roles. They represent the mechanical history of your codebase, letting you restore/merge the state at various points. They're also often used as a legible, coherent history meant for reading or git bisect.
These two uses are at odds with each other – for the mechanical part, you want to make lots of small commits all the time, and for the legible part you want to have a small number of meaningful changes.
There are ways around this within git – for example, git rebase -i lets you create a new set of reorganized commits that have the same end result as your original commits. But all of these methods have enough friction that I've never seen a team actually stick to them.
I've been playing around with jj, a version control system on top of git. jj works seamlessly with existing git repositories/commands, and you can undo all operations, so trying it is very cheap.
jj gets around this problem by explicitly having separate concepts for Revisions (representing the mechanical history) and Changes (representing the logical history.)
Revisions are saved every time you do anything in jj, even with commands like jj log. Amongst other things, this lets you undo any command.
Changes, unlike commits, can be edited after creation. Suppose you have Change 1 -> Change 2 -> ... -> Change n, and then notice you made a typo in Change 1. In git, I would usually just make fixing the typo Commit n + 1, and not bother to go back, rebase, etc. In jj, I can just edit Change 1.
I've only been trying this for two weeks, but I like it a lot so far – I find myself making PRs with much cleaner commit histories, and experimenting more because I know I can just undo when needed.
like this
Ben Weinstein-Raun, Daniel Filan, Daniel Ziegler and Ben Millwood like this.
Ben Weinstein-Raun
in reply to Satvik • • •Satvik likes this.
Satvik
in reply to Ben Weinstein-Raun • •Ben Weinstein-Raun
in reply to Satvik • • •Satvik likes this.
Soccum Speleodontidae
in reply to Satvik • • •I experienced much of the benefits by just having a more efficient interface to git as-is. In particular, reducing the friction of selective staging specific subsets of changes made creating what you describe as the "mechanical" (smaller) commits easier, and happen more naturally. I would often work out a full set of changes first, and then go back and incrementally commit logically-related subsets of the change, which might not have been in the same order I actually performed the corresponding edits. This also reduced the amount of rebasing I felt was necessary, but rebasing is also easier with a better interface.
The most efficient interface I'm aware of is magit.vc/, which I first tried when "trying to learn emacs again". I still feel like I hardly know emacs at all, but even just magit was excellent enough on its own that I miss it enough to occasionally fire up emacs just for magit when I have a lot of otherwise-tedious specific git manipulations to do. This most frequently happens when I'm rebas
... show moreI experienced much of the benefits by just having a more efficient interface to git as-is. In particular, reducing the friction of selective staging specific subsets of changes made creating what you describe as the "mechanical" (smaller) commits easier, and happen more naturally. I would often work out a full set of changes first, and then go back and incrementally commit logically-related subsets of the change, which might not have been in the same order I actually performed the corresponding edits. This also reduced the amount of rebasing I felt was necessary, but rebasing is also easier with a better interface.
The most efficient interface I'm aware of is magit.vc/, which I first tried when "trying to learn emacs again". I still feel like I hardly know emacs at all, but even just magit was excellent enough on its own that I miss it enough to occasionally fire up emacs just for magit when I have a lot of otherwise-tedious specific git manipulations to do. This most frequently happens when I'm rebasing some large/deep/complex patch set that needs to be maintained against some moving upstream.
I find github.com/kahole/edamagit to have the relevant subset of magit I often want to use during regular development. Definitely not as complete of an implementation, and feels slightly buggy compared to the emacs version, but usually good enough. In particular, if I recall correctly, committing incremental subsets of a larger changeset (to retroactively create one possible history of "mechanical log", in an order that makes sense) using interim states that aren't exactly what the final state currently looks like, was easier to do with emacs' way of buffer management compared to in vscode/codium.
like this
Ben Weinstein-Raun and Satvik like this.