master TOC | chapter TOC | license
[Also see good version control habits for more on a related topic.]
Newcomers to git often ask why there is such a thing as the index, and what use is it. They'd rather just do
git add -A; git commiteach time to avoid thinking about the index at all, because it seems like one extra (and needless) complication.
So... what use is this, apart from confusing you with multiple names like index, staging area, and cache?
The index (or any of its other names) is essentially a "holding area" for
changes that will be committed when you next do
git commit. That is, unlike
other VCSs, a "commit" operation does not simply take the current working tree
and check it as-is into the repository. The index allows you to control what
parts of the working tree go into the repository on the next "commit"
That should be sufficient background to appreciate (in abstract terms) the following discussion.
Let's say you worked on a large-ish change, involving a lot of files and quite a few different subtasks. You didn't actually commit any of these -- you were "in the zone", as they say, and you didn't want to think about splitting up the commits the right way just then. (And you're smart enough not to make the whole thing on honking big commit!)
Now the change is all tested and working, you need to commit all this properly, in several clean commits each focused on one aspect of the code changes.
With the index, just stage each set of changes and commit until no more
changes are pending. Really works well with
git gui if you're into that
too, or you can use
git add -p or, with newer gits,
git add -e.
Staging helps you "check off" individual changes as you review a complex commit, and to concentrate on the stuff that has not yet passed your review. Let me explain.
Before you commit, you'll probably review the whole change by using
diff. If you stage each change as you review it, you'll find that you can
concentrate better on the changes that are not yet staged.
git gui is great here. It's two left panes show unstaged and staged changes
respectively, and you can move files between those two panes
(stage/unstage) just by clicking on the icon to the left of the filename.
Even better, you can even stage partial changes to a file. In the right
git gui, right click on a change that you approve of and choose
"stage hunk". Just that change (not the entire file) is now staged; in
fact, if there are other, unstaged, changes in that same file, you'll find
that the file now appears on both top and bottom left panes!
^^[Do remember, however, that if the change is really complex maybe you should split it into multiple commits!]^^
When a merge happens, changes that merge cleanly are updated both in the
staging area as well as in your work tree. Only changes that did not merge
cleanly (i.e., caused a conflict) will show up when you do a
or in the top left pane of
Again, this lets you concentrate on the stuff that needs your attention -- the merge conflicts.
Usually, files that should not be committed go into
.gitignore or the
However, sometimes you want a local change to a file that cannot be excluded (which is not good practice but can happen sometimes). For example, perhaps you upgraded your build environment and it now requires an extra flag or option for compatibility, but if you commit the change to the Makefile, the other developers will have a problem.
Of course you have to discuss with your team and work out a more permanent solution, but right now, you need that change in your working tree to do any work at all!
Another situation could be that you want a new local file that is temporary, and you don't want to bother with the ignore mechanism. This may be some test data, a log file or trace file, or a temporary shell script to automate some test... whatever.
In git, all you have to do is never to stage that file or that change. That's it.
Let's say you're in the middle of a somewhat large-ish change and you are told about a very important bug that needs to be fixed asap.
The usual recommendation is to do this on a separate branch, but let's say this fix is really just a line or two, and can be tested just as easily without affecting your current work.
With git, you can quickly make and commit only that change, without committing all the other stuff you're still working on.
Again, if you use
git gui, whatever's on the bottom left pane gets
committed, so just make sure only that change gets there and commit, then
While the first 3 use cases are perfectly legitimate and useful uses of the index, this last example could be dangerous if you get carried away. You might end up committing something that is not tested exactly, because you tested with the contents of the work tree, which is not the same as the index that is being committed.
Of course, using a powerful DVCS does not absolve you from the responsibility of thinking :-) So if you want to be 100% sure, there is a simple way to test only the staged code:
# stash away the changes to git-tracked files that you have not staged yet git stash save --keep-index # now do your tests on the exact stuff being committed make; make test # etc... # when satisfied, commmit git commit # now bring the stashed changes back to continue normal work git stash pop
If you need to understand how/why this works, ask me, otherwise just use it and be happy :-)