Commit 0226f7dd36c990e9bc1632adbc655fefc797513a

Axel Rasmussen 2015-08-29T13:59:20

diff/index: respect USE_NSEC for racily clean file detection

diff --git a/src/diff.c b/src/diff.c
index b2259c7..f8e0c53 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 57d2ce9..da1d8a0 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 572ff01..d806e83 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 bd56cc7..5311fe8 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 92a454d..fb2f415 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 3b26aab..df25c85 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));