diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2016-07-03 03:58:02 -0400 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2016-07-06 11:22:40 -0700 |
commit | 1bb68cf5633f07d5c7218ee6668a6fa2641ba486 (patch) | |
tree | 094bed5b6eb84fb264f1bf2a76cf337a5e185534 | |
parent | 482a48afbb08c83080c69aaa03044ae39a60b492 (diff) | |
download | git-1bb68cf5633f07d5c7218ee6668a6fa2641ba486.tar.gz |
watchman: support watchman to reduce index refresh cost
The previous patch has the logic to clear bits in 'WAMA' bitmap. This
patch has logic to set bits as told by watchman. The missing bit,
_using_ these bits, are not here yet.
A lot of this code is written by David Turner originally, mostly from
[1]. I'm just copying and polishing it a bit.
[1] http://article.gmane.org/gmane.comp.version-control.git/248006
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | cache.h | 1 | ||||
-rw-r--r-- | config.c | 5 | ||||
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | environment.c | 2 | ||||
-rw-r--r-- | watchman-support.c | 135 | ||||
-rw-r--r-- | watchman-support.h | 7 |
7 files changed, 170 insertions, 0 deletions
@@ -451,6 +451,7 @@ MSGFMT = msgfmt CURL_CONFIG = curl-config PTHREAD_LIBS = -lpthread PTHREAD_CFLAGS = +WATCHMAN_LIBS = GCOV = gcov export TCL_PATH TCLTK_PATH @@ -1418,6 +1419,13 @@ else LIB_OBJS += thread-utils.o endif +ifdef USE_WATCHMAN + LIB_H += watchman-support.h + LIB_OBJS += watchman-support.o + WATCHMAN_LIBS = -lwatchman + BASIC_CFLAGS += -DUSE_WATCHMAN +endif + ifdef HAVE_PATHS_H BASIC_CFLAGS += -DHAVE_PATHS_H endif @@ -2029,6 +2037,9 @@ git-remote-testsvn$X: remote-testsvn.o GIT-LDFLAGS $(GITLIBS) $(VCSSVN_LIB) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) \ $(VCSSVN_LIB) +git-index-helper$X: index-helper.o GIT-LDFLAGS $(GITLIBS) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(WATCHMAN_LIBS) + $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY) $(QUIET_LNCP)$(RM) $@ && \ ln $< $@ 2>/dev/null || \ @@ -2168,6 +2179,7 @@ GIT-BUILD-OPTIONS: FORCE @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+ @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+ @echo NO_MMAP=\''$(subst ','\'',$(subst ','\'',$(NO_MMAP)))'\' >>$@+ + @echo USE_WATCHMAN=\''$(subst ','\'',$(subst ','\'',$(USE_WATCHMAN)))'\' >>$@+ ifdef TEST_OUTPUT_DIRECTORY @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+ endif @@ -696,6 +696,7 @@ extern char *git_replace_ref_base; extern int fsync_object_files; extern int core_preload_index; +extern int core_watchman_sync_timeout; extern int core_apply_sparse_checkout; extern int precomposed_unicode; extern int protect_hfs; @@ -882,6 +882,11 @@ static int git_default_core_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.watchmansynctimeout")) { + core_watchman_sync_timeout = git_config_int(var, value); + return 0; + } + if (!strcmp(var, "core.createobject")) { if (!strcmp(value, "rename")) object_creation_mode = OBJECT_CREATION_USES_RENAMES; diff --git a/configure.ac b/configure.ac index 0cd9f4680b..334d63b993 100644 --- a/configure.ac +++ b/configure.ac @@ -1099,6 +1099,14 @@ AC_COMPILE_IFELSE([BSD_SYSCTL_SRC], HAVE_BSD_SYSCTL=]) GIT_CONF_SUBST([HAVE_BSD_SYSCTL]) +# +# Check for watchman client library + +AC_CHECK_LIB([watchman], [watchman_connect], + [USE_WATCHMAN=YesPlease], + [USE_WATCHMAN=]) +GIT_CONF_SUBST([USE_WATCHMAN]) + ## Other checks. # Define USE_PIC if you need the main git objects to be built with -fPIC # in order to build and link perl/Git.so. x86-64 seems to need this. diff --git a/environment.c b/environment.c index 6dec9d0403..ad41015397 100644 --- a/environment.c +++ b/environment.c @@ -94,6 +94,8 @@ int core_preload_index = 1; */ int ignore_untracked_cache_config; +int core_watchman_sync_timeout = 300; + /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; static char *work_tree; diff --git a/watchman-support.c b/watchman-support.c new file mode 100644 index 0000000000..8421fe029d --- /dev/null +++ b/watchman-support.c @@ -0,0 +1,135 @@ +#include "cache.h" +#include "watchman-support.h" +#include "strbuf.h" +#include "dir.h" +#include <watchman.h> + +static struct watchman_query *make_query(const char *last_update) +{ + struct watchman_query *query = watchman_query(); + watchman_query_set_fields(query, WATCHMAN_FIELD_NAME | + WATCHMAN_FIELD_EXISTS | + WATCHMAN_FIELD_NEWER); + watchman_query_set_empty_on_fresh(query, 1); + query->sync_timeout = core_watchman_sync_timeout; + if (*last_update) + watchman_query_set_since_oclock(query, last_update); + return query; +} + +static struct watchman_query_result *query_watchman( + struct index_state *istate, struct watchman_connection *connection, + const char *fs_path, const char *last_update) +{ + struct watchman_error wm_error; + struct watchman_query *query; + struct watchman_expression *expr; + struct watchman_query_result *result; + + query = make_query(last_update); + expr = watchman_true_expression(); + result = watchman_do_query(connection, fs_path, query, expr, &wm_error); + watchman_free_query(query); + watchman_free_expression(expr); + + if (!result) + warning("Watchman query error: %s (at %s)", + wm_error.message, + *last_update ? last_update : "the beginning"); + + return result; +} + +static void update_index(struct index_state *istate, + struct watchman_query_result *result) +{ + int i; + + if (result->is_fresh_instance) { + /* let refresh clear them later */ + for (i = 0; i < istate->cache_nr; i++) + istate->cache[i]->ce_flags |= CE_WATCHMAN_DIRTY; + goto done; + } + + for (i = 0; i < result->nr; i++) { + struct watchman_stat *wm = result->stats + i; + int pos; + + if (S_ISDIR(wm->mode) || + !strncmp(wm->name, ".git/", 5) || + strstr(wm->name, "/.git/")) + continue; + + pos = index_name_pos(istate, wm->name, strlen(wm->name)); + if (pos < 0) { + if (istate->untracked) { + char *name = xstrdup(wm->name); + char *dname = dirname(name); + + /* + * dirname() returns '.' for the root, + * but we call it ''. + */ + if (dname[0] == '.' && dname[1] == 0) + string_list_append(&istate->untracked->invalid_untracked, ""); + else + string_list_append(&istate->untracked->invalid_untracked, + dname); + free(name); + } + continue; + } + /* FIXME: ignore staged entries and gitlinks too? */ + + istate->cache[pos]->ce_flags |= CE_WATCHMAN_DIRTY; + } + +done: + free(istate->last_update); + istate->last_update = xstrdup(result->clock); + istate->cache_changed |= WATCHMAN_CHANGED; + if (istate->untracked) + string_list_remove_duplicates(&istate->untracked->invalid_untracked, 0); +} + +int check_watchman(struct index_state *istate) +{ + struct watchman_error wm_error; + struct watchman_connection *connection; + struct watchman_query_result *result; + const char *fs_path; + struct timeval timeout; + /* + * Convert core_watchman_sync_timeout, in milliseconds, to + * struct timeval, in seconds and microseconds. + */ + + fs_path = get_git_work_tree(); + if (!fs_path) + return -1; + + timeout.tv_sec = core_watchman_sync_timeout / 1000; + timeout.tv_usec = (core_watchman_sync_timeout % 1000) * 1000; + connection = watchman_connect(timeout, &wm_error); + + if (!connection) { + warning("Watchman watch error: %s", wm_error.message); + return -1; + } + + if (watchman_watch(connection, fs_path, &wm_error)) { + warning("Watchman watch error: %s", wm_error.message); + watchman_connection_close(connection); + return -1; + } + + result = query_watchman(istate, connection, fs_path, + istate->last_update ? istate->last_update : ""); + watchman_connection_close(connection); + if (!result) + return -1; + update_index(istate, result); + watchman_free_query_result(result); + return 0; +} diff --git a/watchman-support.h b/watchman-support.h new file mode 100644 index 0000000000..ee1ef2c924 --- /dev/null +++ b/watchman-support.h @@ -0,0 +1,7 @@ +#ifndef WATCHMAN_SUPPORT_H +#define WATCHMAN_SUPPORT_H + +struct index_state; +int check_watchman(struct index_state *index); + +#endif /* WATCHMAN_SUPPORT_H */ |