Sometimes git suggests git rm --cached
to unstage a file, sometimes git reset HEAD file
. When should I use which?
EDIT:
D:\
git rm --cached
is used to remove a file from the index. In the case where the file is already in the repo, git rm --cached
will remove the file from the index, leaving it in the working directory and a commit will now remove it from the repo as well. Basically, after the commit, you would have unversioned the file and kept a local copy.
git reset HEAD file
( which by default is using the --mixed
flag) is different in that in the case where the file is already in the repo, it replaces the index version of the file with the one from repo (HEAD), effectively unstaging the modifications to it.
In the case of unversioned file, it is going to unstage the entire file as the file was not there in the HEAD. In this aspect git reset HEAD file
and git rm --cached
are same, but they are not same ( as explained in the case of files already in the repo)
To the question of Why are there 2 ways to unstage a file in git?
- there is never really only one way to do anything in git. that is the beauty of it :)
if you've accidentally staged files that you would not like to commit, and want to be certain you keep the changes, you can also use:
git stash
git stash pop
this performs a reset to HEAD and re-applies your changes, allowing you to re-stage individual files for commit. this is also helpful if you've forgotten to create a feature branch for pull requests (git stash ; git checkout -b <feature> ; git stash pop
).
For versions 2.23 and above only,
Instead of these suggestions, you could use
git restore --staged <file>
in order to unstage
the file(s).
Let's say you stage
a whole directory via git add <folder>
, but you want to exclude a file from the staged list (i.e. the list that generates when running git status
) and keep the modifications within the excluded file (you were working on something and it's not ready for commit, but you don't want to lose your work...). You could simply use:
git reset <file>
When you run git status
, you will see that whatever file(s) you reset
are unstaged
and the rest of the files you added
are still in the staged
list.
Quite simply:
git rm --cached <file>
makes git stop tracking the file completely (leaving it in the filesystem, unlike plain git rm
*)git reset HEAD <file>
unstages any modifications made to the file since the last commit (but doesn't revert them in the filesystem, contrary to what the command name might suggest**). The file remains under revision control.If the file wasn't in revision control before (i.e. you're unstaging a file that you had just git add
ed for the first time), then the two commands have the same effect, hence the appearance of these being "two ways of doing something".
* Keep in mind the caveat @DrewT mentions in his answer, regarding git rm --cached
of a file that was previously committed to the repository. In the context of this question, of a file just added and not committed yet, there's nothing to worry about.
** I was scared for an embarrassingly long time to use the git reset command because of its name -- and still today I often look up the syntax to make sure I don't screw up. (update: I finally took the time to summarize the usage of git reset in a tldr page, so now I have a better mental model of how it works, and a quick reference for when I forget some detail.)
This thread is a bit old, but I still want to add a little demonstration since it is still not an intuitive problem:
me$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: to-be-added
# modified: to-be-modified
# deleted: to-be-removed
#
me$ git reset -q HEAD to-be-added
# ok
me$ git reset -q HEAD to-be-modified
# ok
me$ git reset -q HEAD to-be-removed
# ok
# or alternatively:
me$ git reset -q HEAD to-be-added to-be-removed to-be-modified
# ok
me$ git status
# On branch master
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: to-be-modified
# deleted: to-be-removed
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# to-be-added
no changes added to commit (use "git add" and/or "git commit -a")
git reset HEAD
(without -q
) gives a warning about the modified file and its exit code is 1 which will be considered as an error in a script.
Edit: git checkout HEAD to-be-modified to-be-removed
also works for unstaging, but removes the change completely from the workspace
Update git 2.23.0: From time to time, the commands change. Now, git status
says:
(use "git restore --staged <file>..." to unstage)
... which works for all three types of change