3 ms·
One thing about git I learned the hard way is the use of diffs and patches (more accurately, 3-way merges) for operations like merging, cherry picking and rebas
by goku12 3y ago
One thing about git I learned the hard way is the use of diffs and patches (more accurately, 3-way merges) for operations like merging, cherry picking and rebasing. Pro-git (correctly) emphasizes the snapshot storage model of git - it helps a lot in understanding many of its operations and quirks. But the snapshot model can cause confusion in the case of the aforementioned operations - especially rebasing.
For example, I couldn't understand why the deletion/dropping of a commit during a rebase caused changes to all subsequent commits. After all, I only asked for a snapshot to be dropped. I didn't ask for the subsequent snapshots to be modified.
Eventually, I figured out that it was operating on diffs, not snapshots (though storage was still exclusively based on snapshots). The correction on that mental model allowed me to finally understand rebasing. (I did learn later that they were 3-way merges, but that didn't affect the conclusions).
That assumption was eventually corroborated somewhere in Pro-Git or the man pages. But I couldn't find those lines again when I searched it a second time. I feel that these operations can be better understood if the diff/patch nature of those operations are emphasized a bit more. My experience on training people in rebasing also supports this.
PS: Thanks for the book! It's a fantastic example of what software documentation should look like.
- smallpipe 3y ago> Eventually, I figured out that it was operating on diffs, not snapshots The snapshot include all the history that led to the current snapshot. So even if you did a squash instead of dropping, you're changing everything that depends on that
- goku12 3y ago> The snapshot include all the history that led to the current snapshot Git snapshots don't contain any history, other than the commit chain (reference to the parent commit/s) in the commit object. While the storage format is a bit complex, they behave fundamentally like a copy of the working tree at the point of commit. > So even if you did a squash instead of dropping, you're changing everything that depends on that Squashes don't change the subsequent commits/snapshots either, other than the commit ID and chain. The tree itself remains untouched. You can verify this.
- elevatedastalt 3y agoYes, but since they change the commit id, and since each commit has a pointer to its parent commit, you have to rewrite all the commit objects, even if the tree doesn't change.
- goku12 3y agoThe context of my first comment is about how a rebase affects the contents of the tree. It's about predicting what happens to subsequent snapshots. Bringing commit ID and parent commit into that context complicates and obscures the point I'm trying to convey.
- seba_dos1 3y agoWhat I'd stress out is that rebasing is nothing else than automated cherry-picking, as it's hard to imagine cherry-picking in any other way than 3-way merge or patch operation.
- phillco 3y agoJulia Evans wrote a nice blog post about this recently, in that sometimes it's helpful to think commits as snapshots and sometimes as diffs. https://jvns.ca/blog/2024/01/05/do-we-think-of-git-commits-as-diffs--snapshots--or-histories/ https://jvns.ca/blog/2024/01/05/do-we-think-of-git-commits-a... I guess while it's true the storage layer is snapshot based, as you say, that only gets you so far conceptually, and it's probably best to focus on the _operation_ you're doing, as rebase, cherry-pick, apply-patch, etc are easier to think in terms of diffs. When I used to use Phabricator, the fact that I could always fall back to handing it a raw patch file to submit changes also made it easier to reason about (regardless of what the server and client were actually doing).
- infogulch 3y agoCommits have a dual snapshot-like and diff-like nature, depending on how you observe them.