What is the practical difference between `git rm --cached`, `git reset --` and `git reset HEAD` to unstage changes?

git reset is the proper way to onstage, now here's why:

First let's clarify a few things: If no commit is provided git defaults commit to HEAD

git reset == git reset HEAD

As for the -- it expects a path after it.

Now, for the difference between:

git reset --mixed AND git rm --cached

(reset defaults to --mixed)

git reset --mixed
           Resets the index but not the working tree (i.e., the changed files are preserved but not marked for
           commit) and reports what has not been updated. This is the default action.

git rm --cached
       Use this option to unstage and remove paths only from the index. Working tree files, whether modified or
       not, will be left alone.enter code here

Essentially the difference being here, git rm --cached actually puts specified files into untracked, and stages their removal for the next commit. Git reset --mixed, on the other hand, simply moves the file into 'unstaged', but keeps its old version in repo.


The difference (not considering things like confusing pathnames with ref names) is like this:

  • git reset [<tree-ish>] path [path...] would take the value of <tree-ish> (which defaults to HEAD if omitted) and make sure the index entry for the specified path looks exactly like it's recorded in that <tree-ish>, that is:

    • If it has contents recorded as blob a398bc837d, the index entry will refer to that blob as well.
    • If no entry by that name exists, it will be deleted from the index.
  • git rm --cached path [path ...], on the other hand, removes and entry matching path from the index.

As you can see, the difference manifests itself in this case:

  1. Your HEAD already contains an entry for the file foo.txt.
  2. You git add a change to foo.txt from the work tree.
  3. Now

    • git reset [HEAD] foo.txt would revert the contents of foo.txt recorded in the index to the contents this file has in the HEAD.
    • git rm --cached foo.txt would remove the entry for the foo.txt from the index.

As you can see, should you now commit, the contents of the next commit will either have or not have foo.txt -- depending on what you've done.

To sum this up, unstaging is only done via git reset HEAD — it's an opposite to git add.

To put it in another perspective:

  • git add pathname adds changes the pathname contains in the work tree compared to its state in the index. If pathname does not exist in the index at all, an entry for it is first created. Do you see the fine difference?
  • Being reverse to git add, git reset HEAD would remove the change added by git add: if you git add-ed a change over an already existing index entry, its original contents will be restored in the index. If it did not exist in the index at all, it will be removed.

    Conversely to this, git rm --cached removes an entry unconditionally. So it "cancels" a git add operation only in the case where git add created that index entry you're trying nuke using git rm --cached.


I'm going to answer what is the difference between git rm --staged <file> and git reset HEAD <file>

Both can be used when a person wants to remove a file from the staging area.

With git rm --staged, a person can remove the file for the particular commit where he doesn't want the changes to come. And then after the commit git add it and commit it again.

With git reset, a person can remove the changes to the file for the particular commit where he doesn't want his changes to come. And then after the commit git add it and commit it again.

But both are very different in terms of what actually happens.

With git rm --staged some-file, the file gets removed from the staging area and is not tracked anymore. The file will still be in the working area. Commit the changes with the file being removed with git rm --staged. Add the file with git add and commit the changes again. Now go back to where the file was removed, you'll notice that the file doesn't show up at all in the directory. It's missing. This can cause problems if other code depends on this file.

With git reset HEAD some-file, the changes done to the file since the last commit get removed from the staging area but the file is still being tracked. So when you go back to the commit where you ran git reset HEAD some-file.txt, the file will be still there but the changes will not be there.

For unstaging a file, git reset HEAD <file> should be used.

Tags:

Git