4 ms·
> From a storage perspective, describing commits as snapshots seems like a bad mental model. Suppose I have a directory that is 100MB in size. If I take a snaps
by cesarb 6y ago
> From a storage perspective, describing commits as snapshots seems like a bad mental model. Suppose I have a directory that is 100MB in size. If I take a snapshot of it, my snapshot would be 100MB in size. If I take a 2nd snapshot of it tomorrow, my 2nd snapshot would also be 100MB in size. My total storage needs would now be 300MB.
That's not what one would expect. Suppose I have a directory that is 100MB in size. If I take a snapshot of it ("btrfs subvolume snapshot"), my snapshot would be 100MB in size, but the storage needed for the original and the snapshot together would still be 100MB (plus a few kilobytes of overhead). If I take a second snapshot of it tomorrow ("btrfs subvolume snapshot" again), my second snapshot would also be 100MB in size, and my total storage needs would still be 100MB (plus a few kilobytes of overhead).
If I made a change to a small text file before each snapshot, my total storage size would still be barely larger than 100MB.
That is, when creating a snapshot, one would expect it to be copy-on-write. While not exactly what git does (it's a content-addressable storage instead of a copy-on-write storage), the end effect is similar enough for most purposes (the main difference being that undoing a change in git would not need extra storage, while a copy-on-write storage would store a new copy of the contents).
- whack 6y agoI've learnt something new today, thanks for sharing. Looks like I had a naive understanding of how snapshotting actually works. I still think that it's more intuitive to describe commits as diffs, in the context of things like cherry-picking a commit or rebasing/reordering a series of commits. But given that you can also "check out" a commit, in order to get a specific snapshot of the repo, I can see the parallels between commits and snapshots. Maybe both analogies are equally useful in describing the different features that git provides.
- diroussel 6y agoThe point of the article is not an analogy. Git is based on snapshots. Abs diffs are computed from snapshots as needed. The snapshots are also de-duplicated and compressed, but that is not important. The article is a good one. And if you spend the time to understand git it gets easier to use.
- whack 6y agoInternal implementations and external interfaces are not necessarily the same thing. When reading a single-threaded application's code, it is helpful to read it as a series of instructions, executing serially. In reality, both the compiler and your CPU are constantly reordering instructions, and executing them in parallel/out-of-order. However, this is all done while still preserving the illusion of serial-execution. Taking a beginner programmer down this rabbit-hole of implementation details, is going to be more harmful than helpful. Thank you for the suggestion, but I already find git easy to use. And thinking of commits as diffs that can be cherry-picked, rebased and reordered, is something that helps me greatly in understanding it.
- breischl 6y ago>I still think that it's more intuitive to describe commits as diffs, in the context of things like cherry-picking a commit or rebasing/reordering a series of commits. If I understood the article correctly, those things actually are implemented via diffs. It's just that the diffs are calculated on-the-fly, used to create a new snapshot, and then discarded.
- AaronFriel 6y agoIt's helpful to understand git in terms of the "porcelain" and the "plumbing". The git commands you know and love are largely the porcelain, nice fixtures over other things. When you "git cherry-pick", under the hood what it's actually doing is querying that commit's parent(s), finding the diff the commit introduced relative to its parent(s), and then applies those same changes to the index and your working tree. Cherry-pick is porcelain on top of the plumbing. There are a few "write git yourself" tutorials out there, of which "Write yourself a Git!" is I think the most popular. In it, you'll learn how git really stores data, and you'll write a (fairly basic) git client that can do several things to locally manage a repository. Write yourself a Git!: https://wyag.thb.lt/ https://wyag.thb.lt/
- spuz 6y agoThe correct way to think about snapshots and diffs when it comes to cherry-picking and rebasing is to realise that diffs are always derived from snapshots. I.e. the fundamental data-structure is the snapshot and from those we can build diffs. Those diffs are necessary to implement cherry-picking and rebasing but it's also possible to imagine an implementation git that has those features missing. It would still fundamentally work in the same way - it would just be slightly less useful. Edit: If you think this is just splitting hairs, I encourage you to look at the differences between git and pijul which is a VCS where the fundamental building block is diffs: https://pijul.com/ https://pijul.com/
- whack 6y ago> The correct way to think about snapshots and diffs when it comes to cherry-picking and rebasing is to realise that diffs are always derived from snapshots. Ironically, git snapshots are themselves derived from diffs. Creating a snapshot without diffs, would require making a full copy, which git most definitely does not do. So would you rather think of cherry-picking as diffs derived from snapshots which are derived from diffs? Or as simply diffs? I find the latter better as a mental model.
- throwaway894345 6y agoCopy-on-write is an implementation detail that allows for lower storage. The snapshot is still the full copy. One could try to argue that the same is true for git in that diffs (or content addressable storage) are just an internal implementation detail, but as the parent pointed out that's not quite true--our commits document the diff, not the materialized snapshot.
- nybble41 6y ago> our commits document the diff, not the materialized snapshot That's not actually true, though. This is what a raw commit object looks like: $ git cat-file commit bfc766d38e1fae5767d43845c15c79ac8fa6d6af tree 99768f8965d5382d1c1695c371a854d061f2548b parent 860a3b34854d8abe9af9f1eb584691de926ce897 author Peter Maydell <peter.maydell@linaro.org> 1462981466 +0100 committer Peter Maydell <peter.maydell@linaro.org> 1462981466 +0100 Update version for v2.6.0 release Signed-off-by: Peter Maydell <peter.maydell@linaro.org> (This commit is from the QEMU project repository.) Note that there is no reference to a diff. There is a reference to a tree, which is a binary object representing a directory structure—a full snapshot of the state of the working tree as of that commit: $ git ls-tree 99768f8965d5382d1c1695c371a854d061f2548b 100644 blob 3ac0cfc6f0d60a95a5c3557497835c80e52a1696 .dir-locals.el 100644 blob 37755ede83a0d5b4fe22114f624a140d2bcaefff .exrc 100644 blob 88a80ff4a5c552bad3bc2bf40b1fc4a45c57a177 .gitignore 100644 blob 9da9ede26161bc5c6f12552b55bc54f55bb1e839 .gitmodules … Each of those blobs is a reference to the full content of the corresponding file: $ git cat-file blob 3ac0cfc6f0d60a95a5c3557497835c80e52a1696 # .dir-locals.el ((c-mode . ((c-file-style . "stroustrup") (indent-tabs-mode . nil)))) For storage purposes there is deduplication, delta encoding, and compression going on behind the scenes, so committing a 100M working tree with a few small changes doesn't take up 100M of additional storage space, but these are invisible to the upper layer. When Git needs a diff, for example to perform a three-way merge, or in response to `git show` command, it generates one on the fly from the snapshots.
- throwaway894345 6y agoApologies for the typo, I meant to say “...commit messages document...”
- barrkel 6y agoCopy on write filesystems describe changes as a structural diff, effectively.
- klodolph 6y agoThat's not really true. The copy-on-write filesystems just allow multiple files to reference the same blocks, and only allow modifications to blocks if the refcount is 1. At least, at its simplest, that's how copy-on-write works. To copy a file, you copy the block references and increment the reference counts. You won't end up with a diff or deltas stored anywhere.
- barrkel 6y agoSure it is. You just need to look at it a little differently. Even in classical COW of memory pages in a Unix forked process, the set of pages mapped into the process with refcount 1 are a diff to all those with refcount > 1. Virtual machine snapshots are more explicitly diff-oriented. Deltas to the base disk or snapshot are stored separately (that's your diff), and "deleting a snapshot" actually means remapping all the separately stored blocks and collecting the newly released blocks. There's two strategies snapshotting can follow: copy-then-write-in-place, or redirect-on-write. Either way, the set of copied or redirected blocks are a diff to the in-place blocks, just the polarity of the difference is switched. See e.g. https://www.dell.com/community/Student-Discussions/Copy-on-write-Redirect-on-write/td-p/7147904 https://www.dell.com/community/Student-Discussions/Copy-on-w... Things get more interesting with e.g. ZFS snapshots, where the whole filesystem, including metadata, is copy-on-write, and tree-structured to maximize sharing and permit atomic writes (how ZFS solves RAID5 write hole). There, snapshots hold on to one of the old roots in the tree. The diff is implicit in the difference in tree structure; shared blocks are common, different blocks are different. It's super-easy to do a recursive comparison between such trees, extracting a diff is a sublinear time operation because it can trivially skip over identical subtrees. It's a matter of perspective, when you're in the middle of a recursive tree compare, whether you think you're actually diffing data, or whether the data in one leg of compare is simply telling you which subtrees are shared and which subtrees are different, and thus the data is a delta, or diff. You certainly don't need a complete traversal, which tells me that the data is doing most of the work.
- crazygringo 6y agoClearly people are using two diametrically opposed definitions of snapshot. If a snapshot is defined is opposed to a diff, then it's clear snapshot means "full copy". If I snapshot the state of my cloud server, it creates a full copy of its disk in block storage somewhere, and takes several minutes to complete. You are describing snapshots that exist as part of a diff system or copy-on-write system, where they use virtually no storage at all, because further changes are assumed to be applied as diffs rather than overwriting previous data. Where the snapshot is a "marked" diff that can specifically be rewinded to, as opposed to a general ongoing stream of diffs. But that's a more advanced and system-specific definition of snapshot. As a general mental model, when you say "think of it as a snapshot not a diff", I think it's clear that the former definition is being used, and that the expectation is a fully copy that takes up disk space. Because otherwise, in the second case, all the snapshots are just the most recent diff (on top of the entire prior history), so the sentence "think of it as a snapshot not a diff" doesn't really mean anything. The snapshot and the diff are the same.
- klodolph 6y ago> If I snapshot the state of my cloud server, it creates a full copy of its disk in block storage somewhere, and takes several minutes to complete. Which cloud provider are you using? Neither Amazon nor Google take snapshots this way. Amazon EBS and Google Persistent Disk both use copy-on-write semantics for snapshots. If you take a hundred snapshots of a 100 GB disk, your total usage is 100 GB plus metadata. When you run a VM instance from that disk, the storage usage will increase as blocks change, to a maximum of 200 GB total storage (for live disk + out of date snapshot). When I use QEMU or VirtualBox at home, I also get copy-on-write snapshots of disks, although it's certainly possible to get a full copy if you want. I think the feature is pretty standard.
- crazygringo 6y agoDigital Ocean. It absolutely takes snapshots by making a full copy: https://docs.digitalocean.com/products/images/snapshots/ https://docs.digitalocean.com/products/images/snapshots/ So this is a perfect example of what I mean by the word "snapshot" being used in two different ways by different people. Snapshot meaning "full copy" is one usage (Digital Ocean), snapshot meaning "diff checkpoint" is another usage (Google, AWS).