"git rm --cached x" vs "git reset head -- x"? "git rm --cached x" vs "git reset head -- x"? git git

"git rm --cached x" vs "git reset head -- x"?


There are three places where a file, say, can be - the (committed) tree, the index and the working copy. When you just add a file to a folder, you are adding it to the working copy.

When you do something like git add file you add it to the index. And when you commit it, you add it to the tree as well.

It will probably help you to know the three more common flags in git reset:

git reset [--<mode>] [<commit>]

This form resets the current branch head to <commit> and possiblyupdates the index (resetting it to the tree of <commit>) and theworking tree depending on <mode>, which must be one of thefollowing:
--soft

Does not touch the index file nor the working tree at all (but resetsthe head to <commit>, just like all modes do). This leaves all yourchanged files "Changes to be committed", as git status would put it.

--mixed

Resets the index but not the working tree (i.e., the changed filesare preserved but not marked for commit) and reports what has not beenupdated. This is the default action.

--hard

Resets the index and working tree. Any changes to tracked files inthe working tree since <commit> are discarded.

Now, when you do something like git reset HEAD, what you are actually doing is git reset HEAD --mixed and it will "reset" the index to the state it was before you started adding files / adding modifications to the index (via git add). In this case, no matter what the state of the working copy was, you didn't change it a single bit, but you changed the index in such a way that is now in sync with the HEAD of the tree. Whether git add was used to stage a previously committed but changed file, or to add a new (previously untracked) file, git reset HEAD is the exact opposite of git add.

git rm, on the other hand, removes a file from the working directory and the index, and when you commit, the file is removed from the tree as well. git rm --cached, however, removes the file from the index alone and keeps it in your working copy. In this case, if the file was previously committed, then you made the index to be different from the HEAD of the tree and the working copy, so that the HEAD now has the previously committed version of the file, the index has no file at all, and the working copy has the last modification of it. A commit now will sync the index and the tree, and the file will be removed from the tree (leaving it untracked in the working copy). When git add was used to add a new (previously untracked) file, then git rm --cached is the exact opposite of git add (and is pretty much identical to git reset HEAD).

Git 2.25 introduced a new command for these cases, git restore, but as of Git 2.28 it is described as “experimental” in the man page, in the sense that the behavior may change.


Perhaps an example will help:

git rm --cached asdgit commit -m "the file asd is gone from the repository"

versus

git reset HEAD -- asdgit commit -m "the file asd remains in the repository"

Note that if you haven't changed anything else, the second commit won't actually do anything.


git rm --cached file will remove the file from the stage. That is, when you commit the file will be removed. git reset HEAD -- file will simply reset file in the staging area to the state where it was on the HEAD commit, i.e. will undo any changes you did to it since last commiting. If that change happens to be newly adding the file, then they will be equivalent.