Remember how we did the git update-index on file hello
and then
we changed hello
afterward, and could compare the new state of
hello
with the state we saved in the index file?
Further, remember how I said that git write-tree writes the
contents of the index
file to the tree, and thus what we just
committed was in fact the original
contents of the file hello
,
not the new ones. We did that on purpose, to show the difference
between the index state, and the state in the working tree, and
how they don't have to match, even when we commit things.
As before, if we do git diff-files -p
in our git-tutorial
project, we'll still see the same difference we saw last time:
the index file hasn't changed by the act of committing anything.
However, now that we have committed something, we can also learn
to use a new command: git diff-index.
Unlike git diff-files, which showed the difference between the
index file and the working tree, git diff-index shows the
differences between a committed tree
and either the index file or
the working tree. In other words, git diff-index wants a tree to
be diffed against, and before we did the commit, we couldn't do
that, because we didn't have anything to diff against.
But now we can do
$ git diff-index -p HEAD
(where -p
has the same meaning as it did in git diff-files), and
it will show us the same difference, but for a totally different
reason. Now we're comparing the working tree not against the
index file, but against the tree we just wrote. It just so
happens that those two are obviously the same, so we get the same
result.
Again, because this is a common operation, you can also just
shorthand it with
$ git diff HEAD
which ends up doing the above for you.
In other words, git diff-index normally compares a tree against
the working tree, but when given the --cached
flag, it is told to
instead compare against just the index cache contents, and ignore
the current working tree state entirely. Since we just wrote the
index file to HEAD, doing git diff-index --cached -p HEAD
should
thus return an empty set of differences, and that's exactly what
it does.
Note
git diff-index really always uses the index for its
comparisons, and saying that it compares a tree against the
working tree is thus not strictly accurate. In particular,
the list of files to compare (the "meta-data") always
comes
from the index file, regardless of whether the --cached
flag
is used or not. The --cached
flag really only determines
whether the file contents
to be compared come from the
working tree or not.
This is not hard to understand, as soon as you realize that
Git simply never knows (or cares) about files that it is not
told about explicitly. Git will never go looking
for files to
compare, it expects you to tell it what the files are, and
that's what the index is there for.
However, our next step is to commit the change
we did, and again,
to understand what's going on, keep in mind the difference
between "working tree contents", "index file" and "committed
tree". We have changes in the working tree that we want to
commit, and we always have to work through the index file, so the
first thing we need to do is to update the index cache:
$ git update-index hello
(note how we didn't need the --add
flag this time, since Git knew
about the file already).
Note what happens to the different git diff-* versions here.
After we've updated hello
in the index, git diff-files -p
now
shows no differences, but git diff-index -p HEAD
still does
show
that the current state is different from the state we committed.
In fact, now git diff-index shows the same difference whether we
use the --cached
flag or not, since now the index is coherent
with the working tree.
Now, since we've updated hello
in the index, we can commit the
new version. We could do it by writing the tree by hand again,
and committing the tree (this time we'd have to use the -p HEAD
flag to tell commit that the HEAD was the parent
of the new
commit, and that this wasn't an initial commit any more), but
you've done that once already, so let's just use the helpful
script this time:
$ git commit
which starts an editor for you to write the commit message and
tells you a bit about what you have done.
Write whatever message you want, and all the lines that start
with # will be pruned out, and the rest will be used as the
commit message for the change. If you decide you don't want to
commit anything after all at this point (you can continue to edit
things and update the index), you can just leave an empty
message. Otherwise git commit
will commit the change for you.
You've now made your first real Git commit. And if you're
interested in looking at what git commit
really does, feel free
to investigate: it's a few very simple shell scripts to generate
the helpful (?) commit message headers, and a few one-liners that
actually do the commit itself (git commit).