summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-11-12 19:53:09 +0100
committerCarlos Martín Nieto <cmn@dwim.me>2015-11-12 19:53:09 +0100
commit75a0ccf52fef2cab281de886730cda595d3736aa (patch)
tree977b981aca588511e96f20dca4acec983d993a16
parent2c26c8679ffc02b6644e20eba9458121b944beb6 (diff)
parent28659e50d56bb79460c8a1d2315443267d6e6f66 (diff)
downloadlibgit2-75a0ccf52fef2cab281de886730cda595d3736aa.tar.gz
Merge pull request #3170 from CmdrMoozy/nsec_fix
git_index_entry__init_from_stat: set nsec fields in entry stats
-rw-r--r--CMakeLists.txt11
-rw-r--r--include/git2/common.h5
-rw-r--r--src/common.h1
-rw-r--r--src/diff.c35
-rw-r--r--src/fileops.c28
-rw-r--r--src/fileops.h2
-rw-r--r--src/index.c28
-rw-r--r--src/settings.c3
-rw-r--r--src/win32/mingw-compat.h6
-rw-r--r--src/win32/msvc-compat.h3
-rw-r--r--src/win32/w32_util.h22
-rw-r--r--src/win32/win32-compat.h42
-rw-r--r--tests/checkout/checkout_helpers.c10
-rw-r--r--tests/core/features.c6
-rw-r--r--tests/index/racy.c16
-rw-r--r--tests/merge/workdir/dirty.c7
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;