Windows: Skip fstat/lstat optimization in write_entry()
authorJohannes Sixt <j6t@kdbg.org>
Mon, 20 Apr 2009 08:17:00 +0000 (10:17 +0200)
committerJunio C Hamano <gitster@pobox.com>
Mon, 20 Apr 2009 19:14:02 +0000 (12:14 -0700)
Commit e4c72923 (write_entry(): use fstat() instead of lstat() when file
is open, 2009-02-09) introduced an optimization of write_entry().
Unfortunately, we cannot take advantage of this optimization on Windows
because there is no guarantee that the time stamps are updated before the
file is closed:

"The only guarantee about a file timestamp is that the file time is
correctly reflected when the handle that makes the change is closed."

(http://msdn.microsoft.com/en-us/library/ms724290(VS.85).aspx)

The failure of this optimization on Windows can be observed most easily by
running a 'git checkout' that has to update several large files. In this
case, 'git checkout' will report modified files, but infact only the
timestamps were incorrectly recorded in the index, as can be verified by a
subsequent 'git diff', which shows no change.

Dmitry Potapov reports the same fix needs on Cygwin; this commit contains
his updates for that.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Makefile
entry.c
git-compat-util.h
index 6e0838b03ad1e00a7c3eec730d12f02b7b2a1162..49f36f578733932ea654bbda7af49e72271ac60f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -167,6 +167,10 @@ all::
 # Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call
 # your external grep (e.g., if your system lacks grep, if its grep is
 # broken, or spawning external process is slower than built-in grep git has).
+#
+# Define UNRELIABLE_FSTAT if your system's fstat does not return the same
+# information on a not yet closed file that lstat would return for the same
+# file after it was closed.
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -803,6 +807,7 @@ ifeq ($(uname_S),HP-UX)
 endif
 ifneq (,$(findstring CYGWIN,$(uname_S)))
        COMPAT_OBJS += compat/cygwin.o
+       UNRELIABLE_FSTAT = UnfortunatelyYes
 endif
 ifneq (,$(findstring MINGW,$(uname_S)))
        NO_PREAD = YesPlease
@@ -829,6 +834,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        NO_NSEC = YesPlease
        USE_WIN32_MMAP = YesPlease
+       UNRELIABLE_FSTAT = UnfortunatelyYes
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
        COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
@@ -1107,6 +1113,9 @@ endif
 ifdef NO_EXTERNAL_GREP
        BASIC_CFLAGS += -DNO_EXTERNAL_GREP
 endif
+ifdef UNRELIABLE_FSTAT
+       BASIC_CFLAGS += -DUNRELIABLE_FSTAT
+endif
 
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
diff --git a/entry.c b/entry.c
index 5daacc2fe51eada819bedea05f958fbf87f5b889..915514aa5c43d3fd39f077a79f63ac27b841d255 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -147,7 +147,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
 
                wrote = write_in_full(fd, new, size);
                /* use fstat() only when path == ce->name */
-               if (state->refresh_cache && !to_tempfile && !state->base_dir_len) {
+               if (fstat_is_reliable() &&
+                   state->refresh_cache && !to_tempfile && !state->base_dir_len) {
                        fstat(fd, &st);
                        fstat_done = 1;
                }
index f09f244061ab9be5905b69e701faef8f416fd53e..785aa31b46c4781b5003aa60214b42d122310e31 100644 (file)
@@ -408,4 +408,10 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #endif
 #endif
 
+#ifdef UNRELIABLE_FSTAT
+#define fstat_is_reliable() 0
+#else
+#define fstat_is_reliable() 1
+#endif
+
 #endif