5 ms·
Commits are snapshots. How to represent those snapshots, and fix the storage bloat a naive implementation would cause, is a completely different problem. One
by outworlder 6y ago
Commits are snapshots.
How to represent those snapshots, and fix the storage bloat a naive implementation would cause, is a completely different problem.
One of the things that makes Git smart is that it doesn't try to optimize things prematurely. SVN and co. would store actual diff data, but this made some operations really hard to implement (and, in many cases, slow).
Git has commits conceptually as snapshots. It's up to the storage code to figure out how to deal with this.
> But I find it far more intuitive and useful to think of commits as "diffs + some metadata".
Except that this is not what's happening. I wouldn't even call it an abstraction, it's how things actually work. What you call abstractions are actually operations. If we run a diff we are interested in the changes, but if you ask git to show you the commit it will show you just that.
If you think a commit is a diff, you have a mismatch between the mental model and what's actually happening behind the scenes. This will make it difficult to understand concepts later on.
- slavik81 6y ago> If we run a diff we are interested in the changes, but if you ask git to show you the commit it will show you just that. git show <commit SHA-1> will output a diff.
- trulyme 6y agoI think this is more a sign that git (porcelain) is not aligned with the underlying model. It is actually a pity that so little effort went into git UI. I find the OP explanation of git model awesome and the presented concepts beautiful, but the cli utility has countless naming and consistency problems which make me sad that hg didn't win over git. Life would be much simpler for many developers if it did, imho.
- munk-a 6y ago> If you think a commit is a diff, you have a mismatch between the mental model and what's actually happening behind the scenes. This will make it difficult to understand concepts later on. I don't think those concepts are distinct as you're painting them. At a user visible level commits will almost always be visualized as diffs, which puts us at a place where - at the highest level and lowest level they're defined as pretty close to diffs, while at an intermediary level they're defined closer to snapshots. I honestly think they're neither, each expression method (diff vs. snapshot) can be translated pretty easily and both are trying to represent the same end goal. It can be helpful to know that commits are representative of the full state of the codebase that exists at a time, but that view can be at odds with merging and rebasing which use actual change sets to calculate - when a commit is being manipulated it's helpful to view it as a diff (and git does this) - while as, when a commit is being read, we're using it as a snapshot.
- mewse 6y agoStructure purist, ingredient rebel: A snapshot between two levels of diffs is a sandwich.
- xmprt 6y agoOne way I like to think about this is that when you rebase a branch, the diffs are the same (barring any conflicts) but the commits are different. Just another reason commits aren't the same as diffs.
- klodolph 6y agoThe diffs are often different, even without conflicts. Try comparing them some time, and look closely at the diff... look at the lines starting with @. People usually ignore those lines but "patch" does NOT. This is not an irrelevant detail, but it's the result of a three-way merge. The three-way merge can update those @ lines if it has a complete set of inputs (all three inputs). If you to make a patch from one branch and then apply it to a different branch without using the three-way merge algorithm (stripping the diff of all its context), the patch may fail to apply even if the three-way merge succeeded without conflicts.
- hibbelig 6y ago> If you think a commit is a diff, you have a mismatch between the mental model and what's actually happening behind the scenes. This will make it difficult to understand concepts later on. I find that thinking of commits as snapshots is not so useful. I prefer to think of them as a pair of parent commit and diff. With that in mind, things like rebase become obvious: Take the same diff and attempt to apply it to a different parent. It's not clear to me how thinking of commits as snapshots helps me to explain operations such as rebase. I do concede, however, that "git cat" (I think that's the command) seems more closely related to a snapshot: you identify a commit and a file, and it will give you the content of that file at that commit. Clearly in this case the concept of a snapshot works well. But I need this very rarely.
- tlb 6y agoRebase doesn't work that way, though [0]. It first extracts the 3 versions (2 leafs and their common ancestor) and then does a diff & patch. This allows git to store the deltas between versions in the most efficient way on disk, while also letting it use contextual diffs to minimize the chance of spurious merge conflicts. Patching algorithms have various heuristics that make sense for programming languages, like special treatment for lines with only changes in whitespace. (Edited to add:) also, minimal diff algorithms have to do a lot of work to detect large blocks of text being moved around. This is part of what made Subversion, which used the same diff algorithm for storage compression and merging, painfully slow. [0] https://git-scm.com/book/en/v2/Git-Branching-Rebasing https://git-scm.com/book/en/v2/Git-Branching-Rebasing
- hibbelig 6y agoHere is the paragraph that describes what rebase does: > This operation works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. Is "applying the diff to a different parent" not a good way to describe this?
- hongsy 6y agoIf commits are snapshots: - say i have a repo with one file, a big 100MB csv with millions of lines. - i change one line in the CSV for one commit. - repeat multiple times in many many commits. - how big will the repo be?
- hongsy 6y agoi convinced myself that commits are snapshots by doing the following: # generate a 100M text file base64 -b 76 /dev/urandom | head -c 100000000 > file.txt git add . && git commit -m "1" # remove first line and add a new line to bottom tail -n +2 file.txt > tmp && rm file.txt && mv tmp file.txt && base64 -b 76 /dev/urandom | head -n 1 >> file.txt git add . && git commit -m "2" # repeat tail -n +2 file.txt > tmp && rm file.txt && mv tmp file.txt && base64 -b 76 /dev/urandom | head -n 1 >> file.txt git add . && git commit -m "3" ... du -sh . # a very big folder each of the commits are almost 80M big in the git folder. if you run `du -h .` you can see how git stores each object individually (80M big)