From e7de893ef899705f7b7e2d9894a7fda147b6d091 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Mon, 1 Jun 2015 13:43:54 -0600 Subject: [PATCH 01/10] cmake: add USE_NSEC, and only check nanosec m/ctime if enabled --- CMakeLists.txt | 5 +++++ src/diff.c | 4 ++++ src/index.c | 7 +++++-- tests/merge/workdir/dirty.c | 8 +++++++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 713640d6a..5790a03c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) +OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) OPTION( VALGRIND "Configure build for valgrind" OFF ) OPTION( CURL "User curl for HTTP if available" ON) @@ -500,6 +501,10 @@ IF (THREADSAFE) ADD_DEFINITIONS(-DGIT_THREADS) ENDIF() +IF (USE_NSEC) + ADD_DEFINITIONS(-DGIT_USE_NSEC) +ENDIF() + ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) # Collect sourcefiles diff --git a/src/diff.c b/src/diff.c index d97dcd9d2..b2259c74d 100644 --- a/src/diff.c +++ b/src/diff.c @@ -835,7 +835,11 @@ static int maybe_modified( */ } else if (git_oid_iszero(&nitem->id) && new_is_workdir) { bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0); +#ifdef GIT_USE_NSEC bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0); +#else + bool use_nanos = false; +#endif git_index *index; git_iterator_index(&index, info->new_iter); diff --git a/src/index.c b/src/index.c index 2e8934780..54a8b1eec 100644 --- a/src/index.c +++ b/src/index.c @@ -858,8 +858,11 @@ void git_index_entry__init_from_stat( { entry->ctime.seconds = (git_time_t)st->st_ctime; entry->mtime.seconds = (git_time_t)st->st_mtime; - /* entry->mtime.nanoseconds = st->st_mtimensec; */ - /* entry->ctime.nanoseconds = st->st_ctimensec; */ +#if !defined(GIT_WIN32) && !defined(__APPLE__) + /* Apple and Windows doesn't provide these struct stat fields. */ + entry->mtime.nanoseconds = st->st_mtim.tv_nsec; + entry->ctime.nanoseconds = st->st_ctim.tv_nsec; +#endif entry->dev = st->st_rdev; entry->ino = st->st_ino; entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ? diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c index 4bf984c23..ed402bd14 100644 --- a/tests/merge/workdir/dirty.c +++ b/tests/merge/workdir/dirty.c @@ -163,9 +163,15 @@ static void hack_index(char *files[]) cl_git_pass(p_stat(path.ptr, &statbuf)); entry->ctime.seconds = (git_time_t)statbuf.st_ctime; - entry->ctime.nanoseconds = 0; entry->mtime.seconds = (git_time_t)statbuf.st_mtime; +#if !defined(GIT_WIN32) && !defined(__APPLE__) + /* Apple and Windows doesn't provide these struct stat fields. */ + entry->ctime.nanoseconds = statbuf.st_ctim.tv_nsec; + entry->mtime.nanoseconds = statbuf.st_mtim.tv_nsec; +#else + entry->ctime.nanoseconds = 0; entry->mtime.nanoseconds = 0; +#endif entry->dev = statbuf.st_dev; entry->ino = statbuf.st_ino; entry->uid = statbuf.st_uid; From 0269833f92149a50f2eb522af8ef6481a4f17ac4 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Tue, 2 Jun 2015 12:42:07 -0600 Subject: [PATCH 02/10] settings: expose GIT_USE_NSEC flag in git_libgit2_features --- include/git2/common.h | 5 +++-- src/settings.c | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index d84a76512..1846e601a 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -99,8 +99,9 @@ GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev); */ typedef enum { GIT_FEATURE_THREADS = (1 << 0), - GIT_FEATURE_HTTPS = (1 << 1), - GIT_FEATURE_SSH = (1 << 2), + GIT_FEATURE_HTTPS = (1 << 1), + GIT_FEATURE_SSH = (1 << 2), + GIT_FEATURE_NSEC = (1 << 3), } git_feature_t; /** diff --git a/src/settings.c b/src/settings.c index 2097ca314..22e55953a 100644 --- a/src/settings.c +++ b/src/settings.c @@ -33,6 +33,9 @@ int git_libgit2_features() #endif #if defined(GIT_SSH) | GIT_FEATURE_SSH +#endif +#if defined(GIT_USE_NSEC) + | GIT_FEATURE_NSEC #endif ; } From 2be7855727acf829de989f92a8f97a31ec3a378a Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Tue, 2 Jun 2015 12:45:30 -0600 Subject: [PATCH 03/10] caps: add test for GIT_FEATURES_NSEC --- tests/core/features.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/core/features.c b/tests/core/features.c index 5eeb05e81..85cddfeff 100644 --- a/tests/core/features.c +++ b/tests/core/features.c @@ -28,4 +28,10 @@ void test_core_features__0(void) #else cl_assert((caps & GIT_FEATURE_SSH) == 0); #endif + +#if defined(GIT_USE_NSEC) + cl_assert((caps & GIT_FEATURE_NSEC) != 0); +#else + cl_assert((caps & GIT_FEATURE_NSEC) == 0); +#endif } From e9e6df2c8f4a8577fcbcfdf56bf255ef9774b6ec Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Mon, 15 Jun 2015 09:28:55 -0600 Subject: [PATCH 04/10] cmake: Only provide USE_NSEC if struct stat members are avilable. This allows us to remove OS checks from source code, instead relying on CMake to detect whether or not `struct stat` has the nanoseconds members we rely on. --- CMakeLists.txt | 8 +++++++- src/index.c | 3 +-- tests/merge/workdir/dirty.c | 3 +-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5790a03c9..cb1f1b8b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,9 +19,13 @@ CMAKE_POLICY(SET CMP0015 NEW) SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") INCLUDE(CheckLibraryExists) +INCLUDE(CheckStructHasMember) INCLUDE(AddCFlagIfSupported) INCLUDE(FindPkgConfig) +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h + HAVE_STRUCT_STAT_NSEC LANGUAGE C) + # Build options # OPTION( SONAME "Set the (SO)VERSION of the target" ON ) @@ -37,7 +41,9 @@ OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) -OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) +IF(HAVE_STRUCT_STAT_NSEC) + OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) +ENDIF() OPTION( VALGRIND "Configure build for valgrind" OFF ) OPTION( CURL "User curl for HTTP if available" ON) diff --git a/src/index.c b/src/index.c index 54a8b1eec..bd56cc794 100644 --- a/src/index.c +++ b/src/index.c @@ -858,8 +858,7 @@ void git_index_entry__init_from_stat( { entry->ctime.seconds = (git_time_t)st->st_ctime; entry->mtime.seconds = (git_time_t)st->st_mtime; -#if !defined(GIT_WIN32) && !defined(__APPLE__) - /* Apple and Windows doesn't provide these struct stat fields. */ +#if defined(GIT_USE_NSEC) entry->mtime.nanoseconds = st->st_mtim.tv_nsec; entry->ctime.nanoseconds = st->st_ctim.tv_nsec; #endif diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c index ed402bd14..f168963b2 100644 --- a/tests/merge/workdir/dirty.c +++ b/tests/merge/workdir/dirty.c @@ -164,8 +164,7 @@ static void hack_index(char *files[]) entry->ctime.seconds = (git_time_t)statbuf.st_ctime; entry->mtime.seconds = (git_time_t)statbuf.st_mtime; -#if !defined(GIT_WIN32) && !defined(__APPLE__) - /* Apple and Windows doesn't provide these struct stat fields. */ +#if defined(GIT_USE_NSEC) entry->ctime.nanoseconds = statbuf.st_ctim.tv_nsec; entry->mtime.nanoseconds = statbuf.st_mtim.tv_nsec; #else From c963fe1d3d7b576cc794049aafaf6bba46a24d30 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Tue, 23 Jun 2015 09:05:49 -0600 Subject: [PATCH 05/10] cmake: fix CMake code organization problem --- CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb1f1b8b4..a72659d34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,9 +23,6 @@ INCLUDE(CheckStructHasMember) INCLUDE(AddCFlagIfSupported) INCLUDE(FindPkgConfig) -CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h - HAVE_STRUCT_STAT_NSEC LANGUAGE C) - # Build options # OPTION( SONAME "Set the (SO)VERSION of the target" ON ) @@ -41,9 +38,6 @@ OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) -IF(HAVE_STRUCT_STAT_NSEC) - OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) -ENDIF() OPTION( VALGRIND "Configure build for valgrind" OFF ) OPTION( CURL "User curl for HTTP if available" ON) @@ -91,6 +85,12 @@ IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OPTION( USE_OPENSSL "Link with and use openssl library" ON ) ENDIF() +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h + HAVE_STRUCT_STAT_NSEC LANGUAGE C) +IF(HAVE_STRUCT_STAT_NSEC) + OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) +ENDIF() + # This variable will contain the libraries we need to put into # libgit2.pc's Requires.private. That is, what we're linking to or # what someone who's statically linking us needs to link to. From 360dd4dafd30bced1dab1d516765e347d09b91ba Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Tue, 23 Jun 2015 10:02:48 -0600 Subject: [PATCH 06/10] win32: define our own POSIX struct stat, and support USE_NSEC --- CMakeLists.txt | 2 +- src/common.h | 1 + src/win32/mingw-compat.h | 6 ------ src/win32/msvc-compat.h | 3 --- src/win32/w32_util.h | 22 +++++++++++++-------- src/win32/win32-compat.h | 42 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 src/win32/win32-compat.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a72659d34..0c9c356c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,7 @@ ENDIF() CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h HAVE_STRUCT_STAT_NSEC LANGUAGE C) -IF(HAVE_STRUCT_STAT_NSEC) +IF(HAVE_STRUCT_STAT_NSEC OR WIN32) OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF ) ENDIF() diff --git a/src/common.h b/src/common.h index 6dca36fbd..c87343dda 100644 --- a/src/common.h +++ b/src/common.h @@ -41,6 +41,7 @@ # include # include "win32/msvc-compat.h" # include "win32/mingw-compat.h" +# include "win32/win32-compat.h" # include "win32/error.h" # include "win32/version.h" # ifdef GIT_THREADS diff --git a/src/win32/mingw-compat.h b/src/win32/mingw-compat.h index a4a5a31c7..698ebed1a 100644 --- a/src/win32/mingw-compat.h +++ b/src/win32/mingw-compat.h @@ -11,12 +11,6 @@ #undef stat -#if _WIN32_WINNT >= 0x0601 -#define stat __stat64 -#else -#define stat _stati64 -#endif - #if _WIN32_WINNT < 0x0600 && !defined(__MINGW64_VERSION_MAJOR) #undef MemoryBarrier void __mingworg_MemoryBarrier(void); diff --git a/src/win32/msvc-compat.h b/src/win32/msvc-compat.h index 8004bc1f8..12b50d981 100644 --- a/src/win32/msvc-compat.h +++ b/src/win32/msvc-compat.h @@ -9,9 +9,6 @@ #if defined(_MSC_VER) -/* 64-bit stat information, regardless of USE_32BIT_TIME_T define */ -#define stat __stat64 - typedef unsigned short mode_t; typedef SSIZE_T ssize_t; diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h index 8db3afbec..727ed1cef 100644 --- a/src/win32/w32_util.h +++ b/src/win32/w32_util.h @@ -76,17 +76,23 @@ size_t git_win32__path_trim_end(wchar_t *str, size_t len); size_t git_win32__canonicalize_path(wchar_t *str, size_t len); /** - * Converts a FILETIME structure to a time_t. + * Converts a FILETIME structure to a struct timespec. * * @param FILETIME A pointer to a FILETIME - * @return A time_t containing the same time + * @param ts A pointer to the timespec structure to fill in */ -GIT_INLINE(time_t) git_win32__filetime_to_time_t(const FILETIME *ft) +GIT_INLINE(void) git_win32__filetime_to_timespec( + const FILETIME *ft, + struct timespec *ts) { long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */ - winTime /= 10000000; /* Nano to seconds resolution */ - return (time_t)winTime; + ts->tv_sec = (time_t)(winTime / 10000000); +#ifdef GIT_USE_NSEC + ts->tv_nsec = (winTime % 10000000) * 100; +#else + ts->tv_nsec = 0; +#endif } GIT_INLINE(void) git_win32__timeval_to_filetime( @@ -122,9 +128,9 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat( st->st_size = ((git_off_t)attrdata->nFileSizeHigh << 32) + attrdata->nFileSizeLow; st->st_dev = _getdrive() - 1; st->st_rdev = st->st_dev; - st->st_atime = git_win32__filetime_to_time_t(&(attrdata->ftLastAccessTime)); - st->st_mtime = git_win32__filetime_to_time_t(&(attrdata->ftLastWriteTime)); - st->st_ctime = git_win32__filetime_to_time_t(&(attrdata->ftCreationTime)); + git_win32__filetime_to_timespec(&(attrdata->ftLastAccessTime), &(st->st_atim)); + git_win32__filetime_to_timespec(&(attrdata->ftLastWriteTime), &(st->st_mtim)); + git_win32__filetime_to_timespec(&(attrdata->ftCreationTime), &(st->st_ctim)); if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && path) { git_win32_path target; diff --git a/src/win32/win32-compat.h b/src/win32/win32-compat.h new file mode 100644 index 000000000..8b4070df7 --- /dev/null +++ b/src/win32/win32-compat.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_win32_compat__ +#define INCLUDE_win32_compat__ + +#include +#include +#include +#include +#include + +struct p_timespec { + time_t tv_sec; + long tv_nsec; +}; + +#define timespec p_timespec + +struct p_stat { + _dev_t st_dev; + _ino_t st_ino; + mode_t st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + uint64_t st_size; + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; +#define st_atime st_atim.tv_sec +#define st_mtime st_mtim.tv_sec +#define st_ctime st_ctim.tv_sec +}; + +#define stat p_stat + +#endif /* INCLUDE_win32_compat__ */ From 0226f7dd36c990e9bc1632adbc655fefc797513a Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Sat, 29 Aug 2015 13:59:20 -0700 Subject: [PATCH 07/10] diff/index: respect USE_NSEC for racily clean file detection --- src/diff.c | 7 +++++-- src/fileops.c | 12 +++++++++--- src/fileops.h | 2 +- src/index.c | 22 +++++++++++++++------- tests/checkout/checkout_helpers.c | 10 +++++----- tests/index/racy.c | 16 ++++++++-------- 6 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/diff.c b/src/diff.c index b2259c74d..f8e0c53d3 100644 --- a/src/diff.c +++ b/src/diff.c @@ -131,7 +131,7 @@ static int diff_delta__from_one( if (status == GIT_DELTA_UNTRACKED && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED)) return 0; - + if (status == GIT_DELTA_UNREADABLE && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE)) return 0; @@ -864,7 +864,10 @@ static int maybe_modified( oitem->ino != nitem->ino || oitem->uid != nitem->uid || oitem->gid != nitem->gid || - (index && nitem->mtime.seconds >= index->stamp.mtime)) + (index && + ((nitem->mtime.seconds > (int32_t) index->stamp.mtime.tv_sec) || + ((nitem->mtime.seconds == (int32_t) index->stamp.mtime.tv_sec) && + (nitem->mtime.nanoseconds >= (uint32_t) index->stamp.mtime.tv_nsec))))) { status = GIT_DELTA_MODIFIED; modified_uncertain = true; diff --git a/src/fileops.c b/src/fileops.c index 57d2ce9c3..da1d8a0bb 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -1035,12 +1035,18 @@ int git_futils_filestamp_check( if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; - if (stamp->mtime == (git_time_t)st.st_mtime && + if (stamp->mtime.tv_sec == st.st_mtim.tv_sec && +#if defined(GIT_USE_NSEC) + stamp->mtime.tv_nsec == st.st_mtim.tv_nsec && +#endif stamp->size == (git_off_t)st.st_size && stamp->ino == (unsigned int)st.st_ino) return 0; - stamp->mtime = (git_time_t)st.st_mtime; + stamp->mtime.tv_sec = st.st_mtim.tv_sec; +#if defined(GIT_USE_NSEC) + stamp->mtime.tv_nsec = st.st_mtim.tv_nsec; +#endif stamp->size = (git_off_t)st.st_size; stamp->ino = (unsigned int)st.st_ino; @@ -1063,7 +1069,7 @@ void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st) { if (st) { - stamp->mtime = (git_time_t)st->st_mtime; + stamp->mtime = st->st_mtim; stamp->size = (git_off_t)st->st_size; stamp->ino = (unsigned int)st->st_ino; } else { diff --git a/src/fileops.h b/src/fileops.h index 572ff01a5..d806e83ba 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -309,7 +309,7 @@ extern int git_futils_fake_symlink(const char *new, const char *old); * versions could be implemented in the future. */ typedef struct { - git_time_t mtime; + struct timespec mtime; git_off_t size; unsigned int ino; } git_futils_filestamp; diff --git a/src/index.c b/src/index.c index bd56cc794..5311fe85f 100644 --- a/src/index.c +++ b/src/index.c @@ -719,18 +719,27 @@ int git_index__changed_relative_to( return !!git_oid_cmp(&index->checksum, checksum); } -static bool is_racy_timestamp(git_time_t stamp, git_index_entry *entry) +static bool is_racy_timestamp(const struct timespec *stamp, git_index_entry *entry) { /* Git special-cases submodules in the check */ if (S_ISGITLINK(entry->mode)) return false; /* If we never read the index, we can't have this race either */ - if (stamp == 0) + if(stamp->tv_sec == 0) return false; /* If the timestamp is the same or newer than the index, it's racy */ - return ((int32_t) stamp) <= entry->mtime.seconds; +#if defined(GIT_USE_NSEC) + if((int32_t) stamp->tv_sec < entry->mtime.seconds) + return true; + else if((int32_t) stamp->tv_sec > entry->mtime.seconds) + return false; + else + return (uint32_t) stamp->tv_nsec <= entry->mtime.nanoseconds; +#else + return ((int32_t) stamp->tv_sec) <= entry->mtime.seconds; +#endif } /* @@ -742,7 +751,6 @@ static int truncate_racily_clean(git_index *index) size_t i; int error; git_index_entry *entry; - git_time_t ts = index->stamp.mtime; git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff; @@ -756,7 +764,7 @@ static int truncate_racily_clean(git_index *index) diff_opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_DISABLE_PATHSPEC_MATCH; git_vector_foreach(&index->entries, i, entry) { - if (!is_racy_timestamp(ts, entry)) + if (!is_racy_timestamp(&index->stamp.mtime, entry)) continue; /* TODO: use the (non-fnmatching) filelist iterator */ @@ -2875,9 +2883,9 @@ int git_index_read_index( (error = git_iterator_for_index(&new_iterator, (git_index *)new_index, &opts)) < 0) goto done; - if (((error = git_iterator_current(&old_entry, index_iterator)) < 0 && + if (((error = git_iterator_current(&old_entry, index_iterator)) < 0 && error != GIT_ITEROVER) || - ((error = git_iterator_current(&new_entry, new_iterator)) < 0 && + ((error = git_iterator_current(&new_entry, new_iterator)) < 0 && error != GIT_ITEROVER)) goto done; diff --git a/tests/checkout/checkout_helpers.c b/tests/checkout/checkout_helpers.c index 92a454d12..fb2f415e7 100644 --- a/tests/checkout/checkout_helpers.c +++ b/tests/checkout/checkout_helpers.c @@ -132,7 +132,7 @@ int checkout_count_callback( void tick_index(git_index *index) { - git_time_t ts; + struct timespec ts; struct timeval times[2]; cl_assert(index->on_disk); @@ -141,10 +141,10 @@ void tick_index(git_index *index) cl_git_pass(git_index_read(index, true)); ts = index->stamp.mtime; - times[0].tv_sec = ts; - times[0].tv_usec = 0; - times[1].tv_sec = ts + 5; - times[1].tv_usec = 0; + times[0].tv_sec = ts.tv_sec; + times[0].tv_usec = ts.tv_nsec / 1000; + times[1].tv_sec = ts.tv_sec + 5; + times[1].tv_usec = ts.tv_nsec / 1000; cl_git_pass(p_utimes(git_index_path(index), times)); cl_git_pass(git_index_read(index, true)); diff --git a/tests/index/racy.c b/tests/index/racy.c index 3b26aabf4..df25c851c 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -63,10 +63,10 @@ void test_index_racy__write_index_just_after_file(void) cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A")); cl_git_mkfile(path.ptr, "A"); /* Force the file's timestamp to be a second after we wrote the index */ - times[0].tv_sec = index->stamp.mtime + 1; - times[0].tv_usec = 0; - times[1].tv_sec = index->stamp.mtime + 1; - times[1].tv_usec = 0; + times[0].tv_sec = index->stamp.mtime.tv_sec + 1; + times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000; + times[1].tv_sec = index->stamp.mtime.tv_sec + 1; + times[1].tv_usec = index->stamp.mtime.tv_nsec / 1000; cl_git_pass(p_utimes(path.ptr, times)); /* @@ -82,10 +82,10 @@ void test_index_racy__write_index_just_after_file(void) * Pretend this index' modification happend a second after the * file update, and rewrite the file in that same second. */ - times[0].tv_sec = index->stamp.mtime + 2; - times[0].tv_usec = 0; - times[1].tv_sec = index->stamp.mtime + 2; - times[0].tv_usec = 0; + times[0].tv_sec = index->stamp.mtime.tv_sec + 2; + times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000; + times[1].tv_sec = index->stamp.mtime.tv_sec + 2; + times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000; cl_git_pass(p_utimes(git_index_path(index), times)); cl_git_pass(p_utimes(path.ptr, times)); From 28cdb3153c8c6d651c5b2afcf315a2d188e9fde9 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Fri, 18 Sep 2015 23:48:22 -0700 Subject: [PATCH 08/10] apple: work around non-POSIX struct stat on OS X. --- src/fileops.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index da1d8a0bb..5b54298fa 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -359,7 +359,7 @@ GIT_INLINE(int) mkdir_validate_mode( return 0; } - + GIT_INLINE(int) mkdir_canonicalize( git_buf *path, uint32_t flags) @@ -1027,6 +1027,11 @@ int git_futils_filestamp_check( git_futils_filestamp *stamp, const char *path) { struct stat st; +#if defined(__APPLE__) + const struct timespec *statmtime = &st.st_mtimespec; +#else + const struct timespec *statmtime = &st.st_mtim; +#endif /* if the stamp is NULL, then always reload */ if (stamp == NULL) @@ -1035,17 +1040,17 @@ int git_futils_filestamp_check( if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; - if (stamp->mtime.tv_sec == st.st_mtim.tv_sec && + if (stamp->mtime.tv_sec == statmtime->tv_sec && #if defined(GIT_USE_NSEC) - stamp->mtime.tv_nsec == st.st_mtim.tv_nsec && + stamp->mtime.tv_nsec == statmtime->tv_nsec && #endif stamp->size == (git_off_t)st.st_size && stamp->ino == (unsigned int)st.st_ino) return 0; - stamp->mtime.tv_sec = st.st_mtim.tv_sec; + stamp->mtime.tv_sec = statmtime->tv_sec; #if defined(GIT_USE_NSEC) - stamp->mtime.tv_nsec = st.st_mtim.tv_nsec; + stamp->mtime.tv_nsec = statmtime->tv_nsec; #endif stamp->size = (git_off_t)st.st_size; stamp->ino = (unsigned int)st.st_ino; @@ -1068,8 +1073,14 @@ void git_futils_filestamp_set( void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st) { +#if defined(__APPLE__) + const struct timespec *statmtime = &st->st_mtimespec; +#else + const struct timespec *statmtime = &st->st_mtim; +#endif + if (st) { - stamp->mtime = st->st_mtim; + stamp->mtime = *statmtime; stamp->size = (git_off_t)st->st_size; stamp->ino = (unsigned int)st->st_ino; } else { From 973a09a4ca6502e45826ec310aabf931c48f196e Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Thu, 1 Oct 2015 18:35:21 -0700 Subject: [PATCH 09/10] index: don't populate nsec values if GIT_USE_NSEC is off --- src/fileops.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fileops.c b/src/fileops.c index 5b54298fa..a13a40655 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -1081,6 +1081,9 @@ void git_futils_filestamp_set_from_stat( if (st) { stamp->mtime = *statmtime; +#if !defined(GIT_USE_NSEC) + stamp->mtime.tv_nsec = 0; +#endif stamp->size = (git_off_t)st->st_size; stamp->ino = (unsigned int)st->st_ino; } else { From 28659e50d56bb79460c8a1d2315443267d6e6f66 Mon Sep 17 00:00:00 2001 From: Axel Rasmussen Date: Thu, 1 Oct 2015 18:36:10 -0700 Subject: [PATCH 10/10] diff: refactor complex timestamp check into its own function --- src/diff.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/diff.c b/src/diff.c index f8e0c53d3..09f29fcee 100644 --- a/src/diff.c +++ b/src/diff.c @@ -79,7 +79,7 @@ static bool diff_pathspec_match( git_diff *diff, const git_index_entry *entry) { - bool disable_pathspec_match = + bool disable_pathspec_match = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH); /* If we're disabling fnmatch, then the iterator has already applied @@ -703,6 +703,31 @@ static bool diff_time_eq( (!use_nanos || a->nanoseconds == b->nanoseconds); } +/* + * Test if the given index time is newer than the given existing index entry. + * If the timestamps are exactly equivalent, then the given index time is + * considered "racily newer" than the existing index entry. + */ +static bool diff_newer_than_index( + const git_index_time *a, const git_index *b, bool use_nanos) +{ + bool is_newer = false; + + if(!b) + return false; + + is_newer = is_newer || (a->seconds > (int32_t) b->stamp.mtime.tv_sec); + is_newer = is_newer || (!use_nanos && + (a->seconds == (int32_t) b->stamp.mtime.tv_sec)); + if(use_nanos) + { + is_newer = is_newer || ((a->seconds == (int32_t) b->stamp.mtime.tv_sec) && + (a->nanoseconds >= (uint32_t) b->stamp.mtime.tv_nsec)); + } + + return is_newer; +} + typedef struct { git_repository *repo; git_iterator *old_iter; @@ -864,10 +889,7 @@ static int maybe_modified( oitem->ino != nitem->ino || oitem->uid != nitem->uid || oitem->gid != nitem->gid || - (index && - ((nitem->mtime.seconds > (int32_t) index->stamp.mtime.tv_sec) || - ((nitem->mtime.seconds == (int32_t) index->stamp.mtime.tv_sec) && - (nitem->mtime.nanoseconds >= (uint32_t) index->stamp.mtime.tv_nsec))))) + diff_newer_than_index(&nitem->mtime, index, use_nanos)) { status = GIT_DELTA_MODIFIED; modified_uncertain = true;