The git parable
tom.preston-werner.com
tom.preston-werner.com
If you want to read the previous discussion it's here:
Started off easy enough (loved the simplification), but as soon as we got to SHA-1 snapshot names and "Rewriting History" with the redoing of snapshots and then to the staging directory my eyes glazed over.
It dawns on me that:
1. I have been using every SCM since CVS basically like CVS (Subversion and Git specifically). 2. I have a GitHub account I use to host all my open source and commercial code. 3. I got it because it was the "cool thing to do" and I felt left out of the loop ignoring Git for so long. 4. I have no idea how to really use Git in the ways that make Git a powerful thing. 5. The few times I have seen real Git errors like "Snapshot cannot be rebased, please schnoggleford your glerpentrap!", they have freaked me out and never been immediately obvious as to what is wrong; if it weren't for Google search'ing error phrases I would probably be using post-it notes with hand-written source code as my SCM system. 6. I REALLY want an graphic showing these different concepts explained in the article. I am a visual learner and reading (at 6am) through the snapshot re-parenting section without visual indicators of what is actually happening just isn't clicking.
It seems to me a graphic compliment to this article of what situation the main character has every step of the story would make this pretty much the ultimate "get your bearings" resource for new Git users.
At least I think so.
Who knows, I'm still excited when my pull and push commands complete without errors :)
I recommend reading the tutorials on gitready.com. For example, http://www.gitready.com/intermediate/2009/01/31/intro-to-reb...
I'm reading the Dutch translation at the moment.
Edit: for an example page with visuals, see http://progit.org/book/ch3-1.html and http://progit.org/book/ch3-4.html
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. :)
Its not really "source control" until you can have multiple team members making changes to entire source sets independently and then reconcile them in a controlled manner. Missing that, you have a "version control" system - little more useful than zipping up your source periodically and saving it in a folder.
I'm not sure the article explains enough to put git into perspective - its mostly a "what is version control" parable?
The parable walks you through git's data model and is therefore git-centric. The metaphors used are a rather thin veneer on top of the way git actually works instead of a thick and leaky abstraction.
You could apply the high-level concepts across other systems but they would not map as cleanly or completely.
Forgot to mention: there are also build-control-systems, release-control-systems. All often confused/muddled in discussions.
I actually like git but I haven't found a tutorial that demonstrates a workflow for non-ideal conditions.
working tree --[add]--> index --[commit]--> HEAD
index <--[reset]--- HEAD
^----[diff]----^ ^-[diff --cached]-^
• git add moves things from the working tree into the index• git commit creates a new commit from the index and makes it your new tip commit, typically updating whatever branch is indicated by HEAD. (HEAD literally is just .git/HEAD and is normally the name of a branch, take a look)
• git diff shows you the difference between the index and the working tree, i.e. unstaged work
• git diff --cached shows you the difference between the index and HEAD, i.e., staged but uncommitted work
• git add /path/to/file will stage an entire file
• git add -p /path/to/file lets you stage individual hunks from a file
• git reset unstages everything
• git reset /path/to/file unstages that file
• git reset -p /path/to/file lets you unstage individual hunks from a file
Now you know how to stage/unstage individual bits of work, either a whole file at a time, or bit by bit. You also know how to commit what you've staged, and how to view what you're about to commit.
So if you've started to stage some work and you're ready to stage a different commit, just commit the stuff you've already staged. e.g., let's say (for simplicity) you've edited foo and bar and want to make those changes separate commits:
$ git add foo
$ git diff --cached (confirm it's what you expect to commit)
$ git commit -m "add fn() to foo"
$ git add bar
$ git diff --cached (confirm it's what you expect to commit)
$ git commit -m "fix bug in x()"
In between making those two commits, you may wish to test the first commit. You could do that like this: $ git add foo
$ git diff --cached (confirm it's what you expect to commit)
$ git commit -m "add fn() to foo"
$ git stash save "temporarily set aside work so I can test foo"
At this point, your unstaged/uncommitted work has been set aside and your working tree matches (with respect to the files git is tracking) the commit you just made. Here's where you can perform testing, potentially amending foo (git add, git commit --amend). Okay, you're ready to continue: $ git stash pop
$ git add bar
$ git diff --cached (confirm it's what you expect to commit)
$ git commit -m "fix bug in x()"
I hope this helps. The key thing (IMO) is really to understand what git is doing under-the-hood. The git UI and (especially the) documentation are all over the place. Don't be afraid to start with a test repo and peek inside .git to see what it's doing as you execute commands.Good luck!
git stash is exactly what I was looking for in order to save my index without having to make a random commit. And then the -p option is icing on the cake (ps, it also works for stash).
I ran into a tiny problem with git stash. Assume I have a file called README. If I do the following:
$ git mv README README.markdown
$ git stash save
$ git stash pop
The index no longer knows that I did a 'git mv'. It turns out the --index option will try to reinstate the changes made to the index, but it might cause a merge conflict.Also, the patch interactive mode documentation says "g - select a hunk to go to" yet it never gives that option. I thought it might not allow it if there are a small number of chunks, but I even tried it on an old repo I have with 7 modified files and 'g' was never an option.... Have you ever used g?
-g works for me, but it's only an option if there's more than one hunk in a single file. You can use 's' to split a hunk up if its non-contiguous.
I try to teach real VCS, starting with simple patches, tarballs and merges and going through git, like it is an "patch/merge automation process". I realize they really like it and I try to improve some techniques to teach real VCS to students.
Glad to hear from people who's passing through same situation as mine :-)
It covers a lot of the conceptual differences between the distributed version systems and subversion, and gives a similar sort of story telling walk through of use cases.
I wonder, though, if some graphics would not have helped. People who hesitate to use VCS or who barely understand SVN will have a hard time understanding git without any graphic explanation of the DAG on which git operates, I presume.
I really like the idea of a tool not having its underlying details poking out.
Mercurial is a beautiful tool, with very few of the sharp corners poking out. But, at least for the source trees and development branching style I deal with, I'm convinced Git is more adaptable to any workflow you can imagine.
In the end, it may very well be the case that you HAVE to understand Git that well, because that means truly understanding the tree perturbations you're trying to manage. I've been slowly coming to believe that has to be true.