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 toHEAD
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.
- If it has contents recorded as blob
git rm --cached path [path ...]
, on the other hand, removes and entry matchingpath
from the index.
As you can see, the difference manifests itself in this case:
- Your
HEAD
already contains an entry for the filefoo.txt
. - You
git add
a change tofoo.txt
from the work tree. Now
git reset [HEAD] foo.txt
would revert the contents offoo.txt
recorded in the index to the contents this file has in theHEAD
.git rm --cached foo.txt
would remove the entry for thefoo.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 thepathname
contains in the work tree compared to its state in the index. Ifpathname
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 bygit add
: if yougit 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" agit add
operation only in the case wheregit add
created that index entry you're trying nuke usinggit 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.