4 ms·
I can't draw, but IMO git's basic operations are pretty easy to visualise if you think of the repository and commits as a graph (a tree). You start with the fir
by chousuke 16y ago
I can't draw, but IMO git's basic operations are pretty easy to visualise if you think of the repository and commits as a graph (a tree). You start with the first commit/snapshot as the root, and every node after that is a child of the root. To bring diffs into this model, I suppose you could think of the edges as the "diff" between two snapshots; ie. how to get from node A to child A'.
The staging area is pretty simple. You can think of it as a "WIP" snapshot: it's what gets recorded in the tree when you make your next commit. It may seem like extra work if you're just used to committing every change you have in the working directory, but at least I tend to not want to do that, and git has some pretty cool tools for crafting neat commits out of working tree contents. :)
A node can of course have multiple children, so branches are natural in the git world. "Named" branches are merely pointers to a snapshot somewhere in the branching structure of the tree.
Merges happen when two branches join together: a new merge snapshot M has edges from both parents A and B (or more, if needed) representing the conflict resolution, and M represents the finished merge.
Rebasing or reparenting is somewhat different. You can think of it as cutting a branch from the tree and gluing it elsewhere. ie. if you rebase branch B onto branch A, then the divergence point of A and B is looked up, and all the edges (diffs) from that point on until the tip of B are applied on top of A, resulting in a series of completely new snapshots. This is why rebasing is "dangerous" or destructive: branch B is no longer the branch it once was, but something completely different.
Unlike in a merge, information about the original branches, or any changes needed to fit the changes on top of the new base, is discarded.
Of course, git won't actually destroy anything during rebases either. As long as you keep a reference to the original branch B, git will keep the snapshots and you can go back to them. There's just no straightforward way to tell that the original B has at one point been rebased on top of A.
EDIT:
I think I should note that every "ref" in git is a reference to this giant tree data structure. Let's take for example origin/master. When you fetch changes from the origin repository, git fetches snapshots from the remote data structure, includes them in your own local one, and updates origin/master reference to point to the correct tip in the data structure so that it corresponds to the remote's master branch.
The same happens for other branches, too of course, depending on what you tell git to fetch. (git can also handle snapshots completely unrelated to other commits in the repository. ie. you can have multiple root commits, but that's not often done. :P)
A "pull" then goes a step further and merges your local 'master' with origin/master and updates your local reference. (origin/master remains untouched until you fetch new changes or successfully push your merged master to the remote)
In essence, git manipulates references to a growing, mostly immutable, distributed data structure. I think it's simple and elegant, but YMMV. :)
- lysium 16y agoDoesn't git operate on a DAG rather than a tree? I mean, you have merges.
- steveklabnik 16y agoYes, it's a DAG. It's easy to slip and say 'tree', though. While not strictly correct, it's pretty close.