To add to Mark Adelsberger's answer and address your comment here:
I used to take commit as a snapshot but not diff content. So I expect the commit command just takes a snapshot of the index and stores it.
This is correct. However, when you use git commit --only, the way Git achieves this is complicated. (It's also not well documented.)
I normally talk about "the" index / staging-area / cache. Git does have one particular distinguished index, "the" index, although it is actually per-work-tree: if you run git worktree add, you not only get a new work-tree, but also a new index (and new HEAD, and other work-tree-specific refs such as those for git bisect). But Git is capable of working with additional temporary index files, and this is what git commit --only and git commit --include do.
Let's look at your setup again:
$ mkdir t $ cd t $ git init Initialized empty Git repository in /mnt/c/test/git-test/t/.git/ $ touch a b $ git add .
At this point, "the" index (the main one in .git/index) contains two files. Here they are:
$ git ls-files --stage 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 a 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 b
Now, however, you run git commit a -m a, creating the initial commit (a root commit, with no parents). This command—git commit --only a, more or less—works by:
- creating a new temporary index,
.git/indexdigits; - initializing that index from the current commit;1
- running the equivalent of
GIT_INDEX_FILE=.git/indexdigits add a; - running the equivalent of
cp .git/index .git/index.moredigits to create a second temporary index; - running the equivalent of
GIT_INDEX_FILE=.git/index.moredigits add a;2 - building a commit from the first temporary index, in the way
git commit normally builds a commit from the main index;3 and - finishing off the commit by renaming the second temporary index to
.git/index, so that it becomes the primary index.
What this does is:
- Create and use a temporary index for the commit that contains the
HEAD commit plus the --only files. The main index is undisturbed in case the new commit fails (though in your case it succeeds). - Create and set up a second temporary index to be used if the commit succeeds.
- Attempt the commit using the first temporary index.
If the commit succeeds, the first temporary index is discarded and the second temporary index becomes the main index (via a rename operation so that it's all atomic). If the commit fails, both temporary index files are removed.
This means that after a successful git commit --only, the main index is updated as if you had run git add on the --only files. After a failed one—the commit can fail due to pre-commit hooks, or you erasing the commit message, for instance—all is as if you had never run git commit --only at all.
(In your case, since you didn't modify the file a before running git commit --only a, you can't tell some of these cases apart.)
When you went on to run git commit --only b, these steps repeated but with file b instead of file a.
1There is no current commit, as you haven't created any yet, so this is treated as a special case: Git creates this as an empty index.
2This git add winds up having no effect, since the file named a is still empty. Had you modified the file named a in your work-tree at this point, though, it would have updated the second temporary index.
3Since Git is not using the file .git/index to build this new commit, any pre-commit hook that assumes that the index is named .git/index will do the wrong thing. Note that with added work-trees, the main index for that added work-tree has a different name as well (.git/worktrees/<name>/index, if I remember correctly offhand).
git commit -p ./fiepathcommand. see more here git-scm.com/docs/git-commit#Documentation/git-commit.txt--pgit add -pibefore commit. read the help with?after you run the command.git add -piis to choose which change chunks to be staged, not for file choosing. For example, it would show 'no changes' when you have some blank files.a, so it is still there in the second commit. What is it you are trying to achieve, and what for? I think that'll make it easier to explain this (or to realize that you're in fact correct :p)