I have an alias, unstage
, to remove changes from the staging area.
unstage = reset --
I've noticed git's help suggests git reset HEAD
instead. I've also noticed that git rm --cached
will do the same thing.
With the exception that git rm --cached
will not clear the whole stage without -r
, is there a practical difference between these techniques to remove changes from the staging area? By practical I mean can you show a scenario where they would produce different results? Or can I safely use them interchangeably?
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:
a398bc837d
, the index entry will refer to that blob as well.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:
HEAD
already contains an entry for the file foo.txt
.git add
a change to foo.txt
from the work tree.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
.
git reset is the proper way to unstage, 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With