Besides Obsidian's answer, which gets into what you can do with the index / staging area (two phrases for the same underlying Git thing). it is possible to build a version control system that does not have an index / staging-area in the first place.
Mercurial is such a system. In Mercurial, the work-tree is the proposed next commit. This does require a few extra tricks behind the scenes, but the system works fine, and newcomers to Mercurial have far less trouble with it, than newcomers to Git have with Git. It's pretty clear from this that the index / staging-area is a tough concept. It isn't strictly necessary—though it does enable various tricks—and a system that doesn't have it is easier to use.
But Git does have it, so if you're using Git, remember that Git keeps three copies1 of each file at all times: the frozen one in HEAD
(the current commit), the staged one in the index, and the one you can see and edit in the work-tree. Use git add
to copy from the work-tree into the index. Use git reset
to copy from the frozen HEAD
copy into the index. Use git restore
, if you have Git 2.23 or later, to copy from the index into the work-tree.
(If your Git version is older than that, there's a git checkout
mode that copies from index to work-tree. You can't copy into the HEAD
copy as it's frozen! So the only options are: HEAD
-> index, HEAD
-> index -> work-tree, index -> work-tree, and work-tree -> index.)
Side note: the fancy git add -p
and git reset -p
do their work by extracting the index copy to a temporary file, comparing the temporary and work-tree copies, and making whatever changes you like—and then for git add -p
, putting the updated temporary copy back into the index.
1Technically, all the Git-ified (compressed, frozen-format) copies are shared to every possible extent. What's in the index is the hash ID of a blob object. When you update the index from the work-tree copy, Git prepares a new-or-reused blob object, ready to be committed, and updates the index hash ID. Aside from the fact that there's no extra disk space used, this is generally not visible in doing ordinary work with Git. You can think of the index copy as a private copy, as long as you remember that git ls-files --stage
and git update-index
work with blob hashes in reality.
Note that even if you never commit some blob, Git keeps the blob around as long as it's still in the index ... except for a rather nasty bug with added work-trees, starting in Git 2.5 and fixed in Git 2.15. Meanwhile, using git add -p
can create a lot of unused blob objects, which are eventually garbage-collected, but do use up some extra disk space until then.