Git is the best version control system available today, miles ahead of centralized VCS. It has beautiful, consistent concepts internally. But its API is crap. Commands and options are inconsistent, internal details are exposed, and there are many ways to perform the same operation. Let’s try to narrow the options to only the ones that are a good idea.
Learning git command-by-command is difficult, because each command has multiple jobs. (I’m looking at you, git reset.) The objective of this post and later posts in this series is to learn the best commands and forms for advisable operations, trimming away the hairy details. Like every other language and tool that’s too flexible for our own good, git will serve us better if we stick to the good parts.
Let’s look at what Scott Chacon calls the Three Trees in git. These are the three locations where files are stored: the working directory, the staging area, and in a commit (inside the repository). I want to see the best way to find out what is different between these trees, and how to move files around between them.
The first level of simplification is settling on consistent terms for the three trees. The help pages are not consistent, but I will be.
Working directory: this is your project directory on the local filesystem.
Staging area: the nebulous half-formed commit that will be next.
HEAD commit: the most recent commit on your current branch. Each commit contains a tree representing the state of all the files in the repository.
Find out what’s different between them:
The git diff command by default shows the difference between the working directory and the staging area – these are listed in git status under “Changes not staged for commit.” If you want to see all the differences between your working directory and the last commit, use git diff HEAD.
Next, move files between these three trees:
Caution: these commands (other than commit) need a path argument. You can use . as the path to say “all the files,” but leaving off the path for git reset or git checkout can cause the command to do something entirely different. Specifically, if you provide a different commit as an argument instead of the default of HEAD, scary stuff happens. If your objective is only to modify files in the working directory or staging area, be sure to specify a path.
Of course, git commit doesn’t put the files in the staging area into the current HEAD commit. Rather, it creates a new commit with that tree, and makes that new commit the HEAD. This is not pictured.
There are many other ways to see differences and move files between these trees, including other commits besides HEAD. For the full range of options, check out the amazing git cheatsheet.
Look for more Git: The Good Parts posts right here, on your friendly neighborhood blogitron.