1

From whatever I have read about git reset --soft, I learned that it doesn't modified staging area or the index. But in the below example you can see clearly that the staging area/index is modified as a result of git reset --soft. Why?

mkdir test;cd test;git init;
touch a;
echo 1 >>a;git add a;git commit -m"1" a;
echo 2 >>a;git commit -m"2" a;

git diff --cached; <NO OUTPUT>
git reset --soft HEAD~;
git diff --cached;<See the below output>

--- a/a
+++ b/a
@@ -1 +1,2 @@
 1
+2
simplfuzz
  • 12,479
  • 24
  • 84
  • 137
  • By the way, supplying paths to the `git commit` command is very special-purpose usage, and it's redundant in your first use of it here. – jthill May 28 '16 at 22:33

3 Answers3

4

The claim that git reset --soft leaves the index/staging-area unmodified is correct.

Note that git diff always compares two things. Which two things does it compare? The answer is a bit complicated for the fully general form of git diff, but you are using git diff --cached, and it compares the two things noted in the git diff documentation`:

git diff [--options] --cached [<commit>] [--] [<path> ...]

This form is to view the changes you staged for the next commit relative to the named <commit>. Typically you would want comparison with the latest commit, so if you do not give <commit>, it defaults to HEAD. [snip]

Thus, the two things being compared are HEAD and the index.

Aside: the documentation is misleading

The above implies that the index stores changes, which is not true! The index / staging area is simply what would be in the next commit if you made that commit right now. Hence, after making a commit, the index and the current commit are identical—at least, they represent the same work-tree (the index omits significant parts of a commit, namely author and committer name+email+date, parent pointers, and commit message—or to put it a little more briefly, the index has only a work tree, and it's in an unusual form, more suited for being an index/cache than for being a commit).

Back to git reset and git diff --cached

git reset --soft modifies HEAD. It does not modify the index. You first compare HEAD against the index and see no output. Then you change HEAD. Now you compare the changed HEAD against the index, and see output.

What changed to cause the changed output? Well, HEAD changed.

You can see the same thing by omitting the git reset --soft step and providing a specific commit:

git diff --cached HEAD~

This will compare commit HEAD~ against the index, and show you the same output.

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • Index is the place which keeps the changes to be committed. I had nothing to be committed before reset. I added nothing to be committed after reset. Then how were the changes added to index? – simplfuzz May 28 '16 at 21:49
  • The index does not store changes. It stores *the next commit you can make*. When the comparison between `HEAD` and "the next commit" is empty, there is nothing new to commit. Changing `HEAD` changes the difference between `HEAD` and "the next commit" but does not change "the next commit" itself. I'll edit this post to point out the flaw in the `git diff` documentation, which implies the wrong thing. – torek May 28 '16 at 22:01
3

A commit is of particular content. The index indexes particular content. git diff --cached compares indexed content against the named commit. When you reset HEAD, you changed the commit git diff --cached compares against.

jthill
  • 55,082
  • 5
  • 77
  • 137
2

Documentation on git index is a bit misleading. The matter of fact is that git index is very much like commit, but without things like commit timestamp or email of person who made the commit. Git index contains certain state of your files. When you do git checkout master or make new commit git index contains references to the same files/blobs that are in the commit you just checked out or made. If you try to do git commit now nothing will happen because git will see that git index references the same files as last commit and therefore there is nothing to commit. When index differs from commit HEAD points to at the moment then new commit can happen recording whatever is in the index in that new commit. After the second commit your repository looks like this

      index
      |
#1 -> #2
      ^
      master
      ^
      HEAD

When you do git reset --soft HEAD~ then your repo looks like this

      index
      |
#1 -> #2
^     ^
|     master
|
HEAD

Note that your local file also appears as in commit #2 and therefore git diff should show you nothing. But index is now different from commit that HEAD points to. Hence git diff --cached shows you effective difference between commit #1 and #2.

Dmitri
  • 114
  • 5