Subject: [PATCH v3 20/26] read-cache: new variable to verify file-watcher results

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Mon, 3 Feb 2014 11:29:08 +0700

If GIT_TEST_WATCHED is set to a non-zero value, Git still uses file
watcher if configured. But it does lstat() anyway and notifies the
user if a file is changed but the file watcher said otherwise.

Note that there is a race condition. Changed paths are retrieved at
time X, then refresh and validation at time Y. Even if X and Y are
very close, an update can happen between X and Y, causing a false
report.

If GIT_TEST_WATCHED is set greater than 1, git will abort instead of
just warn and move on.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 Documentation/git-file-watcher.txt |  8 ++++++++
 cache.h                            |  5 ++++-
 read-cache.c                       | 17 +++++++++++++++++
 3 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-file-watcher.txt b/Documentation/git-file-watcher.txt
index d694fea..dd09e30 100644
--- a/Documentation/git-file-watcher.txt
+++ b/Documentation/git-file-watcher.txt
@@ -25,6 +25,14 @@ OPTIONS
 --detach::
 	Run in background.
 
+TROUBLESHOOTING
+---------------
+Setting environment variable `GIT_TEST_WATCHED` to a non-zero number
+makes Git communicate with the file watcher, but do lstat anyway to
+verify that the file watcher results. Setting to 1 prints warning when
+file watcher fails to monitor files correctly. Setting to 2 aborts Git
+when it happens.
+
 BUGS
 ----
 On Linux, file watcher may fail to detect changes if you move the work
diff --git a/cache.h b/cache.h
index c229bf9..806c886 100644
--- a/cache.h
+++ b/cache.h
@@ -224,7 +224,10 @@ static inline unsigned create_ce_flags(unsigned stage)
 #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
 static inline int ce_valid(const struct cache_entry *ce)
 {
-	return ce->ce_flags & CE_VALID;
+	extern int test_watched;
+	if (!test_watched)
+		return ce->ce_flags & CE_VALID;
+	return (ce->ce_flags & CE_VALID) && !(ce->ce_flags & CE_WATCHED);
 }
 
 #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
diff --git a/read-cache.c b/read-cache.c
index 95c9ccb..d5f084a 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -37,6 +37,7 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int reall
 #define CACHE_EXT_WATCH 0x57415443	  /* "WATC" */
 
 struct index_state the_index;
+int test_watched;
 
 static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
@@ -1117,6 +1118,16 @@ static void show_file(const char * fmt, const char * name, int in_porcelain,
 	printf(fmt, name);
 }
 
+static void report_bad_watcher(struct index_state *istate,
+			       struct cache_entry *ce)
+{
+	if (test_watched > 1)
+		die("%s is updated but file-watcher said no",
+		    ce->name);
+	warning("%s is updated but file-watcher said no",
+		ce->name);
+}
+
 int refresh_index(struct index_state *istate, unsigned int flags,
 		  const struct pathspec *pathspec,
 		  char *seen, const char *header_msg)
@@ -1188,6 +1199,9 @@ int refresh_index(struct index_state *istate, unsigned int flags,
 				ce->ce_flags &= ~CE_VALID;
 				istate->cache_changed = 1;
 			}
+			if (test_watched &&
+			    (ce->ce_flags & CE_WATCHED) && (ce->ce_flags & CE_VALID))
+				report_bad_watcher(istate, ce);
 			if (quiet)
 				continue;
 
@@ -1460,6 +1474,9 @@ int read_index_from(struct index_state *istate, const char *path)
 	if (istate->initialized)
 		return istate->cache_nr;
 
+	if (getenv("GIT_TEST_WATCHED"))
+		test_watched = atoi(getenv("GIT_TEST_WATCHED"));
+
 	istate->timestamp.sec = 0;
 	istate->timestamp.nsec = 0;
 	fd = open(path, O_RDONLY);
-- 
1.8.5.2.240.g8478abd