In order to undo just the staging of contents, we need another command, the one git status suggested a moment ago: git reset. Just like checkout, reset can accept paths that are actually globs. Using it that way tells the index to lose its current snapshots for the requested paths. You can also omit any path to unstage everything.

This is the only scenario when reset works on specific files, and it might as well be named “unindex” or “unstage”. This would help us differentiate between this specific use-case and other reset scenarios we’ll explore later.

Finally, let’s mention another particular use-case: the "root commit,” that is when we’re just starting with our project and don’t have a single commit in our history yet. Should we need to unstage stuff then, Git lacks a reference point in its database, as no commit exists yet. The reset command is therefore blocked and we’ll need another mechanism to tell Git simply to remove the snapshot from the index. This is what git rm does with its --cached option.

By the way, in Git, options --cached or --staged are always about the index, as the name for the index varied over time and was at times known as the “cache,” the “stage” or the “staging area.” You still frequently stumble upon these legacy names for it in command-line options or command documentations, as is the case here with git rm --cached.

If we come back to our earlier animated example, then modify a file and stage it, we see that the action of a reset on that file overwrites the version known to the index by re-using the one from the local repo.

(Example 03-unstage)

In our terminal, git status tells us our modified files were staged. As we noticed earlier, git status tells us what to do: “use "git reset HEAD <file>..." to unstage” We’ll follow that advice and unstage two files: git reset HEAD file-1 sub-dir/file-3. Let’s verify again with a new git status: our two files did come back to modified state in the working copy, so they show up red in my terminal here.

However, when the file doesn’t exist in the latest known version in the local repo, it is regarded as a creation, so once staged, reset cannot be used. Just as with a “root commit,” we’ll need to use git rm --cached to unstage it but still leave it in the working copy.

(Example 04-rm-cached)

Let’s get back to our terminal. We’re in a fresh example project, brand-new and with no existing history, as an attempt at git log tells us. Our usual git status tells us a file with the most original name my-file is there and was staged. As always, the procedure to unstage it is described right in front of us: “use "git rm --cached <file>..." to unstage.” Let’s do so, and git status confirms that our file is still in the working copy, but shows up as “untracked.”