← All EduBytes

Git's staging area is not a draft folder

1 September 2026 · 4 min read

The index is not a list of files you have marked for later. It is a complete snapshot of your next commit, and once you see that, most of Git's confusing behaviour stops being confusing.

Almost everyone hits this once, early, and files it under “Git is weird”:

git add report.py
# ...notice a typo, fix it...
git commit -m "Add report"

and the commit contains the version with the typo. The fix you made is still sitting in the working tree, unstaged, as though git add had not noticed it.

That behaviour is not a quirk. It follows directly from what the staging area actually is, and the name is most of why people get it wrong.

It is not a list of files. It is a snapshot.

The common mental model is that git add marks a file for inclusion — that staging is a checklist, and committing collects everything ticked.

What actually happens is that git add copies the current content of the file into the index. The index is not a list of names. It is a complete tree: a full description of what the next commit’s contents will be, filename by filename, content and all.

So git add report.py did not tick a box next to report.py. It took a copy of the file as it stood at that moment. Editing the file afterwards changes the working tree and leaves that copy exactly where it was. The commit uses the copy, because the copy is the proposed commit.

Once you have that sentence, most of Git’s confusing surface resolves at once.

The three trees

Git is nearly always juggling three versions of your project:

git status is not one report, it is two comparisons, and the headings say so once you know to read them that way. “Changes to be committed” is HEAD versus the index. “Changes not staged for commit” is the index versus the working tree.

That is why a file can appear in both lists at the same time — which looks like a contradiction under the checklist model and is perfectly ordinary under this one. It means: the index differs from the last commit, and the disk differs from the index. Exactly what happens when you stage a file and then edit it again.

What stops being arbitrary

git diff versus git diff --staged. The first compares the index to the working tree — what you have changed but not staged. The second compares HEAD to the index — what you are about to commit. Two comparisons, two commands, and neither is the default because neither is more fundamental.

The three git reset modes. They differ in how many trees they move. --soft moves HEAD alone, leaving your index and files untouched — which is why it is the tool for redoing a commit message or squashing. --mixed, the default, moves HEAD and resets the index, leaving your files alone. --hard moves all three, which is why it is the one that destroys work.

git add -p. You can stage some hunks of a file and not others, producing a proposed commit that never existed on disk at any moment. This is a genuinely useful thing to be able to do, and it is only coherent because the index holds content rather than filenames. It also carries an obligation nobody mentions: that commit has never been tested, because that state has never been on your disk. git stash --keep-index is the tool for checking it before you push.

Why the index exists at all

It is worth knowing that it was not designed as a user feature.

The index is also Git’s working area during a merge — conflicted paths get recorded there in multiple stages, which is what git status reads to tell you what is unresolved. And it caches filesystem metadata for every tracked file, which is why git status is fast on a large repository instead of hashing everything on every invocation.

The “compose your commit deliberately” workflow is a consequence of a data structure that had to exist anyway. Which explains something about the ergonomics: the index is not an interface designed for humans and then implemented. It is an implementation detail that turned out to be useful and got a command surface bolted on.

The name is the bug

“Staging area” suggests a queue — things lined up, waiting to go. “Index” suggests a lookup table, which is worse.

The accurate name is the proposed next commit. Say that to yourself instead, and the typo case stops being surprising: of course editing a file after proposing a commit does not change the proposal. You would have to propose again.

Which is exactly what git add does the second time.