3 ms·
Why does thinking about commits as snapshots make cherry-pick and rebase easier to understand? I've always thought of and taught commits as diffs because git do
by phailhaus 6y ago
Why does thinking about commits as snapshots make cherry-pick and rebase easier to understand? I've always thought of and taught commits as diffs because git does a good job of abstracting away the distinction.
EDIT: To be clear, I think this is one of the _few_ abstractions that git doesn't leak. It does a pretty bad job everywhere else.
- recursive 6y agoA snapshot is easy to understand. It's just the contents of a file at a point in time. Diffs can have different definitions, and I've never seen a complete rigorous one for whatever git uses, if anything. Loosely a "diff" is the changes between one version an another. But to understand what cherry-pick does, we (or maybe just I) need more than a loose definition. Using snapshots makes `rebase` trivial. The contents of a commit doesn't change. Just the link to its predecessor. Using a diff model, it requires all kinds of fancy changes.
- masklinn 6y ago> Using snapshots makes `rebase` trivial. The contents of a commit doesn't change. Just the link to its predecessor. It's the exact opposite. When you rebase, you want to apply the changes you're rebasing atop other existing changes. If you just copy over the snapshot, you break everything. Rebasing a snapshot basically smashes your history and is utterly useless. That would make a rebase-pull… implicitly revert every intermediate commit. Although AFAIK Git really uses the merge machinery to perform rebases: conceptually (I don't know if it does that technically) it merges each commit to rebase onto the rebase target, then copies over the commits without the old (rebased-from) parent.
- recursive 6y agoWarning: When I made this comment, I was under some false assumptions. Here's the original. --- A snapshot is the aggregate of all previous changes. I don't see what could be broken by copying the snapshot. I'm not familiar with rebase-pull, but I don't see why any revert would be involved. The snapshot before the rebase is exactly the same as the snapshot after the rebase. Only the predecessor is changed. The contents of the predecessor doesn't matter, since a snapshot contains the entire state of the tree.
- masklinn 6y ago> A snapshot is the aggregate of all previous changes. I don't see what could be broken by copying the snapshot. If you copy the snapshot, you lose all the intermediate commits you're rebasing onto, because they're not in the snapshot you're rebasing. > The snapshot before the rebase is exactly the same as the snapshot after the rebase. Which is exactly what you do not want. > The contents of the predecessor doesn't matter, since a snapshot contains the entire state of the tree. Which, again, is exactly what you do not want: a - b - c - d \ e - f If I rebase `d` onto `f`, I want the changes performed by `e` and `f` as well as those performed by `d`. If I just copy over the `d` snapshot to d', I get the exact same state I had at `d` with a different history, losing the changes performed in e and f, thus reverting them. That's why when you rebase a commit, not only does the commit id change (makes sense, different parent) the tree also changes, which also makes sense because the content changes.
- recursive 6y agoOk I see. I actually misunderstood what rebase actually did. So I guess it's a good thing then that I've never used it. Thanks for your persistence.
- jfengel 6y agoRebase is useful for artificially straightening out simultaneous work. It's less clear in the above example, where d is just a single commit. But if there's a d1--d2--d3--d4, it can be confusing to merge chronologically (d1--d2--e--d3--f--d4). If there aren't any conflicts, it's conceptually simpler in the long run to rebase it as e--f--d1--d2--d3--d4. You artificially serialize the work, which isn't what actually happened, but it could have happened that way. When you go back and look at it, pretending that it did is simpler. For example, if you have to go back and debug something that happened while working on d1...d4. It's not absolutely mandatory to learn about it. But it the simple cases are common enough, and useful enough, that it's worth learning.
- pjc50 6y agoThat didn't stop you from arguing that it was trivial upthread...
- phailhaus 6y ago> I've never seen a complete rigorous one for whatever git uses, if anything Isn't that the point of a conceptual model? Thinking of commits as diffs abstracts away the actual implementation, which allows me to understand cherry picks and rebasing without worrying about object OIDs and trees. I can think of those actions as "applying the diffs onto other commits", even though technically it can't be implemented that way.
- recursive 6y ago> Isn't that the point of a conceptual model? Probably, but my understanding of the conceptual model is full of holes. There are many scenarios to which I don't know how cherry-pick would react. Maybe I should just try them. "applying the diffs onto other commits" is all well and good for simple scenarios where there are not multiple interpretations of changes. But for other scenarios, it requires one to know what's actually in a diff, which I don't know. In fact, different git clients present diffs differently. Is that evidence that diffs aren't a real thing? I don't know.
- the_duke 6y agoIt does a good job in simple scenarios. It breaks down often. I've seen plenty of horribly botched merges and rebases that took many hours to fix up. We learn to work around this by adapting and limiting our workflows. Most devs are just so used to git that a different model like Pijul is hard to reason about or see the benefits of, but improvement is definitely possible.
- phailhaus 6y agoTo be clear, git has a horrible interface and is the leakiest abstraction. But I don't think that the snapshot-diff distinction is one of those leaks.
- Nullabillity 6y agoIt highlights why they're generally bad ideas that should be used sparingly, and certainly not as the default.