3 ms·
> The basic logical unit of Git is the commit, which is a set of changes to multiple files. In some circumstances it's helpful to think of commits as sets of c
by nemetroid 4y ago
> The basic logical unit of Git is the commit, which is a set of changes to multiple files.
In some circumstances it's helpful to think of commits as sets of changes, but Git's commit are snapshots and not changesets. Converting commits into sets of changes is done on-demand.
- rectang 4y agoThat's true until you get back to Git's packfiles, which use deltas. Git's form of compression is very interesting, but a key concept as you study the history of version control is that snapshots and deltas encode the same information, so the choice of which to use is an implementation detail[1]. [1] I covered this in detail in a preso on Git data structure design for Papers We Love San Diego: https://www.youtube.com/watch?v=fHSZz_Mx-Uo&t=400s https://www.youtube.com/watch?v=fHSZz_Mx-Uo&t=400s
- karatinversion 4y agoThis is sensitive to the design of the delta format though, once you have merges. I had to write some code to migrate version history from Rational Team Concert to git. RTC has a uuid for each version of a file, and deltas are a set of zero or more predecessor versions, and a successor version. This allowed the same delta to be present in different “streams” (branches), if they only differed in files not relevant to the delta; but it becomes impossible to reconstruct a history of snapshots from the change sets.
- kaashif 4y agoGit's user interface is very confused in this respect. You can tag a commit with a version identifier, and that indicates commits are snapshots or versions. But then you have cherry-pick, which seems to work by "applying a commit" (you can't "apply" snapshots...I think) with git cherry-pick hash. That's the same thing as git cherry-pick hash^..hash which seems to more clearly indicate that we're applying the diff between two commits. And rebase, which really does present itself as a tool for reordering diffs. Anyway, everyone knows what you mean when you say "apply this commit" so maybe this is just a stupid complaint about "consistency" with no real point. Especially since git does use both deltas and snapshots under the hood.
- rectang 4y agoIt's true that Git mostly stores snapshots rather than deltas internally. But it's still a version control system, and I don't think it would be improved if the abstraction of snapshots was leaked more aggressively into the interface. You can reconstruct a delta from a snapshot and vice versa, so it still makes sense that you can apply a commit (i.e. patch, delta) in Git. One thing about Subversion that I miss in Git: when you move a file or a directory, Subversion tracks where it came from. That's not true in Git, which just stores snapshots (mostly) and has to guess whether a file was moved. That means that `svn log FILE` behaves like `git log --follow FILE` by default (except always right rather than "right when the heuristics work"), which is much more useful. One thing I dislike about Git is that its history model tends to crumble when you move files and directories around.
- vlovich123 4y agoIt is kind of frustrating that git doesn’t remember when you tell it you moved something. On the other hand, it’s also convenient to be able to mv something and just have git figure that out. Another case git does better in is when you do complex refactors. So loving the bulk of the content of one file to another is recognized as a move whereas SVN wouldn’t help you. All in all, it’s a hard problem and there’s no perfect answer I think.
- rectang 4y agoThere's a solution which works most of the time, and it works equally well in Subversion and Git. Divide up the change into two commits: 1. Move the file verbatim. 2. Change the file as necessary. Subversion will remember the move because it always does — Subversion stores changesets internally, and those changesets contain history metadata. Git won't track the move, but when you run `git log --follow` later it detects that the file was moved because the content of the file that was added was the same as something which existed before. It's a little awkward if only a small portion of a file was migrated, though. In such a case, you may just have to fall back to documenting in the commit message that code was moved. This approach has another benefit: it is easy to review. The first commit can be validated as "verbatim move — check!". Then the logical change can be validated with effort proportional to the amount that was changed. In contrast, if you lump them all together, it can be hard to discern what changed when presented with a huge diff containing mostly verbatim move but also a few subtle changes.