No option to ignore filemode if diff'ing not in a repository?

Viewed 62

I use git in some automated tests as an easy way to have a "diff" command available with same syntax on both Linux and Windows. It's working ok, but since the files I'm comparing are on a shared drive I've been stumbling about differences in filemode being (unwantedly) reported as changes. I looked around and found the core.filemode setting being recommended in this case - but in my experiments, the setting does not seem to be used when outside of a working directory.

This is my diff command for the test:

git diff --ignore-space-at-eol --no-index createdFile expectedFile

Output:

diff --git a/createdFile b/expectedFile
old mode 100644
new mode 100755

(and I want to ignore differences in just the file mode).

What I've tried:

git -c core.filemode=false diff --ignore-space-at-eol --no-index createdFile expectedFile

As suggested e.g. in this answer. The output is however still the same as above (git version: 2.25.1 - maybe this filemode actually doesn't work there as somehow hinted in here?) Setting core.filemode globally also doesn't work.

My current workaround is to copy the "expected" files to a location within the linux filesystem with default permissions. Would be great if this could be avoided though...

1 Answers

There is a workaround: Show git diff, ignoring file permission changes? (Zed notes that git diff -G. does the trick). Note: the dot (period) in -G. is the key here. A mode-only diff has no diff text, so . fails to match; all other diffs do have diff text, even if it's a pure delete, so . matches something, and the file gets displayed. But this is just a workaround, not a true fix.


To expand a bit on my comment above: there is indeed no way to do exactly what you wanted (other than temporarily chmod-ing the files, or copying them and chmod-ing the copies).

When you run git diff --no-index (or any diff in which --no-index is implied), Git calls lstat on each file to find out whether it's a regular file or directory and what its mode is. This call is made from diff-no-index.c around line 43, whose last few lines include:

else
        *mode = st.st_mode;

This assumes that the OS, whatever it is, produces consistent st_mode executable bits, even if they cannot be trusted.

To make this obey the core.filemode setting, the last line might need to read, e.g.:

        *mode = !S_IFREG(st.st_mode) || trust_executable_bit ?
            st.st_mode : st.st_mode & ~0111;

for instance. This could perhaps be simplified: I don't think Git cares about the x bits on directories so just always masking away 0111 might suffice, when trust_executable_bit is false.

Related