Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the practical difference between `git rm --cached`, `git reset --` and `git reset HEAD` to unstage changes?

Tags:

git

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?

like image 885
Schwern Avatar asked Oct 07 '15 23:10

Schwern


2 Answers

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:

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

  1. Your HEAD already contains an entry for the file foo.txt.
  2. You git add a change to foo.txt from the work tree.
  3. 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.

like image 156
kostix Avatar answered Oct 22 '22 23:10

kostix


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.

like image 24
Tomasz Kaminski Avatar answered Oct 22 '22 21:10

Tomasz Kaminski