Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does git diff-index HEAD result change for touched files after git diff or git status?

Tags:

git

If I touch a file tracked in a git repo, and run git diff-index HEAD, it will print output with M indicating the file has been modified. For example,

$ touch foo
$ git diff-index HEAD
:100644 100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0000000000000000000000000000000000000000 M  foo

I am unsure if this makes sense or not, but that is not the question. The question is, why does the output change (to no diff) if I run git diff HEAD or git status?

$ touch foo
$ git diff-index HEAD
:100644 100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0000000000000000000000000000000000000000 M  foo
$ git diff                  # no output
$ git diff-index HEAD       # no output

I would expect the result, whatever it is, to stay the same across commands that are not supposed to change anything.

like image 897
Jani Avatar asked Jan 15 '16 09:01

Jani


1 Answers

Let’s first take a look at what the output means:

:100644 100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0000000000000000000000000000000000000000 M  foo

The manual says the following, from left to right:

  1. a colon.
  2. mode for "src"; 000000 if creation or unmerged.
  3. a space.
  4. mode for "dst"; 000000 if deletion or unmerged.
  5. a space.
  6. sha1 for "src"; 0{40} if creation or unmerged.
  7. a space.
  8. sha1 for "dst"; 0{40} if creation, unmerged or "look at work tree".
  9. a space.
  10. status, followed by optional "score" number.
  11. a tab or a NUL when -z option is used.
  12. path for "src"
  13. a tab or a NUL when -z option is used; only exists for C or R.
  14. path for "dst"; only exists for C or R.
  15. an LF or a NUL when -z option is used, to terminate the record.

Interesting is the 8th point:

sha1 for "dst"; 0{40} if creation, unmerged or "look at work tree".

So in your case, you get 40 zeros, so that either means “creation”, “unmerged” or “look at work tree”. Since you only touched the file, and it was already tracked, you can eliminate the first two options. That leaves us with “look at work tree”.

And if you do that, using git diff (which will try to generate actual content diffs for all changes and as such actually look at the file contents), then Git apparently finds out that there was no change after all, so subsequent calls no longer say anything about it.

This observation makes me believe that by default git diff-index will only take a quick look at the files, without actually comparing any content. Since you have modified the file date, Git considers it as “possibly changed” and requires a more detailed look to figure it out properly.

If you run git diff-index with an option that requires it to take a more thorough look at the files, then it will also not find any changes, e.g. when using the -p option to generate a patch (which is kind of what git diff does).

So it’s likely just a performance optimization to assume a file could be changed if the file modification date was changed, without making actual claims about it; instead it just leaves a “look at this later” marker.

like image 123
poke Avatar answered Oct 04 '22 20:10

poke