git diff --quiet on dirty tree sometimes erroneously exits with status 0

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I'm seeing a strange problem where "git diff --quiet" sometimes returns an
exit code of zero even though the tree is dirty and other invocations of
"git diff --quiet" in the same directory return an exit code of 1. I'm
using git v1.8.2 from Debian unstable but I've also seen the problem when
running v1.8.2 built from source and using older versions from Debian. The
repository in question is a submodule.

Unfortunately this bad behaviour only happens deep inside a variable
expansion under OpenEmbedded's bitbake so investigation is not
straightforward. It's possible that when the bad behaviour occurs other
"git diff" and similar informational commands are being run on the same
repository in parallel. I have been unable to provoke the problem by more
conventional means. :( I can reproduce it on a separate repository on the
same machine though.

Using "git diff --exit-code --no-ext-diff" instead seems to avoid the
problem but unfortunately so does adding a sleep before calling "git diff
--quiet" so that result isn't necessarily significant.

I have been able to strace the problematic case. Diffing the straces and
ignoring unrelated differences such as addresses yields  (+ =
diff returns 0, - = diff returns 1):

 [...lots of omitted stuff...]
 open("/home/mac/src/oe/sources/linux/.gitmodules", O_RDONLY) = -1 ENOENT
 (No such file or directory)
 access("/etc/gitconfig", R_OK)          = -1 ENOENT (No such file or
 directory)
 access("/home/mac/.config/git/config", R_OK) = -1 ENOENT (No such file or
 directory)
 access("/home/mac/.gitconfig", R_OK)    = 0
 open("/home/mac/.gitconfig", O_RDONLY)  = 20
 fstat(20, {st_mode=S_IFREG|0644, st_size=424, ...}) = 0
0x7f22bfa18000
 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x7f79414f1000
 read(20, "[color]\n\tui = true\n[core]\n#\tpage"..., 4096) = 424
 read(20, "", 4096)                      = 0
 close(20)                               = 0
 munmap(0x7f79414f1000, 4096)            = 0
 access("/home/mac/src/oe/.git/modules/sources/linux/config", R_OK) = 0
 open("/home/mac/src/oe/.git/modules/sources/linux/config", O_RDONLY) = 20
 fstat(20, {st_mode=S_IFREG|0644, st_size=1467, ...}) = 0
 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x7f79414f1000
 read(20, "[core]\n\trepositoryformatversion "..., 4096) = 1467
 read(20, "", 4096)                      = 0
 close(20)                               = 0
 munmap(0x7f79414f1000, 4096)            = 0
 chdir("/home/mac/src/oe/sources/linux") = 0
 lstat(".gitignore", {st_mode=S_IFREG|0644, st_size=955, ...}) = 0

At this point the diff that erroneously returned zero went off and did:

+lstat(".gitignore", {st_mode=S_IFREG|0644, st_size=955, ...}) = 0
+open("/home/mac/src/oe/.git/modules/sources/linux/objects/pack",
O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 20
+getdents(20, /* 4 entries */, 32768)    = 192
+access("/home/mac/src/oe/.git/modules/sources/linux/objects/pack/pack-2711d98bc06e8caa9c8d8e10ee40e645e6d05f18.keep",
F_OK) = -1 ENOENT (No such file or directory)
+stat("/home/mac/src/oe/.git/modules/sources/linux/objects/pack/pack-2711d98bc06e8caa9c8d8e10ee40e645e6d05f18.pack",
{st_mode=S_IFREG|0444, st_size=148768254, ...}) = 0
+getdents(20, /* 0 entries */, 32768)    = 0
+close(20)                               = 0

..and spent a while re-reading config files before continuing to recurse
over all the files but whilst the diff that provides the correct result
merely lstats each file this one reads them too:

 lstat("COPYING", {st_mode=S_IFREG|0644, st_size=18693, ...}) = 0
+open("COPYING", O_RDONLY)               = 21
+read(21, "\n   NOTE! This copyright does *n"..., 18693) = 18693
+close(21)                               = 0

When it comes across the repository file that has been changed the
behaviour of the invocation that provides the correct result is rather
different:

 lstat("drivers/net/bcmgenet/bcmgenet.c", {st_mode=S_IFREG|0644,
 st_size=114290, ...}) = 0
-lstat("drivers/net/bcmgenet/bcmgenet.c", {st_mode=S_IFREG|0644,
st_size=114290, ...}) = 0
-open("/home/mac/src/oe/.git/modules/sources/linux/objects/pack",
O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 20
-getdents(20, /* 4 entries */, 32768)    = 192
-access("/home/mac/src/oe/.git/modules/sources/linux/objects/pack/pack-2711d98bc06e8caa9c8d8e10ee40e645e6d05f18.keep",
F_OK) = -1 ENOENT (No such file or directory)
-stat("/home/mac/src/oe/.git/modules/sources/linux/objects/pack/pack-2711d98bc06e8caa9c8d8e10ee40e645e6d05f18.pack",
{st_mode=S_IFREG|0444, st_size=148768254, ...}) = 0
-getdents(20, /* 0 entries */, 32768)    = 0
-close(20)                               = 0
-open("/home/mac/src/oe/.git/modules/sources/linux/objects/info/alternates",
O_RDONLY|O_NOATIME) = -1 ENOENT (No such file or directory)
-open("/home/mac/src/oe/.git/modules/sources/linux/objects/pack/pack-2711d98bc06e8caa9c8d8e10ee40e645e6d05f18.idx",
O_RDONLY|O_NOATIME) = 20
-fstat(20, {st_mode=S_IFREG|0444, st_size=1945504, ...}) = 0
-mmap(NULL, 1945504, PROT_READ, MAP_PRIVATE, 20, 0) = 0x7f22bea11000
-close(20)                               = 0
-getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
-open("/home/mac/src/oe/.git/modules/sources/linux/objects/pack/pack-2711d98bc06e8caa9c8d8e10ee40e645e6d05f18.pack",
O_RDONLY|O_NOATIME) = 20
-fstat(20, {st_mode=S_IFREG|0444, st_size=148768254, ...}) = 0
-fcntl(20, F_GETFD)                      = 0
-fcntl(20, F_SETFD, FD_CLOEXEC)          = 0
-read(20, "PACK\0\0\0\2\0\1\17D", 12)    = 12
-lseek(20, 148768234, SEEK_SET)          = 148768234
-read(20, "\226~C\235\340\255\221\354e't>\325p\340M\307\314j@", 20) = 20
-mmap(NULL, 148768254, PROT_READ, MAP_PRIVATE, 20, 0) = 0x7f22b5c30000

and at that point the diff that provides the correct exit status basically
gives up since it has found a change. The diff that provides the incorrect
exit status _does_not_ read this file that has changed and any other files
that have changed even though it reads all the unchanged files.

At the end the diff that provides the incorrect exit status appears to
rewrite the index and then:

+rename("/home/mac/src/oe/.git/modules/sources/linux/index.lock",
"/home/mac/src/oe/.git/modules/sources/linux/index") = 0
+fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 8), ...}) = 0
+close(1)                                = 0
+exit_group(0)                           = ?

I'm afraid that I don't know enough about Git's internals to understand why
it decided to look at the pack files and rebuild the index but it seems
that if this happens the exit status is correct. :(

(Full straces available on request by private email if required.)

Mike.
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]