181

GitRef.org - Basic:

git rm will remove entries from the staging area. This is a bit different from git reset HEAD which "unstages" files. By "unstage" I mean it reverts the staging area to what was there before we started modifying things. git rm on the other hand just kicks the file off the stage entirely, so that it's not included in the next commit snapshot, thereby effectively deleting it.

By default, a git rm file will remove the file from the staging area entirely and also off your disk > (the working directory). To leave the file in the working directory, you can use git rm --cached.

But what exactly is the difference between git rm --cached asd and git reset head -- asd?

Jeremy
  • 1
  • 85
  • 340
  • 366
Pacerier
  • 86,231
  • 106
  • 366
  • 634

3 Answers3

242

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 possibly updates the index (resetting it to the tree of <commit>) and the working tree depending on <mode>, which must be one of the following:
--soft

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

--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.

--hard

Resets the index and working tree. Any changes to tracked files in the 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.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
manojlds
  • 290,304
  • 63
  • 469
  • 417
  • I notice that after `git rm --cached` the `git diff` command doesn't show any diff, but `git diff --cached` shows the diff, as if it is still cached. The `git status` however shows the file as being `Untracked`. Seems kind of inconsistent. – haridsv Nov 06 '11 at 19:15
  • 8
    Never mind... I should have used `git reset --mixed`. I was a little confused by the statement that `git rm --cached` is the opposite of `git add`. Taken literally, it is incorrect and could cause damage. In my case, I used `git add` to add a modified file to the staging area and wanted the opposite of "that add" not the initial add of the file. +Greg Hewgill's answer helped me get a clearer picture. – haridsv Nov 06 '11 at 19:19
  • 12
    I find the use of working copy, tree, and working tree a little confusing. Is the working tree the working copy, or tree ? – Nealv Sep 23 '13 at 09:00
  • 4
    As @haridsv mentioned, saying `git rm --cached` 'is the exact opposite of `git add file`' is misleading. `git reset file` is closer to being the opposite of `git add file`. – Matt Browne Dec 14 '17 at 15:05
  • @Nealv belated, but for others who find this thread: working copy, tree, and working tree all refer to the same thing (in the context of git). – De Novo Feb 14 '19 at 17:05
  • @DeNovo I think `working copy` is same as `working tree`, but different from `tree`. If you check the first line the author mentions three things -- where `tree` is being distinguished from `working copy`. – Yash Kant Jul 09 '19 at 08:17
  • It's crazy these terms get mixed up - so confusing - I've seen at least 4 variations on Working Tree. – Snowcrash Oct 08 '19 at 15:56
98

Perhaps an example will help:

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

versus

git reset HEAD -- asd
git 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.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 3
    Can you tell me what does that double hyphen -- after HEAD actually means? – yuva Nov 06 '14 at 09:22
  • 36
    @yuva: The `--` is used to separate command options from file names. If there were both a *branch* and a *file* named `asd`, then `git reset HEAD asd` would be ambiguous. The `--` says "everything following this is a file name". – Greg Hewgill Nov 06 '14 at 15:55
  • Is `git reset HEAD ` exactly the same as `git rm --cached ` and then `git add --intent-to-add `? – alcohol is evil Jul 28 '17 at 23:29
  • 2
    @alcoholisevil no, except in a special case. See [this](https://stackoverflow.com/a/5798963/7936744) excellent, succinct answer. – De Novo Feb 14 '19 at 17:05
  • Using "**git rm --cached asd**" the asd won't go anywhere. This command unstages the "asd" while it remains in the working directory. I did both of them. They unstage the files in the staging area. I still don't understand the difference between them. – Fox Aug 26 '23 at 12:02
54

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.

yuriks
  • 1,084
  • 10
  • 13
  • 8
    In conjunction with the notion (as mentioned in other answers) that `git rm --cached file` is kinda the opposite of `git add`, this answer made a lot of sense to me, and was pretty succinct. Almost as short as this comment ;) – rbatt Dec 05 '15 at 01:37
  • 3
    @rbatt just to put the comment here as well, and clarify, **`git rm --cached file` is not the opposite of `git add file`**. The behavior happens to be the opposite of `git add file` in the specific case where you have added a new, previously untracked file. In every other case the opposite of `git add file` is `git reset HEAD file`. **`git reset HEAD file` also reverses `git add file` in the first case (adding an untracked file), and in every case, which is why it's what git suggests to do if you want to reverse a git add.** – De Novo Feb 14 '19 at 17:02
  • There is something wrong with your answer, if the change is an 'add file', then using `git reset HEAD file` will not have any effect, you will only get `fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree.` – Andy Jun 03 '21 at 11:24
  • I grasped everything with this answer rather than the accepted answer which was informative – Ali Dec 30 '21 at 02:49