diff options
-rw-r--r-- | CMakeLists.txt | 11 | ||||
-rw-r--r-- | include/git2/common.h | 5 | ||||
-rw-r--r-- | src/common.h | 1 | ||||
-rw-r--r-- | src/diff.c | 35 | ||||
-rw-r--r-- | src/fileops.c | 28 | ||||
-rw-r--r-- | src/fileops.h | 2 | ||||
-rw-r--r-- | src/index.c | 28 | ||||
-rw-r--r-- | src/settings.c | 3 | ||||
-rw-r--r-- | src/win32/mingw-compat.h | 6 | ||||
-rw-r--r-- | src/win32/msvc-compat.h | 3 | ||||
-rw-r--r-- | src/win32/w32_util.h | 22 | ||||
-rw-r--r-- | src/win32/win32-compat.h | 42 | ||||
-rw-r--r-- | tests/checkout/checkout_helpers.c | 10 | ||||
-rw-r--r-- | tests/core/features.c | 6 | ||||
-rw-r--r-- | tests/index/racy.c | 16 | ||||
-rw-r--r-- | tests/merge/workdir/dirty.c | 7 |
16 files changed, 175 insertions, 50 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 92e0081ca..0537b19d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Mo INCLUDE(CheckLibraryExists) INCLUDE(CheckFunctionExists) +INCLUDE(CheckStructHasMember) INCLUDE(AddCFlagIfSupported) INCLUDE(FindPkgConfig) @@ -85,6 +86,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 OR WIN32) + 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. @@ -516,6 +523,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/include/git2/common.h b/include/git2/common.h index bf341e79f..e687977d5 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -101,8 +101,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/common.h b/src/common.h index 7170df91a..2913baa92 100644 --- a/src/common.h +++ b/src/common.h @@ -41,6 +41,7 @@ # include <ws2tcpip.h> # 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/diff.c b/src/diff.c index c2362358a..0ab3b8d1f 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 @@ -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; @@ -706,6 +706,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; @@ -838,7 +863,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); @@ -863,7 +892,7 @@ static int maybe_modified( oitem->ino != nitem->ino || oitem->uid != nitem->uid || oitem->gid != nitem->gid || - (index && nitem->mtime.seconds >= index->stamp.mtime)) + diff_newer_than_index(&nitem->mtime, index, use_nanos)) { status = GIT_DELTA_MODIFIED; modified_uncertain = true; diff --git a/src/fileops.c b/src/fileops.c index cfc22bb53..07eb504bd 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -366,7 +366,7 @@ GIT_INLINE(int) mkdir_validate_mode( return 0; } - + GIT_INLINE(int) mkdir_canonicalize( git_buf *path, uint32_t flags) @@ -1034,6 +1034,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) @@ -1042,12 +1047,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 == statmtime->tv_sec && +#if defined(GIT_USE_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 = (git_time_t)st.st_mtime; + stamp->mtime.tv_sec = statmtime->tv_sec; +#if defined(GIT_USE_NSEC) + stamp->mtime.tv_nsec = statmtime->tv_nsec; +#endif stamp->size = (git_off_t)st.st_size; stamp->ino = (unsigned int)st.st_ino; @@ -1069,8 +1080,17 @@ 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 = (git_time_t)st->st_mtime; + 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 { diff --git a/src/fileops.h b/src/fileops.h index c6db1953e..6c6c49dcf 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -310,7 +310,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 d3b8afd39..0903770bb 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 */ @@ -858,8 +866,10 @@ 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_USE_NSEC) + 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)) ? @@ -2924,9 +2934,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/src/settings.c b/src/settings.c index 030d28537..91bdfd520 100644 --- a/src/settings.c +++ b/src/settings.c @@ -34,6 +34,9 @@ int git_libgit2_features() #if defined(GIT_SSH) | GIT_FEATURE_SSH #endif +#if defined(GIT_USE_NSEC) + | GIT_FEATURE_NSEC +#endif ; } 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 <stdint.h> +#include <time.h> +#include <wchar.h> +#include <sys/stat.h> +#include <sys/types.h> + +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__ */ 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/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 } 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)); diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c index 4bf984c23..f168963b2 100644 --- a/tests/merge/workdir/dirty.c +++ b/tests/merge/workdir/dirty.c @@ -163,9 +163,14 @@ 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_USE_NSEC) + 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; |