Fast git status via a file system watcher
Subsystems (2)
v5 cover Fast git status via a file system watcher
Changes from V4 include:
fsmonitor.c:
Only flag index dirty if fsmonitor extension is added or removed or if new cache or untracked cache entries are marked dirty
dir.c:
If an untracked cache entry is flagged as fsmonitor_dirty, fall back to existing logic to stat the file and check for changes instead of assuming it is dirty.
hooks--query-fsmonitor.sample:
Optimize query to exclude transitory files (files that were created and deleted since the last call).
Update path mangling on Windows to match Watchman beta.
test-drop-caches.c:
Add perf helper to drop the disk cache on Windows.
p7519-fsmonitor.sh:
Add perf test for fsmonitor changes
Ben Peart (7):
bswap: add 64 bit endianness helper get_be64
dir: make lookup_untracked() available outside of dir.c
fsmonitor: teach git to optionally utilize a file system monitor to
speed up detecting new or changed files.
fsmonitor: add test cases for fsmonitor extension
fsmonitor: add documentation for the fsmonitor extension.
fsmonitor: add a sample query-fsmonitor hook script for Watchman
fsmonitor: add a performance test
Documentation/config.txt | 7 + Documentation/githooks.txt | 23 +++ Documentation/technical/index-format.txt | 19 +++ Makefile | 2 + builtin/update-index.c | 1 + cache.h | 5 + compat/bswap.h | 4 + config.c | 4 + dir.c | 29 ++-- dir.h | 5 + entry.c | 1 + environment.c | 1 + fsmonitor.c | 261 +++++++++++++++++++++++++++++++ fsmonitor.h | 9 ++ read-cache.c | 28 +++- t/helper/test-drop-caches.c | 107 +++++++++++++ t/perf/p7519-fsmonitor.sh | 161 +++++++++++++++++++ t/t7519-status-fsmonitor.sh | 173 ++++++++++++++++++++ templates/hooks--query-fsmonitor.sample | 76 +++++++++ unpack-trees.c | 1 + 20 files changed, 904 insertions(+), 13 deletions(-) create mode 100644 fsmonitor.c create mode 100644 fsmonitor.h create mode 100644 t/helper/test-drop-caches.c create mode 100755 t/perf/p7519-fsmonitor.sh create mode 100755 t/t7519-status-fsmonitor.sh create mode 100755 templates/hooks--query-fsmonitor.sample
Signature · 1 line
2.13.0
PATCH v5 1/7 bswap: add 64 bit endianness helper get_be64
Add a new get_be64 macro to enable 64 bit endian conversions on memory that may or may not be aligned.
--- compat/bswap.h | 4 ++++ 1 file changed, 4 insertions(+)
compat/bswap.h +4 -0
@@ -158,6 +158,7 @@ static inline uint64_t git_bswap64(uint64_t x)#define get_be16 (p) ntohs(*(unsigned short *)(p))#define get_be32 (p) ntohl(*(unsigned int *)(p)) +#define get_be64 (p) ntohll(*(uint64_t *)(p))#define put_be32 (p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)#else
@@ -170,6 +171,9 @@ static inline uint64_t git_bswap64(uint64_t x) (*((unsigned char *)(p) + 1) << 16) | \ (*((unsigned char *)(p) + 2) << 8) | \ (*((unsigned char *)(p) + 3) << 0) ) +#define get_be64 (p) ( \ + ((uint64_t)get_be32((unsigned char *)(p) + 0) << 32) | \ + ((uint64_t)get_be32((unsigned char *)(p) + 4) << 0)#define put_be32 (p, v) do { \ unsigned int __v = (v); \ *((unsigned char *)(p) + 0) = __v >> 24; \
Signature · 1 line
2.13.0
PATCH v5 2/7 dir: make lookup_untracked() available outside of dir.c
In reply to Ben PeartRemove the static qualifier from lookup_untracked() and make it available to other modules by exporting it from dir.h. This will be used later when we need to find entries to mark 'fsmonitor dirty.'
--- dir.c | 2 +- dir.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-)
dir.c +1 -1
@@ -666,7 +666,7 @@ static void trim_trailing_spaces(char *buf)* * If "name" has the trailing slash, it'll be excluded in the search. */ -static struct untracked_cache_dir *lookup_untracked (struct untracked_cache *uc, +struct untracked_cache_dir *lookup_untracked (struct untracked_cache *uc,struct untracked_cache_dir *dir,const char *name,int len) {
dir.h +3 -0
@@ -354,4 +354,7 @@ extern void connect_work_tree_and_git_dir(const char *work_tree, const char *gitextern void relocate_gitdir (const char *path,const char *old_git_dir,const char *new_git_dir); +struct untracked_cache_dir *lookup_untracked (struct untracked_cache *uc, +struct untracked_cache_dir *dir, +const char *name,int len);#endif
Signature · 1 line
2.13.0
PATCH v5 3/7 fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
In reply to Ben PeartWhen the index is read from disk, the query-fsmonitor index extension is used to flag the last known potentially dirty index and untracked cache entries.
If git finds out some entries are 'fsmonitor-dirty', but are really unchanged (e.g. the file was changed, then reverted back), then Git will clear the marking in the extension. If git adds or updates an index entry, it is marked 'fsmonitor-dirty' to ensure it is checked for changes in the working directory.
Before the 'fsmonitor-dirty' flags are used to limit the scope of the files to be checked, the query-fsmonitor hook proc is called with the time the index was last updated. The hook proc returns the list of files changed since that last updated time and the list of potentially dirty entries is updated to reflect the current state.
refresh_index() and valid_cached_dir() are updated so that any entry not flagged as potentially dirty is not checked as it cannot have any changes.
--- Makefile | 1 + builtin/update-index.c | 1 + cache.h | 5 + config.c | 4 + dir.c | 27 +++-- dir.h | 2 + entry.c | 1 + environment.c | 1 + fsmonitor.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++ fsmonitor.h | 9 ++ read-cache.c | 28 +++++- unpack-trees.c | 1 + 12 files changed, 329 insertions(+), 12 deletions(-) create mode 100644 fsmonitor.c create mode 100644 fsmonitor.h
Makefile +1 -0
@@ -763,6 +763,7 @@ LIB_OBJS += ewah/ewah_rlw.o LIB_OBJS += exec_cmd.o LIB_OBJS += fetch-pack.o LIB_OBJS += fsck.o +LIB_OBJS += fsmonitor.o LIB_OBJS += gettext.o LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o
builtin/update-index.c +1 -0
@@ -232,6 +232,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)else active_cache[pos]->ce_flags &= ~flag; active_cache[pos]->ce_flags |=CE_UPDATE_IN_BASE ; + active_cache[pos]->ce_flags |=CE_FSMONITOR_DIRTY ;cache_tree_invalidate_path (&the_index, path); active_cache_changed |=CE_ENTRY_CHANGED ;return 0 ;
cache.h +5 -0
@@ -201,6 +201,7 @@ struct cache_entry {#define CE_ADDED (1 << 19)#define CE_HASHED (1 << 20) +#define CE_FSMONITOR_DIRTY (1 << 21)#define CE_WT_REMOVE (1 << 22)/* remove in work directory */ #define CE_CONFLICTED (1 << 23)
@@ -324,6 +325,7 @@ static inline unsigned int canon_mode(unsigned int mode)#define CACHE_TREE_CHANGED (1 << 5)#define SPLIT_INDEX_ORDERED (1 << 6)#define UNTRACKED_CHANGED (1 << 7) +#define FSMONITOR_CHANGED (1 << 8)struct split_index ;struct untracked_cache ;
@@ -342,6 +344,8 @@ struct index_state {struct hashmap dir_hash;unsigned char sha1[20 ];struct untracked_cache *untracked; +uint64_t fsmonitor_last_update; +struct ewah_bitmap *fsmonitor_dirty; };extern struct index_state the_index;
@@ -767,6 +771,7 @@ extern int precomposed_unicode;extern int protect_hfs;extern int protect_ntfs;extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env; +extern int core_fsmonitor;/* * Include broken refs in all ref iterations, which will
config.c +4 -0
@@ -1245,6 +1245,10 @@ static int git_default_core_config(const char *var, const char *value) hide_dotfiles =HIDE_DOTFILES_DOTGITONLY ; else hide_dotfiles =git_config_bool (var, value); + } + +if (!strcmp (var,"core.fsmonitor" )) { + core_fsmonitor =git_config_bool (var, value);return 0 ; }
dir.c +17 -10
@@ -17,6 +17,7 @@#include "utf8.h" #include "varint.h" #include "ewah/ewok.h" +#include "fsmonitor.h" /* * Tells read_directory_recursive how a file or directory should be treated.
@@ -1680,17 +1681,23 @@ static int valid_cached_dir(struct dir_struct *dir,if (!untracked)return 0 ; -if (stat (path->len ? path->buf :"." , &st)) { -invalidate_directory (dir->untracked, untracked); -memset (&untracked->stat_data,0 ,sizeof (untracked->stat_data)); -return 0 ; - } -if (!untracked->valid || -match_stat_data_racy (istate, &untracked->stat_data, &st)) { -if (untracked->valid) +/* +* With fsmonitor, we can trust the untracked cache's valid field. +*/ +refresh_by_fsmonitor (istate); +if (!(dir->untracked->use_fsmonitor && untracked->valid)) { +if (stat (path->len ? path->buf :"." , &st)) {invalidate_directory (dir->untracked, untracked); -fill_stat_data (&untracked->stat_data, &st); -return 0 ; +memset (&untracked->stat_data,0 ,sizeof (untracked->stat_data)); +return 0 ; + } +if (!untracked->valid || +match_stat_data_racy (istate, &untracked->stat_data, &st)) { +if (untracked->valid) +invalidate_directory (dir->untracked, untracked); +fill_stat_data (&untracked->stat_data, &st); +return 0 ; + } }if (untracked->check_only != !!check_only) {
dir.h +2 -0
@@ -139,6 +139,8 @@ struct untracked_cache {int gitignore_invalidated;int dir_invalidated;int dir_opened; +/* fsmonitor invalidation data */ + unsigned int use_fsmonitor : 1; };struct dir_struct {
entry.c +1 -0
@@ -222,6 +222,7 @@ static int write_entry(struct cache_entry *ce,lstat (ce->name, &st);fill_stat_cache_info (ce, &st); ce->ce_flags |=CE_UPDATE_IN_BASE ; + ce->ce_flags |=CE_FSMONITOR_DIRTY ; state->istate->cache_changed |=CE_ENTRY_CHANGED ; }return 0 ;
environment.c +1 -0
@@ -64,6 +64,7 @@ int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */unsigned long pack_size_limit_cfg;enum hide_dotfiles_type hide_dotfiles =HIDE_DOTFILES_DOTGITONLY ;enum log_refs_config log_all_ref_updates =LOG_REFS_UNSET ; +int core_fsmonitor;#ifndef PROTECT_HFS_DEFAULT #define PROTECT_HFS_DEFAULT 0
fsmonitor.c new file +261 -0
261 lines not on this page · raw message
fsmonitor.h new file +9 -0
@@ -0,0 +1,9 @@ +#ifndef FSMONITOR_H +#define FSMONITOR_H + +int read_fsmonitor_extension (struct index_state *istate,const void *data,unsigned long sz); +void write_fsmonitor_extension (struct strbuf *sb,struct index_state *istate); +void tweak_fsmonitor_extension (struct index_state *istate); +void refresh_by_fsmonitor (struct index_state *istate); + +#endif
read-cache.c +26 -2
@@ -18,6 +18,7 @@#include "varint.h" #include "split-index.h" #include "utf8.h" +#include "fsmonitor.h" /* Mask for the name length in ce_flags in the on-disk index */
@@ -37,11 +38,12 @@#define CACHE_EXT_RESOLVE_UNDO 0x52455543/* "REUC" */ #define CACHE_EXT_LINK 0x6c696e6b/* "link" */ #define CACHE_EXT_UNTRACKED 0x554E5452/* "UNTR" */ +#define CACHE_EXT_FSMONITOR 0x46534D4E/* "FSMN" */ /* changes that can be kept in $GIT_DIR/index (basically all extensions) */ #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \ CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \ - SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED) + SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED | FSMONITOR_CHANGED)struct index_state the_index;static const char *alternate_index_output;
@@ -61,6 +63,7 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cachefree (old);set_index_entry (istate, nr, ce); ce->ce_flags |=CE_UPDATE_IN_BASE ; + ce->ce_flags |=CE_FSMONITOR_DIRTY ; istate->cache_changed |=CE_ENTRY_CHANGED ; }
@@ -777,6 +780,7 @@ int chmod_index_entry(struct index_state *istate, struct cache_entry *ce, }cache_tree_invalidate_path (istate, ce->name); ce->ce_flags |=CE_UPDATE_IN_BASE ; + ce->ce_flags |=CE_FSMONITOR_DIRTY ; istate->cache_changed |=CE_ENTRY_CHANGED ;return 0 ;
@@ -1344,6 +1348,8 @@ int refresh_index(struct index_state *istate, unsigned int flags,const char *added_fmt;const char *unmerged_fmt; +refresh_by_fsmonitor (istate); + modified_fmt = (in_porcelain ?"M\t%s\n" :"%s: needs update\n" ); deleted_fmt = (in_porcelain ?"D\t%s\n" :"%s: needs update\n" ); typechange_fmt = (in_porcelain ?"T\t%s\n" :"%s needs update\n" );
@@ -1380,8 +1386,11 @@ int refresh_index(struct index_state *istate, unsigned int flags,continue ; new =refresh_cache_ent (istate, ce, options, &cache_errno, &changed); -if (new == ce) +if (new == ce) { + ce->ce_flags &= ~CE_FSMONITOR_DIRTY ;continue ; + } +if (!new) {const char *fmt;
@@ -1391,6 +1400,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,*/ ce->ce_flags &= ~CE_VALID ; ce->ce_flags |=CE_UPDATE_IN_BASE ; + ce->ce_flags |=CE_FSMONITOR_DIRTY ; istate->cache_changed |=CE_ENTRY_CHANGED ; }if (quiet)
@@ -1549,6 +1559,9 @@ static int read_index_extension(struct index_state *istate,case CACHE_EXT_UNTRACKED : istate->untracked =read_untracked_extension (data, sz);break ; +case CACHE_EXT_FSMONITOR : +read_fsmonitor_extension (istate, data, sz); +break ;default :if (*ext <'A' ||'Z' < *ext) return error("index uses %.4s extension, which we do not understand",
@@ -1721,6 +1734,7 @@ static void post_read_index_from(struct index_state *istate)check_ce_order (istate);tweak_untracked_cache (istate);tweak_split_index (istate); +tweak_fsmonitor_extension (istate); }/* remember to discard_cache() before reading a different cache! */
@@ -2295,6 +2309,16 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,if (err)return -1 ; } +if (!strip_extensions && istate->fsmonitor_last_update) { +struct strbuf sb =STRBUF_INIT ; + +write_fsmonitor_extension (&sb, istate); + err =write_index_ext_header (&c, newfd,CACHE_EXT_FSMONITOR , sb.len) <0 + ||ce_write (&c, newfd, sb.buf, sb.len) <0 ; +strbuf_release (&sb); +if (err) +return -1 ; + }if (ce_flush (&c, newfd, istate->sha1))return -1 ;
unpack-trees.c +1 -0
@@ -417,6 +417,7 @@ static int apply_sparse_checkout(struct index_state *istate, ce->ce_flags &= ~CE_SKIP_WORKTREE ;if (was_skip_worktree !=ce_skip_worktree (ce)) { ce->ce_flags |=CE_UPDATE_IN_BASE ; + ce->ce_flags |=CE_FSMONITOR_DIRTY ; istate->cache_changed |=CE_ENTRY_CHANGED ; }
Signature · 1 line
2.13.0
Re: [PATCH v5 3/7] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
fsmonitor.c line 22
Quoted 12 lines · Ben Peart
fsmonitor.c new file +12
-0+int read_fsmonitor_extension (struct index_state *istate,const void *data, +unsigned long sz) +{ +const char *index = data; +uint32_t hdr_version; +uint32_t ewah_size; +int ret; + +if (sz <sizeof (uint32_t ) +sizeof (uint64_t ) +sizeof (uint32_t )) +return error ("corrupt fsmonitor extension (too short)" ); + + hdr_version =get_be32 (index);
Here we use get_be32(), ...
fsmonitor.c line 27
Quoted 5 lines
fsmonitor.c new file +5
-0+ index +=sizeof (uint32_t ); +if (hdr_version !=INDEX_EXTENSION_VERSION ) +return error ("bad fsmonitor version %d" , hdr_version); + + istate->fsmonitor_last_update =get_be64 (index);
...get_be64(), ...
fsmonitor.c line 30
Quoted 3 lines
fsmonitor.c new file +3
-0+ index +=sizeof (uint64_t ); + + ewah_size =get_be32 (index);
... and get_be32 again, ...
Quoted 14 lines
fsmonitor.c new file +14
-0+ index +=sizeof (uint32_t ); + + istate->fsmonitor_dirty =ewah_new (); + ret =ewah_read_mmap (istate->fsmonitor_dirty, index, ewah_size); +if (ret != ewah_size) { +ewah_free (istate->fsmonitor_dirty); + istate->fsmonitor_dirty =NULL ; +return error ("failed to parse ewah bitmap reading fsmonitor index extension" ); + } + +return 0 ; +} + +void write_fsmonitor_extension (struct strbuf *sb,struct index_state *istate)
fsmonitor.c line 54
Quoted 10 lines
fsmonitor.c new file +10
-0+{ +uint32_t hdr_version; +uint64_t tm; +struct ewah_bitmap *bitmap; +int i; +uint32_t ewah_start; +uint32_t ewah_size =0 ; +int fixup =0 ; + + hdr_version =htonl (INDEX_EXTENSION_VERSION );
... but here we use htonl() instead of put_be32(), ...
fsmonitor.c line 57
Quoted 3 lines
fsmonitor.c new file +3
-0+strbuf_add (sb, &hdr_version,sizeof (uint32_t )); + + tm =htonll ((uint64_t )istate->fsmonitor_last_update);
... htonll(), ...
Quoted 4 lines
fsmonitor.c new file +4
-0+strbuf_add (sb, &tm,sizeof (uint64_t )); + fixup = sb->len; +strbuf_add (sb, &ewah_size,sizeof (uint32_t ));/* we'll fix this up later */ +
fsmonitor.c line 71
Quoted 10 lines
fsmonitor.c new file +10
-0+ ewah_start = sb->len; + bitmap =ewah_new (); +for (i =0 ; i < istate->cache_nr; i++) +if (istate->cache[i]->ce_flags &CE_FSMONITOR_DIRTY ) +ewah_set (bitmap, i); +ewah_serialize_strbuf (bitmap, sb); +ewah_free (bitmap); + +/* fix up size field */ + ewah_size =htonl (sb->len - ewah_start);
... and htonl() again.
It would be more consistent (and perhaps more correct) to use put_beXX() functions, instead of the htonl() and htonll() functions.
Quoted 12 lines
fsmonitor.c new file +11
-0+memcpy (sb->buf + fixup, &ewah_size,sizeof (uint32_t )); +} +/* +* Call the query-fsmonitor hook passing the time of the last saved results. +*/ +static int query_fsmonitor (int version,uint64_t last_update,struct strbuf *query_result) +{ +struct child_process cp =CHILD_PROCESS_INIT ; +char ver[64 ]; +char date[64 ]; +const char *argv[4 ];
fsmonitor.c line 155
Quoted 10 lines
fsmonitor.c new file +10
-0+ +if (!(argv[0 ] =find_hook ("query-fsmonitor" ))) +return -1 ; + +snprintf (ver,sizeof (version),"%d" , version); +snprintf (date,sizeof (date),"%" PRIuMAX, (uintmax_t )last_update); + argv[1 ] = ver; + argv[2 ] = date; + argv[3 ] =NULL ; + cp.argv = argv;
Maybe it would be nicer using the argv_array_pushX() functions.
Quoted 23 lines
fsmonitor.c new file +23
-0+ cp.out =-1 ; + +return capture_command (&cp, query_result,1024 ); +} + +static void mark_file_dirty (struct index_state *istate,const char *name) +{ +struct untracked_cache_dir *dir; +int pos; + +/* find it in the index and mark that entry as dirty */ + pos =index_name_pos (istate, name,strlen (name)); +if (pos >=0 ) { +if (!(istate->cache[pos]->ce_flags &CE_FSMONITOR_DIRTY )) { + istate->cache[pos]->ce_flags |=CE_FSMONITOR_DIRTY ; + istate->cache_changed |=FSMONITOR_CHANGED ; + } + } + +/* +* Find the corresponding directory in the untracked cache +* and mark it as invalid +*/
fsmonitor.c line 188
Quoted 10 lines
fsmonitor.c new file +10
-0+if (!istate->untracked || !istate->untracked->root) +return ; + + dir =find_untracked_cache_dir (istate->untracked, istate->untracked->root, name); +if (dir) { +if (dir->valid) { + dir->valid =0 ; + istate->cache_changed |=FSMONITOR_CHANGED ; + } + }
The code above is quite similar as what is in mark_fsmonitor_dirty(), so I wonder if a refactoring is possible.
Quoted 29 lines
fsmonitor.c new file +29
-0+} + +void refresh_by_fsmonitor (struct index_state *istate) +{ +static int has_run_once =0 ; +struct strbuf query_result =STRBUF_INIT ; +int query_success =0 ; +size_t bol =0 ;/* beginning of line */ +uint64_t last_update; +char *buf, *entry; +int i; + +if (!core_fsmonitor || has_run_once) +return ; + has_run_once =1 ; + +/* +* This could be racy so save the date/time now and the hook +* should be inclusive to ensure we don't miss potential changes. +*/ + last_update =getnanotime (); + +/* +* If we have a last update time, call query-monitor for the set of +* changes since that time. +*/ +if (istate->fsmonitor_last_update) { + query_success = !query_fsmonitor (HOOK_INTERFACE_VERSION , + istate->fsmonitor_last_update, &query_result);
fsmonitor.c line 227
Quoted 10 lines
fsmonitor.c new file +10
-0+trace_performance_since (last_update,"query-fsmonitor" ); + } + +if (query_success) { +/* Mark all entries returned by the monitor as dirty */ + buf = entry = query_result.buf; +for (i =0 ; i < query_result.len; i++) { +if (buf[i] !='\0' ) +continue ; +mark_file_dirty (istate, buf + bol);
It looks like bol is always equal to i here...
fsmonitor.c line 231
Quoted 4 lines
fsmonitor.c new file +4
-0+ bol = i +1 ; + } +if (bol < query_result.len) +mark_file_dirty (istate, buf + bol);
... and here too. As it is not used below, I wonder if you really need the bol variable.
Quoted 7 lines
fsmonitor.c new file +7
-0+/* Mark all clean entries up-to-date */ +for (i =0 ; i < istate->cache_nr; i++) { +struct cache_entry *ce = istate->cache[i]; +if (ce_stage (ce) || (ce->ce_flags &CE_FSMONITOR_DIRTY )) +continue ; +ce_mark_uptodate (ce); + }
Re: [PATCH v5 3/7] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 81 lines · Christian Couder
fsmonitor.c line 22
Quoted 12 lines · Ben Peart
fsmonitor.c new file +12
-0+int read_fsmonitor_extension (struct index_state *istate,const void *data, +unsigned long sz) +{ +const char *index = data; +uint32_t hdr_version; +uint32_t ewah_size; +int ret; + +if (sz <sizeof (uint32_t ) +sizeof (uint64_t ) +sizeof (uint32_t )) +return error ("corrupt fsmonitor extension (too short)" ); + + hdr_version =get_be32 (index);Here we use
get_be32(), ...fsmonitor.c line 27
Quoted 5 lines
fsmonitor.c new file +5
-0+ index +=sizeof (uint32_t ); +if (hdr_version !=INDEX_EXTENSION_VERSION ) +return error ("bad fsmonitor version %d" , hdr_version); + + istate->fsmonitor_last_update =get_be64 (index);...get_be64(), ...
fsmonitor.c line 30
Quoted 3 lines
fsmonitor.c new file +3
-0+ index +=sizeof (uint64_t ); + + ewah_size =get_be32 (index);... and
get_be32again, ...Quoted 14 lines
fsmonitor.c new file +14
-0+ index +=sizeof (uint32_t ); + + istate->fsmonitor_dirty =ewah_new (); + ret =ewah_read_mmap (istate->fsmonitor_dirty, index, ewah_size); +if (ret != ewah_size) { +ewah_free (istate->fsmonitor_dirty); + istate->fsmonitor_dirty =NULL ; +return error ("failed to parse ewah bitmap reading fsmonitor index extension" ); + } + +return 0 ; +} + +void write_fsmonitor_extension (struct strbuf *sb,struct index_state *istate)fsmonitor.c line 54
Quoted 10 lines
fsmonitor.c new file +10
-0+{ +uint32_t hdr_version; +uint64_t tm; +struct ewah_bitmap *bitmap; +int i; +uint32_t ewah_start; +uint32_t ewah_size =0 ; +int fixup =0 ; + + hdr_version =htonl (INDEX_EXTENSION_VERSION );... but here we use
htonl()instead ofput_be32(), ...fsmonitor.c line 57
Quoted 3 lines
fsmonitor.c new file +3
-0+strbuf_add (sb, &hdr_version,sizeof (uint32_t )); + + tm =htonll ((uint64_t )istate->fsmonitor_last_update);...
htonll(), ...Quoted 4 lines
fsmonitor.c new file +4
-0+strbuf_add (sb, &tm,sizeof (uint64_t )); + fixup = sb->len; +strbuf_add (sb, &ewah_size,sizeof (uint32_t ));/* we'll fix this up later */ +fsmonitor.c line 71
Quoted 10 lines
fsmonitor.c new file +10
-0+ ewah_start = sb->len; + bitmap =ewah_new (); +for (i =0 ; i < istate->cache_nr; i++) +if (istate->cache[i]->ce_flags &CE_FSMONITOR_DIRTY ) +ewah_set (bitmap, i); +ewah_serialize_strbuf (bitmap, sb); +ewah_free (bitmap); + +/* fix up size field */ + ewah_size =htonl (sb->len - ewah_start);... and
htonl()again.
Quoted 2 lines
It would be more consistent (and perhaps more correct) to use
put_beXX()functions, instead of thehtonl()andhtonll()functions.
I agree that these are asymmetric. I followed the pattern used in the untracked cache in which write_untracked_extension uses htonl and read_untracked_extension uses get_be32. I can change this to be more symmetric.
Quoted 17 lines
Quoted 17 lines
fsmonitor.c new file +15
-0+memcpy (sb->buf + fixup, &ewah_size,sizeof (uint32_t )); +} +/* +* Call the query-fsmonitor hook passing the time of the last saved results. +*/ +static int query_fsmonitor (int version,uint64_t last_update,struct strbuf *query_result) +{ +struct child_process cp =CHILD_PROCESS_INIT ; +char ver[64 ]; +char date[64 ]; +const char *argv[4 ]; + +if (!(argv[0 ] =find_hook ("query-fsmonitor" ))) +return -1 ; +
Quoted 8 lines
fsmonitor.c line 155
Quoted 6 lines
fsmonitor.c new file +6
-0+snprintf (ver,sizeof (version),"%d" , version); +snprintf (date,sizeof (date),"%" PRIuMAX, (uintmax_t )last_update); + argv[1 ] = ver; + argv[2 ] = date; + argv[3 ] =NULL ; + cp.argv = argv;Maybe it would be nicer using the
argv_array_pushX()functions.
When the number of arguments is fixed and known, I prefer avoiding the dynamic allocations that come along with the argv_array_pushX() functions.
Quoted 29 lines
Quoted 29 lines
fsmonitor.c new file +28
-0+ cp.out =-1 ; + +return capture_command (&cp, query_result,1024 ); +} + +static void mark_file_dirty (struct index_state *istate,const char *name) +{ +struct untracked_cache_dir *dir; +int pos; + +/* find it in the index and mark that entry as dirty */ + pos =index_name_pos (istate, name,strlen (name)); +if (pos >=0 ) { +if (!(istate->cache[pos]->ce_flags &CE_FSMONITOR_DIRTY )) { + istate->cache[pos]->ce_flags |=CE_FSMONITOR_DIRTY ; + istate->cache_changed |=FSMONITOR_CHANGED ; + } + } + +/* +* Find the corresponding directory in the untracked cache +* and mark it as invalid +*/ +if (!istate->untracked || !istate->untracked->root) +return ; + + dir =find_untracked_cache_dir (istate->untracked, istate->untracked->root, name); +if (dir) {
Quoted 8 lines
fsmonitor.c line 188
Quoted 5 lines
fsmonitor.c new file +5
-0+if (dir->valid) { + dir->valid =0 ; + istate->cache_changed |=FSMONITOR_CHANGED ; + } + }The code above is quite similar as what is in
mark_fsmonitor_dirty(), so I wonder if a refactoring is possible.
I've felt the same way and looked at how to refactor it better a number of times but never came up with a way that made it simpler, clearer and easier to maintain. I'm happy to review a patch if you can figure out something better. :)
Quoted 43 lines
Quoted 30 lines
fsmonitor.c new file +29
-0+} + +void refresh_by_fsmonitor (struct index_state *istate) +{ +static int has_run_once =0 ; +struct strbuf query_result =STRBUF_INIT ; +int query_success =0 ; +size_t bol =0 ;/* beginning of line */ +uint64_t last_update; +char *buf, *entry; +int i; + +if (!core_fsmonitor || has_run_once) +return ; + has_run_once =1 ; + +/* +* This could be racy so save the date/time now and the hook +* should be inclusive to ensure we don't miss potential changes. +*/ + last_update =getnanotime (); + +/* +* If we have a last update time, call query-monitor for the set of +* changes since that time. +*/ +if (istate->fsmonitor_last_update) { + query_success = !query_fsmonitor (HOOK_INTERFACE_VERSION , + istate->fsmonitor_last_update, &query_result);fsmonitor.c line 227
Quoted 10 lines
fsmonitor.c new file +10
-0+trace_performance_since (last_update,"query-fsmonitor" ); + } + +if (query_success) { +/* Mark all entries returned by the monitor as dirty */ + buf = entry = query_result.buf; +for (i =0 ; i < query_result.len; i++) { +if (buf[i] !='\0' ) +continue ; +mark_file_dirty (istate, buf + bol);It looks like bol is always equal to i here...
Quoted 7 lines
fsmonitor.c line 231
Quoted 4 lines
fsmonitor.c new file +4
-0+ bol = i +1 ; + } +if (bol < query_result.len) +mark_file_dirty (istate, buf + bol);... and here too. As it is not used below, I wonder if you really need the bol variable.
bol saves the position of the beginning of the current line while i iterates through the remainder of the string looking for the NULL.
However, the entry variable is no longer used so I will remove it.
Quoted 8 lines
Quoted 8 lines
fsmonitor.c new file +7
-0+/* Mark all clean entries up-to-date */ +for (i =0 ; i < istate->cache_nr; i++) { +struct cache_entry *ce = istate->cache[i]; +if (ce_stage (ce) || (ce->ce_flags &CE_FSMONITOR_DIRTY )) +continue ; +ce_mark_uptodate (ce); + }
PATCH v5 4/7 fsmonitor: add test cases for fsmonitor extension
In reply to Ben PeartAdd test cases that ensure status results are correct when using the new fsmonitor extension. Test untracked, modified, and new files by ensuring the results are identical to when not using the extension.
Add a test to ensure updates to the index properly mark corresponding entries in the index extension as dirty so that the status is correct after commands that modify the index but don't trigger changes in the working directory.
Add a test that verifies that if the fsmonitor extension doesn't tell git about a change, it doesn't discover it on its own. This ensures git is honoring the extension and that we get the performance benefits desired.
All test hooks output a marker file that is used to ensure the hook was actually used to generate the test results.
--- t/t7519-status-fsmonitor.sh | 173 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100755 t/t7519-status-fsmonitor.sh
t/t7519-status-fsmonitor.sh new file +173 -0
@@ -0,0 +1,173 @@ +#!/bin/sh + +test_description='git status with file system watcher' + +. ./test-lib.sh + +clean_repo () { + git reset --hard HEAD && + git clean -fd && + rm -f marker +} + +dirty_repo () { + : >untracked && + : >dir1/untracked && + : >dir2/untracked && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + echo 4 >new && + echo 5 >dir1/new && + echo 6 >dir2/new && + git add new && + git add dir1/new && + git add dir2/new +} + +# The test query-fsmonitor hook proc will output a marker file we can use to +# ensure the hook was actually used to generate the correct results. + +# fsmonitor works correctly with or without the untracked cache +# but if it is available, we'll turn it on to ensure we test that +# codepath as well. + +test_lazy_prereq UNTRACKED_CACHE ' + { git update-index --test-untracked-cache; ret=$?; } && + test $ret -ne 1 +' + +if test_have_prereq UNTRACKED_CACHE; then + git config core.untrackedcache true +else + git config core.untrackedcache false +fi + +test_expect_success 'setup' ' + mkdir -p .git/hooks && + : >tracked && + : >modified && + mkdir dir1 && + : >dir1/tracked && + : >dir1/modified && + mkdir dir2 && + : >dir2/tracked && + : >dir2/modified && + git add . && + test_tick && + git commit -m initial && + git config core.fsmonitor true && + cat >.gitignore <<-\EOF + .gitignore + expect* + output* + marker* + EOF +' + +# Ensure commands that call refresh_index() to move the index back in time +# properly invalidate the fsmonitor cache + +test_expect_success 'refresh_index() invalidates fsmonitor cache' ' + git status && + test_path_is_missing marker && + dirty_repo && + write_script .git/hooks/query-fsmonitor<<-\EOF && + :>marker + EOF + git add . && + git commit -m "to reset" && + git status && + test_path_is_file marker && + git reset HEAD~1 && + rm -f marker && + git status >output && + test_path_is_file marker && + git -c core.fsmonitor=false status >expect && + test_i18ncmp expect output +' + +# Now make sure it's actually skipping the check for modified and untracked +# files unless it is told about them. Note, after "git reset --hard HEAD" no +# extensions exist other than 'TREE' so do a "git status" to get the extension +# written before testing the results. + +test_expect_success "status doesn't detect unreported modifications" ' + write_script .git/hooks/query-fsmonitor<<-\EOF && + :>marker + EOF + clean_repo && + git status && + test_path_is_missing marker && + : >untracked && + echo 2 >dir1/modified && + git status >output && + test_path_is_file marker && + test_i18ngrep ! "Changes not staged for commit:" output && + test_i18ngrep ! "Untracked files:" output && + write_script .git/hooks/query-fsmonitor<<-\EOF && + :>marker + printf "untracked\0" + printf "dir1/modified\0" + EOF + rm -f marker && + git status >output && + test_path_is_file marker && + test_i18ngrep "Changes not staged for commit:" output && + test_i18ngrep "Untracked files:" output +' + +# Status is well tested elsewhere so we'll just ensure that the results are +# the same when using core.fsmonitor. First call after turning on the option +# does a complete scan so we need to do two calls to ensure we test the new +# codepath. + +test_expect_success 'status with core.untrackedcache false' ' + git config core.untrackedcache false && + write_script .git/hooks/query-fsmonitor<<-\EOF && + if [ $1 -ne 1 ] + then + echo -e "Unsupported query-fsmonitor hook version.\n" >&2 + exit 1; + fi + : >marker + printf "untracked\0" + printf "dir1/untracked\0" + printf "dir2/untracked\0" + printf "modified\0" + printf "dir1/modified\0" + printf "dir2/modified\0" + printf "new\0"" + printf "dir1/new\0" + printf "dir2/new\0" + EOF + clean_repo && + dirty_repo && + git -c core.fsmonitor=false status >expect && + clean_repo && + git status && + test_path_is_missing marker && + dirty_repo && + git status >output && + test_path_is_file marker && + test_i18ncmp expect output +' + +if ! test_have_prereq UNTRACKED_CACHE; then + skip_all='This system does not support untracked cache' + test_done +fi + +test_expect_success 'status with core.untrackedcache true' ' + git config core.untrackedcache true && + git -c core.fsmonitor=false status >expect && + clean_repo && + git status && + test_path_is_missing marker && + dirty_repo && + git status >output && + test_path_is_file marker && + test_i18ncmp expect output +' + +test_done
Signature · 1 line
2.13.0
Re: [PATCH v5 4/7] fsmonitor: add test cases for fsmonitor extension
Quoted 4 lines · Ben Peart
t/t7519-status-fsmonitor.sh new file +4
-0+# fsmonitor works correctly with or without the untracked cache +# but if it is available, we'll turn it on to ensure we test that +# codepath as well. +
t/t7519-status-fsmonitor.sh line 44
Quoted 10 lines
t/t7519-status-fsmonitor.sh new file +10
-0+test_lazy_prereq UNTRACKED_CACHE ' + { git update-index --test-untracked-cache; ret=$?; } && + test $ret -ne 1 +' + +if test_have_prereq UNTRACKED_CACHE; then + git config core.untrackedcache true +else + git config core.untrackedcache false +fi
I wonder if it would be better to just do something like:
=====================
test_expect_success 'setup' '
....
'
uc_values="false"test_have_prereq UNTRACKED_CACHE && uc_values="false true"
for uc_val in $uc_values
do
test_expect_success "setup untracked cache to $uc_val" '
git config core.untrackedcache $uc_val
'
test_expect_success 'refresh_index() invalidates fsmonitor cache' '
...
'
test_expect_success "status doesn't detect unreported modifications" '
...
'...
done
=====================
Re: [PATCH v5 4/7] fsmonitor: add test cases for fsmonitor extension
Quoted 18 lines · Christian Couder
Quoted 4 lines · Ben Peart
t/t7519-status-fsmonitor.sh new file +4
-0+# fsmonitor works correctly with or without the untracked cache +# but if it is available, we'll turn it on to ensure we test that +# codepath as well. +t/t7519-status-fsmonitor.sh line 44
Quoted 10 lines
t/t7519-status-fsmonitor.sh new file +10
-0+test_lazy_prereq UNTRACKED_CACHE ' + { git update-index --test-untracked-cache; ret=$?; } && + test $ret -ne 1 +' + +if test_have_prereq UNTRACKED_CACHE; then + git config core.untrackedcache true +else + git config core.untrackedcache false +fiI wonder if it would be better to just do something like:
That is a good idea; I'll add that around the tests that aren't explicitly testing interop with and without the untracked cache.
Thanks!
Quoted 31 lines
=====================
test_expect_success'setup' '....'
uc_values="false"
test_have_prereqUNTRACKED_CACHE&& uc_values="false true"for
uc_valin $uc_values
dotest_expect_success "setup untracked cache to $uc_val" ' git config core.untrackedcache $uc_val ' test_expect_success 'refresh_index() invalidates fsmonitor cache' ' ... ' test_expect_success "status doesn't detect unreported modifications" ' ... '...
done
=====================
PATCH v5 5/7 fsmonitor: add documentation for the fsmonitor extension.
In reply to Ben PeartThis includes the core.fsmonitor setting, the query-fsmonitor hook, and the fsmonitor index extension.
--- Documentation/config.txt | 7 +++++++ Documentation/githooks.txt | 23 +++++++++++++++++++++++ Documentation/technical/index-format.txt | 19 +++++++++++++++++++ 3 files changed, 49 insertions(+)
Documentation/config.txt +7 -0
@@ -410,6 +410,13 @@ core.protectNTFS:: 8.3 "short" names. Defaults to `true` on Windows, and `false` elsewhere. +core.fsmonitor:: + If set to true, call the query-fsmonitor hook proc which will + identify all files that may have had changes since the last + request. This information is used to speed up operations like + 'git commit' and 'git status' by limiting what git must scan to + detect changes. + core.trustctime:: If false, the ctime differences between the index and the working tree are ignored; useful when the inode change time
Documentation/githooks.txt +23 -0
@@ -456,6 +456,29 @@ non-zero status causes 'git send-email' to abort before sending any e-mails. +[[query-fsmonitor]] +query-fsmonitor +~~~~~~~~~~~~~~~ + +This hook is invoked when the configuration option core.fsmonitor is +set and git needs to identify changed or untracked files. It takes +two arguments, a version (currently 1) and the time in elapsed +nanoseconds since midnight, January 1, 1970. + +The hook should output to stdout the list of all files in the working +directory that may have changed since the requested time. The logic +should be inclusive so that it does not miss any potential changes. +The paths should be relative to the root of the working directory +and be separated by a single NUL. + +Git will limit what files it checks for changes as well as which +directories are checked for untracked files based on the path names +given. + +The exit status determines whether git will use the data from the +hook to limit its search. On error, it will fall back to verifying +all files and folders. + GIT --- Part of the linkgit:git[1] suite
Documentation/technical/index-format.txt +19 -0
@@ -295,3 +295,22 @@ The remaining data of each directory block is grouped by type: in the previous ewah bitmap. - One NUL. + +== File System Monitor cache + + The file system monitor cache tracks files for which the query-fsmonitor + hook has told us about changes. The signature for this extension is + { 'F', 'S', 'M', 'N' }. + + The extension starts with + + - 32-bit version number: the current supported version is 1. + + - 64-bit time: the extension data reflects all changes through the given + time which is stored as the nanoseconds elapsed since midnight, + January 1, 1970. + + - 32-bit bitmap size: the size of the CE_FSMONITOR_DIRTY bitmap. + + - An ewah bitmap, the n-th bit indicates whether the n-th index entry + is CE_FSMONITOR_DIRTY.
Signature · 1 line
2.13.0
PATCH v5 6/7 fsmonitor: add a sample query-fsmonitor hook script for Watchman
In reply to Ben PeartThis hook script integrates the new fsmonitor capabilities of git with the cross platform Watchman file watching service. To use the script:
Download and install Watchman from https://facebook.github.io/watchman/
and instruct Watchman to watch your working directory for changes ('watchman watch-project /usr/src/git').
Rename the sample integration hook from query-fsmonitor.sample to query-fsmonitor.
Configure git to use the extension ('git config core.fsmonitor true') and optionally turn on the untracked cache for optimal performance ('git config core.untrackedcache true').
--- templates/hooks--query-fsmonitor.sample | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100755 templates/hooks--query-fsmonitor.sample
templates/hooks--query-fsmonitor.sample new file +76 -0
@@ -0,0 +1,76 @@ +#!/bin/sh +# +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to provide fast +# git status. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-fsmonitor" + +# check the hook interface version +if [ "$1" -eq 1 ] +then + # convert nanoseconds to seconds + time_t=$(($2/1000000000)) +else + echo -e "Unsupported query-fsmonitor hook version.\nFalling back to scanning...\n" >&2 + exit 1; +fi + +# Convert unix style paths to what Watchman expects +case "$(uname -s)" in +MINGW*|MSYS_NT*) + GIT_WORK_TREE="$(cygpath -aw "$PWD" | sed 's,\\,/,g')" + ;; +*) + GIT_WORK_TREE="$PWD" + ;; +esac + +# In the query expression below we're asking for names of files that +# changed since $time_t but were not transient (ie created after +# $time_t but no longer exist). +# +# To accomplish this, we're using the "since" generator to use the +# recency index to select candidate nodes and "fields" to limit the +# output to file names only. Then we're using the "expression" term to +# further constrain the results. +# +# The category of transient files that we want to ignore will have a +# creation clock (cclock) newer than $time_t value and will also not +# currently exist. + +echo "[\"query\", \"$GIT_WORK_TREE\", { \ + \"since\": $time_t, \ + \"fields\": [\"name\"], \ + \"expression\": [\"not\", [\"allof\", [\"since\", $time_t, \"cclock\"], [\"not\", \"exists\"]]] \ + }]" | \ + watchman -j | + perl -0666 -e ' + use strict; + use warnings; + + my $stdin = <>; + die "Watchman: command returned no output.\nFalling back to scanning...\n" if $stdin eq ""; + die "Watchman: command returned invalid output: $stdin\nFalling back to scanning...\n" unless $stdin =~ /^\{/; + + my $json_pkg; + eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; + } or do { + require JSON::PP; + $json_pkg = "JSON::PP"; + }; + + my $o = $json_pkg->new->utf8->decode($stdin); + die "Watchman: $o->{error}.\nFalling back to scanning...\n" if $o->{error}; + + local $, = "\0"; + print @{$o->{files}}; + '
Signature · 1 line
2.13.0
PATCH v5 7/7 fsmonitor: add a performance test
In reply to Ben PeartAdd a test utility (test-drop-caches) that enables dropping the file system cache on Windows.
Add a perf test (p7519-fsmonitor.sh) for fsmonitor.
--- Makefile | 1 + t/helper/test-drop-caches.c | 107 +++++++++++++++++++++++++++++ t/perf/p7519-fsmonitor.sh | 161 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 t/helper/test-drop-caches.c create mode 100755 t/perf/p7519-fsmonitor.sh
Makefile +1 -0
@@ -648,6 +648,7 @@ TEST_PROGRAMS_NEED_X += test-subprocess TEST_PROGRAMS_NEED_X += test-svn-fe TEST_PROGRAMS_NEED_X += test-urlmatch-normalization TEST_PROGRAMS_NEED_X += test-wildmatch +TEST_PROGRAMS_NEED_X += test-drop-caches TEST_PROGRAMS = $(patsubst %,t/helper/%$X,$(TEST_PROGRAMS_NEED_X))
t/helper/test-drop-caches.c new file +107 -0
107 lines not on this page · raw message
t/perf/p7519-fsmonitor.sh new file +161 -0
161 lines not on this page · raw message
Signature · 1 line
2.13.0
Re: [PATCH v5 7/7] fsmonitor: add a performance test
Here are some perf test results for repos of various size generated with many-files.sh.
Comparing cold fs cache times on a fast SSD running Windows:
# files preloadindex fsmonitor reduction
=========================================================
10,000 0.69 0.46 33%
100,000 3.75 0.7 81%
1,000,000 35.07 3.24 91%
10,000 files
GIT_PERF_REPEAT_COUNT=1 GIT_PERF_LARGE_REPO=/c/Repos/gen-many-files-3.10.9.git/ ./run p7519-fsmonitor.sh
Test this tree
---------------------------------------------------------------------------------
7519.3: status (fsmonitor=false, cold fs cache) 0.69(0.03+0.06)
7519.4: status (fsmonitor=false, warm fs cache) 0.45(0.01+0.06)
7519.6: status -uno (fsmonitor=false, cold fs cache) 0.53(0.03+0.04)
7519.8: status -uall (fsmonitor=false, cold fs cache) 0.57(0.04+0.01)
7519.11: status (fsmonitor=true, cold fs cache, cold fsmonitor) 2.16(0.01+0.06) 7519.12: status (fsmonitor=true, warm fs cache, warm fsmonitor) 0.36(0.01+0.07) 7519.14: status (fsmonitor=true, cold fs cache, warm fsmonitor) 0.46(0.04+0.06) 7519.16: status -uno (fsmonitor=true, cold fs cache) 0.46(0.03+0.04)
7519.18: status -uall (fsmonitor=true, cold fs cache) 0.76(0.04+0.04)
100,000 files
GIT_PERF_REPEAT_COUNT=1 GIT_PERF_LARGE_REPO=/c/Repos/gen-many-files-4.10.9.git/ ./run p7519-fsmonitor.sh
Test this tree
----------------------------------------------------------------------------------
7519.3: status (fsmonitor=false, cold fs cache) 3.75(0.01+0.04)
7519.4: status (fsmonitor=false, warm fs cache) 2.74(0.06+0.06)
7519.6: status -uno (fsmonitor=false, cold fs cache) 2.88(0.01+0.06)
7519.8: status -uall (fsmonitor=false, cold fs cache) 3.24(0.00+0.06)
7519.11: status (fsmonitor=true, cold fs cache, cold fsmonitor) 17.90(0.00+0.10) 7519.12: status (fsmonitor=true, warm fs cache, warm fsmonitor) 0.59(0.00+0.09) 7519.14: status (fsmonitor=true, cold fs cache, warm fsmonitor) 0.70(0.00+0.07) 7519.16: status -uno (fsmonitor=true, cold fs cache) 0.69(0.01+0.01)
7519.18: status -uall (fsmonitor=true, cold fs cache) 4.51(0.00+0.06)
1,000,000 files
GIT_PERF_REPEAT_COUNT=1 GIT_PERF_LARGE_REPO=/c/Repos/gen-many-files-5.10.9.git/ ./run p7519-fsmonitor.sh
Test this tree
-----------------------------------------------------------------------------------
7519.3: status (fsmonitor=false, cold fs cache) 35.07(0.01+0.03)
7519.4: status (fsmonitor=false, warm fs cache) 26.58(0.03+0.07)
7519.6: status -uno (fsmonitor=false, cold fs cache) 26.46(0.03+0.06)
7519.8: status -uall (fsmonitor=false, cold fs cache) 31.55(0.01+0.03)
7519.11: status (fsmonitor=true, cold fs cache, cold fsmonitor) 193.15(0.01+0.04) 7519.12: status (fsmonitor=true, warm fs cache, warm fsmonitor) 3.03(0.01+0.07) 7519.14: status (fsmonitor=true, cold fs cache, warm fsmonitor) 3.24(0.01+0.04) 7519.16: status -uno (fsmonitor=true, cold fs cache) 2.99(0.03+0.03)
7519.18: status -uall (fsmonitor=true, cold fs cache) 35.07(0.03+0.07)
Quoted 308 lines · Ben Peart
Add a test utility (test-drop-caches) that enables dropping the file system cache on Windows.
Add a perf test (
p7519-fsmonitor.sh) for fsmonitor.Signed-off-by Ben Peart <[email protected]>Signed-off-by Ævar Arnfjörð Bjarmason <[email protected]>--- Makefile | 1 + t/helper/test-drop-caches.c | 107 +++++++++++++++++++++++++++++ t/perf/p7519-fsmonitor.sh | 161 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+)create mode 100644
t/helper/test-drop-caches.c
create mode 100755t/perf/p7519-fsmonitor.shMakefile +1
-0@@ -648,6 +648,7 @@ TEST_PROGRAMS_NEED_X += test-subprocess TEST_PROGRAMS_NEED_X += test-svn-fe TEST_PROGRAMS_NEED_X += test-urlmatch-normalization TEST_PROGRAMS_NEED_X += test-wildmatch +TEST_PROGRAMS_NEED_X += test-drop-caches TEST_PROGRAMS = $(patsubst %,t/helper/%$X,$(TEST_PROGRAMS_NEED_X))t/helper/test-drop-caches.c new file +107
-0@@ -0,0 +1,107 @@ +#include "git-compat-util.h" +#include <stdio.h> + +typedef DWORD NTSTATUS ; + +#ifdef GIT_WINDOWS_NATIVE +#include <tchar.h> + +#define STATUS_SUCCESS (0x00000000L) +#define STATUS_PRIVILEGE_NOT_HELD (0xC0000061L) + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemMemoryListInformation =80 ,// 80, q: SYSTEM_MEMORY_LIST_INFORMATION; s: SYSTEM_MEMORY_LIST_COMMAND (requires SeProfileSingleProcessPrivilege) +}SYSTEM_INFORMATION_CLASS ; + +// private +typedef enum _SYSTEM_MEMORY_LIST_COMMAND +{ + MemoryCaptureAccessedBits, + MemoryCaptureAndResetAccessedBits, + MemoryEmptyWorkingSets, + MemoryFlushModifiedList, + MemoryPurgeStandbyList, + MemoryPurgeLowPriorityStandbyList, + MemoryCommandMax +}SYSTEM_MEMORY_LIST_COMMAND ; + +BOOL GetPrivilege (HANDLE TokenHandle,LPCSTR lpName,int flags) +{ +BOOL bResult; +DWORD dwBufferLength; +LUID luid; +TOKEN_PRIVILEGES tpPreviousState; +TOKEN_PRIVILEGES tpNewState; + + dwBufferLength =16 ; + bResult =LookupPrivilegeValueA (0 , lpName, &luid); +if (bResult) + { + tpNewState.PrivilegeCount =1 ; + tpNewState.Privileges[0 ].Luid = luid; + tpNewState.Privileges[0 ].Attributes =0 ; + bResult =AdjustTokenPrivileges (TokenHandle,0 , &tpNewState, (DWORD )((LPBYTE )&(tpNewState.Privileges[1 ]) - (LPBYTE )&tpNewState), &tpPreviousState, &dwBufferLength); +if (bResult) + { + tpPreviousState.PrivilegeCount =1 ; + tpPreviousState.Privileges[0 ].Luid = luid; + tpPreviousState.Privileges[0 ].Attributes = flags !=0 ?2 :0 ; + bResult =AdjustTokenPrivileges (TokenHandle,0 , &tpPreviousState, dwBufferLength,0 ,0 ); + } + } +return bResult; +} +#endif + +int cmd_main (int argc,const char **argv) +{ +NTSTATUS status =1 ; +#ifdef GIT_WINDOWS_NATIVE +HANDLE hProcess =GetCurrentProcess (); +HANDLE hToken; +if (!OpenProcessToken (hProcess,TOKEN_QUERY |TOKEN_ADJUST_PRIVILEGES , &hToken)) + { +_ftprintf (stderr,_T ("Can't open current process token\n" )); +return 1 ; + } + +if (!GetPrivilege (hToken,"SeProfileSingleProcessPrivilege" ,1 )) + { +_ftprintf (stderr,_T ("Can't get SeProfileSingleProcessPrivilege\n" )); +return 1 ; + } + +CloseHandle (hToken); + +HMODULE ntdll =LoadLibrary (_T ("ntdll.dll" )); +if (!ntdll) + { +_ftprintf (stderr,_T ("Can't load ntdll.dll, wrong Windows version?\n" )); +return 1 ; + } + +NTSTATUS (WINAPI *NtSetSystemInformation)(INT ,PVOID ,ULONG ) = (NTSTATUS (WINAPI *)(INT ,PVOID ,ULONG ))GetProcAddress (ntdll,"NtSetSystemInformation" ); +if (!NtSetSystemInformation) + { +_ftprintf (stderr,_T ("Can't get function addresses, wrong Windows version?\n" )); +return 1 ; + } + +SYSTEM_MEMORY_LIST_COMMAND command = MemoryPurgeStandbyList; + status =NtSetSystemInformation ( + SystemMemoryListInformation, + &command, +sizeof (SYSTEM_MEMORY_LIST_COMMAND ) + ); +if (status ==STATUS_PRIVILEGE_NOT_HELD ) + { +_ftprintf (stderr,_T ("Insufficient privileges to execute the memory list command" )); + } +else if (status !=STATUS_SUCCESS ) + { +_ftprintf (stderr,_T ("Unable to execute the memory list command %lX" ), status); + } +#endif + +return status; +}t/perf/p7519-fsmonitor.sh new file +161
-0@@ -0,0 +1,161 @@ +#!/bin/sh + +test_description="Test core.fsmonitor" + +. ./perf-lib.sh + +# This has to be run with GIT_PERF_REPEAT_COUNT=1 to generate valid results. +# Otherwise the caching that happens for the nth run will negate the validity +# of the comparisons. +if [ "$GIT_PERF_REPEAT_COUNT" -ne 1 ] +then + echo "warning: This test must be run with GIT_PERF_REPEAT_COUNT=1 to generate valid results." >&2 + echo "warning: Setting GIT_PERF_REPEAT_COUNT=1" >&2 + GIT_PERF_REPEAT_COUNT=1 +fi + +test_perf_large_repo +test_checkout_worktree + +# Convert unix style paths to what Watchman expects +case "$(uname -s)" in +MINGW*|MSYS_NT*) + GIT_WORK_TREE="$(cygpath -aw "$PWD" | sed 's,\\,/,g')" + ;; +*) + GIT_WORK_TREE="$PWD" + ;; +esac + +# The big win for using fsmonitor is the elimination of the need to scan +# the working directory looking for changed files and untracked files. If +# the file information is all cached in RAM, the benefits are reduced. + +flush_disk_cache () { + case "$(uname -s)" in + MINGW*|MSYS_NT*) + sync && test-drop-caches + ;; + *) + sudo sync && echo 3 | sudo tee /proc/sys/vm/drop_caches + ;; + esac + +} + +test_lazy_prereq UNTRACKED_CACHE ' + { git update-index --test-untracked-cache; ret=$?; } && + test $ret -ne 1 +' + +test_expect_success "setup" ' + # Maybe set untrackedCache & splitIndex depending on the environment + if test -n "$GIT_PERF_7519_UNTRACKED_CACHE" + then + git config core.untrackedCache "$GIT_PERF_7519_UNTRACKED_CACHE" + else + if test_have_prereq UNTRACKED_CACHE + then + git config core.untrackedCache true + else + git config core.untrackedCache false + fi + fi && + + if test -n "$GIT_PERF_7519_SPLIT_INDEX" + then + git config core.splitIndex "$GIT_PERF_7519_SPLIT_INDEX" + fi && + + # Hook scaffolding + mkdir .git/hooks && + cp ../../../templates/hooks--query-fsmonitor.sample .git/hooks/query-fsmonitor && + + # have Watchman monitor the test folder + watchman watch "$GIT_WORK_TREE" && + watchman watch-list | grep -q -F "$GIT_WORK_TREE" +' + +# Worst case without fsmonitor +test_expect_success "clear fs cache" ' + git config core.fsmonitor false && + flush_disk_cache +' +test_perf "status (fsmonitor=false, cold fs cache)" ' + git status +' + +# Best case without fsmonitor +test_perf "status (fsmonitor=false, warm fs cache)" ' + git status +' + +# Let's see if -uno & -uall make any difference +test_expect_success "clear fs cache" ' + flush_disk_cache +' +test_perf "status -uno (fsmonitor=false, cold fs cache)" ' + git status -uno +' + +test_expect_success "clear fs cache" ' + flush_disk_cache +' +test_perf "status -uall (fsmonitor=false, cold fs cache)" ' + git status -uall +' + +# The first run with core.fsmonitor=true has to do a normal scan and write +# out the index extension. +test_expect_success "populate extension" ' + # core.preloadIndex defeats the benefits of core.fsMonitor as it + # calls lstat for the index entries. Turn it off as _not_ doing + # the work is faster than doing the work across multiple threads. + git config core.fsmonitor true && + git config core.preloadIndex false && + git status +' + +# Worst case with fsmonitor +test_expect_success "shutdown fsmonitor, clear fs cache" ' + watchman shutdown-server && + flush_disk_cache +' +test_perf "status (fsmonitor=true, cold fs cache, cold fsmonitor)" ' + git status +' + +# Best case with fsmonitor +test_perf "status (fsmonitor=true, warm fs cache, warm fsmonitor)" ' + git status +' + +# Best improved with fsmonitor (compare to worst case without fsmonitor) +test_expect_success "clear fs cache" ' + flush_disk_cache +' +test_perf "status (fsmonitor=true, cold fs cache, warm fsmonitor)" ' + git status +' + +# Let's see if -uno & -uall make any difference +test_expect_success "clear fs cache" ' + flush_disk_cache +' +test_perf "status -uno (fsmonitor=true, cold fs cache)" ' + git status -uno +' + +test_expect_success "clear fs cache" ' + flush_disk_cache +' +test_perf "status -uall (fsmonitor=true, cold fs cache)" ' + git status -uall +' + +test_expect_success "cleanup" ' + watchman watch-del "$GIT_WORK_TREE" && + watchman shutdown-server +' + +test_done
Re: [PATCH v5 7/7] fsmonitor: add a performance test
In reply to Ben Peartt/helper/test-drop-caches.c line 2
Quoted 8 lines · Ben Peart
t/helper/test-drop-caches.c new file +2
-0@@ -0,0 +1,107 @@ +#include "git-compat-util.h" +#include <stdio.h>
I thought compat-util should already include stdio?
t/helper/test-drop-caches.c line 4
Quoted 2 lines
t/helper/test-drop-caches.c new file +2
-0+ +typedef DWORD NTSTATUS ;
Is this safe to have outside "#ifdef GIT_WINDOWS_NATIVE"?
t/helper/test-drop-caches.c line 13
Quoted 9 lines
t/helper/test-drop-caches.c new file +9
-0+ +#ifdef GIT_WINDOWS_NATIVE +#include <tchar.h> + +#define STATUS_SUCCESS (0x00000000L) +#define STATUS_PRIVILEGE_NOT_HELD (0xC0000061L) + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemMemoryListInformation = 80, // 80, q: SYSTEM_MEMORY_LIST_INFORMATION; s: SYSTEM_MEMORY_LIST_COMMAND (requires SeProfileSingleProcessPrivilege)
I would have said "Please avoid // comment in this codebase unless we know we only use the compilers that grok it". This particular one may be OK, as it is inside GIT_WINDOWS_NATIVE and I assume everybody on that platform uses recent GCC (or VStudio groks it I guess)?
t/helper/test-drop-caches.c line 18
Quoted 5 lines
t/helper/test-drop-caches.c new file +5
-0+}SYSTEM_INFORMATION_CLASS ; + +// private +typedef enum _SYSTEM_MEMORY_LIST_COMMAND +{
Style: '{' comes at the end of the previous line, with a single SP immediately before it, unless it is the beginning of the function body.
What you did for _SYSTEM_INFORMATION_CLASS above is correct.
t/helper/test-drop-caches.c line 25
Quoted 7 lines
t/helper/test-drop-caches.c new file +7
-0+ MemoryCaptureAccessedBits, + MemoryCaptureAndResetAccessedBits, + MemoryEmptyWorkingSets, + MemoryFlushModifiedList, + MemoryPurgeStandbyList, + MemoryPurgeLowPriorityStandbyList, + MemoryCommandMax
Style: avoid CamelCase.
Quoted 4 lines
t/helper/test-drop-caches.c new file +4
-0+}SYSTEM_MEMORY_LIST_COMMAND ; + +BOOL GetPrivilege (HANDLE TokenHandle,LPCSTR lpName,int flags) +{
t/helper/test-drop-caches.c line 39
Quoted 10 lines
t/helper/test-drop-caches.c new file +10
-0+BOOL bResult; +DWORD dwBufferLength; +LUID luid; +TOKEN_PRIVILEGES tpPreviousState; +TOKEN_PRIVILEGES tpNewState; + + dwBufferLength =16 ; + bResult =LookupPrivilegeValueA (0 , lpName, &luid); +if (bResult) + {
Style: '{' comes at the end of the previous line, with a single SP immediately before it, unless it is the beginning of the function body.
Quoted 34 lines
t/helper/test-drop-caches.c new file +34
-0+ tpNewState.PrivilegeCount =1 ; + tpNewState.Privileges[0 ].Luid = luid; + tpNewState.Privileges[0 ].Attributes =0 ; + bResult =AdjustTokenPrivileges (TokenHandle,0 , &tpNewState, (DWORD )((LPBYTE )&(tpNewState.Privileges[1 ]) - (LPBYTE )&tpNewState), &tpPreviousState, &dwBufferLength); +if (bResult) + { + tpPreviousState.PrivilegeCount =1 ; + tpPreviousState.Privileges[0 ].Luid = luid; + tpPreviousState.Privileges[0 ].Attributes = flags !=0 ?2 :0 ; + bResult =AdjustTokenPrivileges (TokenHandle,0 , &tpPreviousState, dwBufferLength,0 ,0 ); + } + } +return bResult; +} +#endif + +int cmd_main (int argc,const char **argv) +{ +NTSTATUS status =1 ; +#ifdef GIT_WINDOWS_NATIVE +HANDLE hProcess =GetCurrentProcess (); +HANDLE hToken; +if (!OpenProcessToken (hProcess,TOKEN_QUERY |TOKEN_ADJUST_PRIVILEGES , &hToken)) + { +_ftprintf (stderr,_T ("Can't open current process token\n" )); +return 1 ; + } + +if (!GetPrivilege (hToken,"SeProfileSingleProcessPrivilege" ,1 )) + { +_ftprintf (stderr,_T ("Can't get SeProfileSingleProcessPrivilege\n" )); +return 1 ; + } +
t/helper/test-drop-caches.c line 83
Quoted 10 lines
t/helper/test-drop-caches.c new file +10
-0+CloseHandle (hToken); + +HMODULE ntdll =LoadLibrary (_T ("ntdll.dll" )); +if (!ntdll) + { +_ftprintf (stderr,_T ("Can't load ntdll.dll, wrong Windows version?\n" )); +return 1 ; + } + +NTSTATUS (WINAPI *NtSetSystemInformation)(INT ,PVOID ,ULONG ) = (NTSTATUS (WINAPI *)(INT ,PVOID ,ULONG ))GetProcAddress (ntdll,"NtSetSystemInformation" );
Is this "decl-after-stmt"? Avoid it.
Quoted 14 lines
t/helper/test-drop-caches.c new file +14
-0+if (!NtSetSystemInformation) + { +_ftprintf (stderr,_T ("Can't get function addresses, wrong Windows version?\n" )); +return 1 ; + } + +SYSTEM_MEMORY_LIST_COMMAND command = MemoryPurgeStandbyList; + status =NtSetSystemInformation ( + SystemMemoryListInformation, + &command, +sizeof (SYSTEM_MEMORY_LIST_COMMAND ) + ); +if (status ==STATUS_PRIVILEGE_NOT_HELD ) + {
t/helper/test-drop-caches.c line 107
Quoted 10 lines
t/helper/test-drop-caches.c new file +10
-0+ _ftprintf(stderr, _T("Insufficient privileges to execute the memory list command")); + } +else if (status !=STATUS_SUCCESS ) + { +_ftprintf (stderr,_T ("Unable to execute the memory list command %lX" ), status); + } +#endif + +return status; +}
So status is initialized to 1 and anybody without GIT_WINDOWS_NATIVE unconditionally get exit(1)?
Given that 'status' is the return value of this function that returns 'int', I wonder if we need NTSTATUS type here.
Having said all that, I think you are using this ONLY on windows; perhaps it is better to drop #ifdef GIT_WINDOWS_NATIVE from all of the above and arrange Makefile to build test-drop-cache only on that platform, or something?
Quoted 7 lines
t/perf/p7519-fsmonitor.sh new file +1
-0@@ -0,0 +1,161 @@ +#!/bin/sh
t/perf/p7519-fsmonitor.sh line 11
Quoted 10 lines
t/perf/p7519-fsmonitor.sh new file +10
-0+ +test_description="Test core.fsmonitor" + +. ./perf-lib.sh + +# This has to be run with GIT_PERF_REPEAT_COUNT=1 to generate valid results. +# Otherwise the caching that happens for the nth run will negate the validity +# of the comparisons. +if [ "$GIT_PERF_REPEAT_COUNT" -ne 1 ] +then
Style:
if test "$GIT_PERF_REPEAT_COUNT" -ne 1
then
t/perf/p7519-fsmonitor.sh line 72
Quoted 6 lines
t/perf/p7519-fsmonitor.sh new file +6
-0+ ... +test_expect_success "setup" ' +... + # Hook scaffolding + mkdir .git/hooks && + cp ../../../templates/hooks--query-fsmonitor.sample .git/hooks/query-fsmonitor &&
Does this assume $TRASH_DIRECTORY must be in $TEST_DIRECTORY/perf/? Shouldn't t/perf/p[0-9][0-9][0-9][0-9]-*.sh scripts be capable of running with the --root=<ramdisk> option? Perhaps take the copy relative to $TEST_DIRECTORY?
Re: [PATCH v5 7/7] fsmonitor: add a performance test
Quoted 13 lines · Junio C Hamano
t/helper/test-drop-caches.c line 2
Quoted 8 lines · Ben Peart
t/helper/test-drop-caches.c new file +2
-0@@ -0,0 +1,107 @@ +#include "git-compat-util.h" +#include <stdio.h> I thought compat-util should already include stdio?
Quoted 4 lines
t/helper/test-drop-caches.c line 4
Quoted 2 lines
t/helper/test-drop-caches.c new file +2
-0+ +typedef DWORD NTSTATUS ;Is this safe to have outside "#ifdef
GIT_WINDOWS_NATIVE"?
I think it's safe but it isn't required. I remove it to be sure.
Quoted 17 lines
t/helper/test-drop-caches.c line 13
Quoted 10 lines
t/helper/test-drop-caches.c new file +9
-0+ +#ifdef GIT_WINDOWS_NATIVE +#include <tchar.h> + +#define STATUS_SUCCESS (0x00000000L) +#define STATUS_PRIVILEGE_NOT_HELD (0xC0000061L) + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemMemoryListInformation = 80, // 80, q: SYSTEM_MEMORY_LIST_INFORMATION; s: SYSTEM_MEMORY_LIST_COMMAND (requires SeProfileSingleProcessPrivilege)I would have said "Please avoid // comment in this codebase unless we know we only use the compilers that grok it". This particular one may be OK, as it is inside
GIT_WINDOWS_NATIVEand I assume everybody on that platform uses recent GCC (or VStudio groks it I guess)?
I'll remove them to be sure.
Quoted 9 lines
t/helper/test-drop-caches.c line 18
Quoted 5 lines
t/helper/test-drop-caches.c new file +5
-0+}SYSTEM_INFORMATION_CLASS ; + +// private +typedef enum _SYSTEM_MEMORY_LIST_COMMAND +{Style: '{' comes at the end of the previous line, with a single SP immediately before it, unless it is the beginning of the function body.
Old habits. :) Started writing Windows code and adopted their coding style without even thinking about it. I'll 'gitify' the style as much as possible. This should address the various style comments below.
Quoted 16 lines
What you did for
_SYSTEM_INFORMATION_CLASSabove is correct.t/helper/test-drop-caches.c line 25
Quoted 7 lines
t/helper/test-drop-caches.c new file +7
-0+ MemoryCaptureAccessedBits, + MemoryCaptureAndResetAccessedBits, + MemoryEmptyWorkingSets, + MemoryFlushModifiedList, + MemoryPurgeStandbyList, + MemoryPurgeLowPriorityStandbyList, + MemoryCommandMaxStyle: avoid CamelCase.
So status is initialized to 1 and anybody without
GIT_WINDOWS_NATIVEunconditionally get exit(1)?
Quoted 8 lines
Given that 'status' is the return value of this function that returns 'int', I wonder if we need NTSTATUS type here.
Having said all that, I think you are using this ONLY on windows; perhaps it is better to drop #ifdef
GIT_WINDOWS_NATIVEfrom all of the above and arrangeMakefileto build test-drop-cache only on that platform, or something?
I didn't find any other examples of Windows only tools. I'll update the #ifdef to properly dump the file system cache on Linux as well and only error out on other platforms.
Quoted 24 lines
Quoted 8 lines
t/perf/p7519-fsmonitor.sh new file +1
-0@@ -0,0 +1,161 @@ +#!/bin/sht/perf/p7519-fsmonitor.sh line 11
Quoted 10 lines
t/perf/p7519-fsmonitor.sh new file +10
-0+ +test_description="Test core.fsmonitor" + +. ./perf-lib.sh + +# This has to be run with GIT_PERF_REPEAT_COUNT=1 to generate valid results. +# Otherwise the caching that happens for the nth run will negate the validity +# of the comparisons. +if [ "$GIT_PERF_REPEAT_COUNT" -ne 1 ] +thenStyle:
if test "$GIT_PERF_REPEAT_COUNT" -ne 1
then
Quoted 12 lines
t/perf/p7519-fsmonitor.sh line 72
Quoted 6 lines
t/perf/p7519-fsmonitor.sh new file +6
-0+ ... +test_expect_success "setup" ' +... + # Hook scaffolding + mkdir .git/hooks && + cp ../../../templates/hooks--query-fsmonitor.sample .git/hooks/query-fsmonitor &&Does this assume $TRASH_DIRECTORY must be in $TEST_DIRECTORY/perf/? Shouldn't t/perf/p[0-9][0-9][0-9][0-9]-*.sh scripts be capable of running with the --root=<ramdisk> option? Perhaps take the copy relative to $TEST_DIRECTORY?
I'll copy it relative to $TEST_DIRECTORY in the next iteration.
Re: [PATCH v5 7/7] fsmonitor: add a performance test
Quoted 8 lines · Ben Peart
Quoted 4 lines
Having said all that, I think you are using this ONLY on windows; perhaps it is better to drop #ifdef
GIT_WINDOWS_NATIVEfrom all of the above and arrangeMakefileto build test-drop-cache only on that platform, or something?I didn't find any other examples of Windows only tools. I'll update the #ifdef to properly dump the file system cache on Linux as well and only error out on other platforms.
If this will become Windows-only, then I have no problem with platform specfic typedef ;-) I have no problem with CamelCase, either, as that follows the local convention on the platform (similar to those in compat/* that are only for Windows).
Having said all that.
Another approach is to build this helper on all platforms, with sections protected by "#ifdef LINUX", "#ifdef WINDOWS", etc. That way, the platform detection and switching between running this program and echoing something into /proc filesystem performed in p7519 can be removed (this test-helper program will be responsible to implement that logic instead). When p7519 wants to drop the filesystem cache, regardless of the platform, it can call this test-helper without having to know how the filesystem cache is dropped.
I do not have strong preference either way, but I have a slight suspicion that the "another approach" above may give us a better result. For one thing, the test-helper can be reused in new perf scripts people will write in the future.
Thanks.
Re: [PATCH v5 7/7] fsmonitor: add a performance test
Quoted 18 lines · Junio C Hamano
Quoted 8 lines · Ben Peart
Quoted 4 lines
Having said all that, I think you are using this ONLY on windows; perhaps it is better to drop #ifdef
GIT_WINDOWS_NATIVEfrom all of the above and arrangeMakefileto build test-drop-cache only on that platform, or something?I didn't find any other examples of Windows only tools. I'll update the #ifdef to properly dump the file system cache on Linux as well and only error out on other platforms.
If this will become Windows-only, then I have no problem with platform specfic typedef ;-) I have no problem with CamelCase, either, as that follows the local convention on the platform (similar to those in compat/* that are only for Windows).
Having said all that.
Quoted 10 lines
Another approach is to build this helper on all platforms, with sections protected by "#ifdef LINUX", "#ifdef WINDOWS", etc. That way, the platform detection and switching between running this program and echoing something into /proc filesystem performed in p7519 can be removed (this test-helper program will be responsible to implement that logic instead). When p7519 wants to drop the filesystem cache, regardless of the platform, it can call this test-helper without having to know how the filesystem cache is dropped.
I'll take a cut at doing this but it is obviously very platform dependent and I'm unfamiliar with platforms other than Windows.
For everything other than Windows, my implementation will be calling "system" for external utilities based on what I can find on the web. Oh, and I have no way to test it other than on Windows so could use some review/testing from others. :)
Quoted 7 lines
I do not have strong preference either way, but I have a slight suspicion that the "another approach" above may give us a better result. For one thing, the test-helper can be reused in new perf scripts people will write in the future.
Thanks.
Re: [PATCH v5 7/7] fsmonitor: add a performance test
Quoted 12 lines · Ben Peart
Quoted 11 lines · Junio C Hamano
Quoted 8 lines · Ben Peart
Quoted 4 lines
Having said all that, I think you are using this ONLY on windows; perhaps it is better to drop #ifdef
GIT_WINDOWS_NATIVEfrom all of the above and arrangeMakefileto build test-drop-cache only on that platform, or something?I didn't find any other examples of Windows only tools. I'll update the #ifdef to properly dump the file system cache on Linux as well and only error out on other platforms.
Quoted 8 lines
Quoted 8 lines
If this will become Windows-only, then I have no problem with platform specfic typedef ;-) I have no problem with CamelCase, either, as that follows the local convention on the platform (similar to those in compat/* that are only for Windows).
Having said all that.
Another approach is to build this helper on all platforms, ...
... and having said all that, I think it is perfectly fine to do such a clean-up long after the series gets more exposure to wider audiences as a follow-up patch. Let's get the primary part that affects people's everyday use of Git right and then worry about the test details later.
A quick show of hands to the list audiences. How many of you guys actually tried this series on 'pu' and checked to see its performance (and correctness ;-) characteristics?
Do you folks like it? Rather not have such complexity in the core part of the system? A good first step to start adding more performance improvements? No opinion?
Re: [PATCH v5 7/7] fsmonitor: add a performance test
Quoted 29 lines · Junio C Hamano
Quoted 12 lines · Ben Peart
Quoted 11 lines · Junio C Hamano
Quoted 8 lines · Ben Peart
Quoted 4 lines
Having said all that, I think you are using this ONLY on windows; perhaps it is better to drop #ifdef
GIT_WINDOWS_NATIVEfrom all of the above and arrangeMakefileto build test-drop-cache only on that platform, or something?I didn't find any other examples of Windows only tools. I'll update the #ifdef to properly dump the file system cache on Linux as well and only error out on other platforms.
Quoted 8 lines
Quoted 8 lines
If this will become Windows-only, then I have no problem with platform specfic typedef ;-) I have no problem with CamelCase, either, as that follows the local convention on the platform (similar to those in compat/* that are only for Windows).
Having said all that.
Another approach is to build this helper on all platforms, ...
... and having said all that, I think it is perfectly fine to do such a clean-up long after the series gets more exposure to wider audiences as a follow-up patch. Let's get the primary part that affects people's everyday use of Git right and then worry about the test details later.
Quoted 3 lines
A quick show of hands to the list audiences. How many of you guys actually tried this series on 'pu' and checked to see its performance (and correctness ;-) characteristics?
TLDR: the current version isn't correct.
One of the things I did was hack up the test script to enable running all the tests with fsmonitor enabled. I found a number of bugs in Watchman on Windows and have been working with Wez to get them fixed.
Just last week Watchman got to the point where I could run the complete git test suite. As a result, I found that fsmonitor is overly aggressive in marking things with ce_mark_uptodate. Submodules are currently broken as are commands that pass "--ignore-missing."
I started reworking the logic to fix these bugs and realized I was duplicating the set of tests that already exist in preload_thread. I'm currently working on integrating the logic into preload_thread so that both options can be used in combination and so I can avoid doing the (nearly identical) loop twice.
Quoted 6 lines
Do you folks like it? Rather not have such complexity in the core part of the system? A good first step to start adding more performance improvements? No opinion?
RE: [PATCH v5 7/7] fsmonitor: add a performance test
In reply to Junio C HamanoQuoted 38 lines
-----Original Message-----
From: Junio C Hamano [mailto:[email protected]] On Behalf Of Junio C Hamano
Sent: Friday, July 7, 2017 2:35 PM
To: Ben Peart <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; David Turner <[email protected]>; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH v5 7/7] fsmonitor: add a performance testQuoted 12 lines · Ben Peart
Quoted 11 lines · Junio C Hamano
Quoted 8 lines · Ben Peart
Quoted 4 lines
Having said all that, I think you are using this ONLY on windows; perhaps it is better to drop #ifdef
GIT_WINDOWS_NATIVEfrom all of the above and arrangeMakefileto build test-drop-cache only on that platform, or something?I didn't find any other examples of Windows only tools. I'll update the #ifdef to properly dump the file system cache on Linux as well and only error out on other platforms.
Quoted 8 lines
Quoted 8 lines
If this will become Windows-only, then I have no problem with platform specfic typedef ;-) I have no problem with CamelCase, either, as that follows the local convention on the platform (similar to those in compat/* that are only for Windows).
Having said all that.
Another approach is to build this helper on all platforms, ...
... and having said all that, I think it is perfectly fine to do such a clean-up long after the series gets more exposure to wider audiences as a follow-up patch. Let's get the primary part that affects people's everyday use of Git right and then worry about the test details later.
Quoted 7 lines
A quick show of hands to the list audiences. How many of you guys actually tried this series on 'pu' and checked to see its performance (and correctness ;-) characteristics?
Do you folks like it? Rather not have such complexity in the core part of the system? A good first step to start adding more performance improvements? No opinion?
I have not had the chance to test the latest version out yet. The idea, broadly, seems sound, but as Ben notes in a later mail, the details are important. Since he's going to re-roll with more interesting invalidation logic, I'll wait to try it again until a new version is available.
Re: [PATCH v5 7/7] fsmonitor: add a performance test
In reply to Junio C HamanoQuoted 29 lines · Junio C Hamano
Quoted 12 lines · Ben Peart
Quoted 11 lines · Junio C Hamano
Quoted 8 lines · Ben Peart
Quoted 4 lines
Having said all that, I think you are using this ONLY on windows; perhaps it is better to drop #ifdef
GIT_WINDOWS_NATIVEfrom all of the above and arrangeMakefileto build test-drop-cache only on that platform, or something?I didn't find any other examples of Windows only tools. I'll update the #ifdef to properly dump the file system cache on Linux as well and only error out on other platforms.
Quoted 8 lines
Quoted 8 lines
If this will become Windows-only, then I have no problem with platform specfic typedef ;-) I have no problem with CamelCase, either, as that follows the local convention on the platform (similar to those in compat/* that are only for Windows).
Having said all that.
Another approach is to build this helper on all platforms, ...
... and having said all that, I think it is perfectly fine to do such a clean-up long after the series gets more exposure to wider audiences as a follow-up patch. Let's get the primary part that affects people's everyday use of Git right and then worry about the test details later.
Quoted 3 lines
A quick show of hands to the list audiences. How many of you guys actually tried this series on 'pu' and checked to see its performance (and correctness ;-) characteristics?
As you can guess from my previous replies to this thread (and the previous version of this patch series), I lightly tried it and checked its performance for Booking.com.
Quoted 3 lines
Do you folks like it? Rather not have such complexity in the core part of the system? A good first step to start adding more performance improvements? No opinion?
I already gave my opinion which I think is shared with Ævar. In short I don't think it should be a hook, as that limits the performance and is not necessary, but it is going in the right direction.
Quoted 1 line · Ben Peart
Changes from V4 include:
...
I took a look at this patch series except the last patch ([PATCH v5 7/7] fsmonitor: add a performance test) as Junio reviewed it already, and had only a few comments on patches 3/7 and 4/7.
I am still not convinced by the discussions following v2
(http://public-inbox.org/git/[email protected]/)
about using a hook instead of for example a "core.fsmonitorcommand".
I think using a hook is not necessary and might not be a good match for later optimizations. For example people might want to use a library or some OS specific system calls to do what the hook does.
AEvar previously reported some not so great performance numbers on some big Booking.com boxes with a big monorepo and it seems that using taskset for example to make sure that the hook is run on the same CPU improves these numbers significantly. So avoiding to run a separate process can be important in some cases.
Quoted 16 lines · Christian Couder
Quoted 1 line · Ben Peart
Changes from V4 include:
...
I took a look at this patch series except the last patch ([PATCH v5 7/7] fsmonitor: add a performance test) as Junio reviewed it already, and had only a few comments on patches 3/7 and 4/7.
I am still not convinced by the discussions following v2
(http://public-inbox.org/git/[email protected]/)
about using a hook instead of for example a "core.fsmonitorcommand".I think using a hook is not necessary and might not be a good match for later optimizations. For example people might want to use a library or some OS specific system calls to do what the hook does.
Quoted 6 lines
AEvar previously reported some not so great performance numbers on some big Booking.com boxes with a big monorepo and it seems that using taskset for example to make sure that the hook is run on the same CPU improves these numbers significantly. So avoiding to run a separate process can be important in some cases.
Using a hook is the only pattern I've seen in git that provides a way to enable OS specific calls. I used a hook so that different file monitoring services could be plugged in depending on the OS or tools available. The Watchman integration script was mostly intended as a sample that could be used where Watchman is available and works well.
I had not heard about the taskset issues you mention above. If there is something else that can be done to make this work better, please let me know the details.
If I'm understanding you correctly, you are suggesting that someone should be able to configure a setting (core.fsmonitorcommand) that gives a custom command line that would be run instead of running the query-fsmonitor hook.
I'm not entirely sure how that should work. There are command line options that need to be passed (currently the interface version as well as the current clock in nanoseconds). How would those passed when using the custom command?
Is it OK to just append them to the given command line? Does there need to be some substitution token to indicate where they should be inserted (ie "mycustomcommand --custom --options %version% --more-options %timestamp%"). Are there any other tokens that should be supported (ie PID or processor mask?).
Is there a design pattern already used somewhere in git that I can follow or is this all blazing a new trail?
Thanks for continuing to look into this. Feedback is good!
Quoted 31 lines · Ben Peart
Quoted 17 lines · Christian Couder
Quoted 1 line · Ben Peart
Changes from V4 include:
...
I took a look at this patch series except the last patch ([PATCH v5 7/7] fsmonitor: add a performance test) as Junio reviewed it already, and had only a few comments on patches 3/7 and 4/7.
I am still not convinced by the discussions following v2
(http://public-inbox.org/git/[email protected]/)about using a hook instead of for example a "core.fsmonitorcommand".
I think using a hook is not necessary and might not be a good match for later optimizations. For example people might want to use a library or some OS specific system calls to do what the hook does.
Quoted 6 lines
AEvar previously reported some not so great performance numbers on some big Booking.com boxes with a big monorepo and it seems that using taskset for example to make sure that the hook is run on the same CPU improves these numbers significantly. So avoiding to run a separate process can be important in some cases.
Using a hook is the only pattern I've seen in git that provides a way to enable OS specific calls. I used a hook so that different file monitoring services could be plugged in depending on the OS or tools available. The Watchman integration script was mostly intended as a sample that could be used where Watchman is available and works well.
I had not heard about the taskset issues you mention above. If there is something else that can be done to make this work better, please let me know the details.
Quoted 8 lines
If I'm understanding you correctly, you are suggesting that someone should be able to configure a setting (core.fsmonitorcommand) that gives a custom command line that would be run instead of running the query-fsmonitor hook.
I'm not entirely sure how that should work. There are command line options that need to be passed (currently the interface version as well as the current clock in nanoseconds). How would those passed when using the custom command?
Is it OK to just append them to the given command line? Does there need to be some substitution token to indicate where they should be inserted (ie "mycustomcommand --custom --options %version% --more-options %timestamp%"). Are there any other tokens that should be supported (ie PID or processor mask?).
Is there a design pattern already used somewhere in git that I can follow or is this all blazing a new trail?
My co-worker reminded me about git difftool - is this what you had in mind?
Quoted 3 lines
Thanks for continuing to look into this. Feedback is good!
v6 cover Fast git status via a file system watcher
In reply to Ben PeartThis is a fairly significant rewrite since V5. The big changes include:
Multiple functions including preload-index(), ie_match_stat(), and refresh_cache_ent() have been updated to honor the CE_FSMONITOR_VALID bit following the same pattern as skip_worktree and CE_VALID. As a result, performance improvements apply to all git commands that would otherwise have had to scan the entire working directory.
core.fsmonitor is now a registered command (instead of a hook) to provide additional flexibility. It is called when needed to ensure the state of the index is up-to-date.
The Watchman integration script is now entirely written in perl to minimize spawning additional helper commands. This along with the other changes have helped reduce the overhead and made the extension applicable to more (ie smaller) repos.
There are additional opportunities for performance improvements but I wanted to get this version out there and then build on it as the foundation. Some potential examples of future patches include:
- call the integration script on a background thread so that it can execute in parallel.
- optimize traverse trees by pruning out entire branches that do not contain any changes.
Other optimizations likely exist where knowledge that files have not changed can be used to short circuit some of the normal workflow.
Performance
===========
With the various enhancements, performance has been improved especially for smaller repos. The included perf test compares status times without fsmonitor to those with fsmonitor using the provided Watchman integration script.
Due to the overhead of calling out to Watchman, on very small repos (<10K files) the overhead exceeds the savings. Once repos hit 10K files the savings kick in and for repos beyond that, the savings are dramatic.
Test with 10,000 files this tree
------------------------------------------------------------------------
7519.2: status (fsmonitor=.git/hooks/fsmonitor-watchman) 0.35(0.03+0.04) 7519.3: status -uno (fsmonitor=.git/hooks/fsmonitor-watchman) 0.37(0.00+0.09) 7519.4: status -uall (fsmonitor=.git/hooks/fsmonitor-watchman) 0.43(0.03+0.06) 7519.6: status (fsmonitor=) 0.45(0.00+0.07) 7519.7: status -uno (fsmonitor=) 0.40(0.03+0.07) 7519.8: status -uall (fsmonitor=) 0.44(0.04+0.04)
Test with 100,000 files this tree
------------------------------------------------------------------------
7519.2: status (fsmonitor=.git/hooks/fsmonitor-watchman) 0.33(0.01+0.03) 7519.3: status -uno (fsmonitor=.git/hooks/fsmonitor-watchman) 0.36(0.00+0.06) 7519.4: status -uall (fsmonitor=.git/hooks/fsmonitor-watchman) 0.93(0.00+0.07) 7519.6: status (fsmonitor=) 2.66(0.04+0.03) 7519.7: status -uno (fsmonitor=) 2.44(0.01+0.06) 7519.8: status -uall (fsmonitor=) 2.94(0.03+0.07)
Test with 1,000,000 files this tree
---------------------------------------------------------------------------------
7519.2: status (fsmonitor=.git/hooks/fsmonitor-watchman) 1.45(0.00+0.06) 7519.3: status -uno (fsmonitor=.git/hooks/fsmonitor-watchman) 0.88(0.01+0.04) 7519.4: status -uall (fsmonitor=.git/hooks/fsmonitor-watchman) 6.14(0.03+0.04) 7519.6: status (fsmonitor=) 25.91(0.04+0.06) 7519.7: status -uno (fsmonitor=) 23.96(0.04+0.03) 7519.8: status -uall (fsmonitor=) 28.81(0.00+0.07)
Note: all numbers above are with a warm disk cache on a fast SSD, real world performance numbers are often dramatically better as fsmonitor can eliminate all the file IO to lstat every file and then traverse the working directory looking for untracked files. For example, a cold status without fsmonitor on a HDD with 1M files takes 1m22.774s
$ time git -c core.fsmonitor= status On branch p0006-ballast
It took 2.09 seconds to enumerate untracked files. 'status -uno' may speed it up, but you have to be careful not to forget to add new files yourself (see 'git help status').
nothing to commit, working tree clean
real 1m22.774s user 0m0.000s sys 0m0.000s
Ben Peart (12):
bswap: add 64 bit endianness helper get_be64
preload-index: add override to enable testing preload-index
update-index: add a new --force-write-index option
fsmonitor: teach git to optionally utilize a file system monitor to
speed up detecting new or changed files.
fsmonitor: add documentation for the fsmonitor extension.
ls-files: Add support in ls-files to display the fsmonitor valid bit
update-index: add fsmonitor support to update-index
fsmonitor: add a test tool to dump the index extension
split-index: disable the fsmonitor extension when running the split
index test
fsmonitor: add test cases for fsmonitor extension
fsmonitor: add a sample integration script for Watchman
fsmonitor: add a performance test
Documentation/config.txt | 6 + Documentation/githooks.txt | 23 +++ Documentation/technical/index-format.txt | 19 +++ Makefile | 3 + apply.c | 2 +- builtin/ls-files.c | 8 +- builtin/update-index.c | 26 ++- cache.h | 10 +- compat/bswap.h | 22 +++ config.c | 14 ++ config.h | 1 + diff-lib.c | 2 + dir.c | 27 +-- dir.h | 2 + entry.c | 4 +- environment.c | 1 + fsmonitor.c | 253 ++++++++++++++++++++++++++++ fsmonitor.h | 61 +++++++ preload-index.c | 8 +- read-cache.c | 49 +++++- submodule.c | 2 +- t/helper/.gitignore | 1 + t/helper/test-drop-caches.c | 161 ++++++++++++++++++ t/helper/test-dump-fsmonitor.c | 21 +++ t/perf/p7519-fsmonitor.sh | 184 ++++++++++++++++++++ t/t1700-split-index.sh | 1 + t/t7519-status-fsmonitor.sh | 259 +++++++++++++++++++++++++++++ t/t7519/fsmonitor-all | 23 +++ t/t7519/fsmonitor-none | 21 +++ t/t7519/fsmonitor-watchman | 128 ++++++++++++++ templates/hooks--fsmonitor-watchman.sample | 119 +++++++++++++ unpack-trees.c | 8 +- 32 files changed, 1440 insertions(+), 29 deletions(-) create mode 100644 fsmonitor.c create mode 100644 fsmonitor.h create mode 100644 t/helper/test-drop-caches.c create mode 100644 t/helper/test-dump-fsmonitor.c create mode 100755 t/perf/p7519-fsmonitor.sh create mode 100755 t/t7519-status-fsmonitor.sh create mode 100755 t/t7519/fsmonitor-all create mode 100755 t/t7519/fsmonitor-none create mode 100755 t/t7519/fsmonitor-watchman create mode 100755 templates/hooks--fsmonitor-watchman.sample
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
PATCH v6 1/12 bswap: add 64 bit endianness helper get_be64
Add a new get_be64 macro to enable 64 bit endian conversions on memory that may or may not be aligned.
--- compat/bswap.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
compat/bswap.h +22 -0
@@ -158,7 +158,9 @@ static inline uint64_t git_bswap64(uint64_t x)#define get_be16 (p) ntohs(*(unsigned short *)(p))#define get_be32 (p) ntohl(*(unsigned int *)(p)) +#define get_be64 (p) ntohll(*(uint64_t *)(p))#define put_be32 (p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) +#define put_be64 (p, v) do { *(uint64_t *)(p) = htonll(v); } while (0)#else
@@ -178,6 +180,13 @@ static inline uint32_t get_be32(const void *ptr) (uint32_t )p[3 ] <<0 ; } +static inline uint64_t get_be64 (const void *ptr) +{ +const unsigned char *p = ptr; +return (uint64_t )get_be32 (p[0 ]) <<32 | + (uint64_t )get_be32 (p[4 ]) <<0 ; +} +static inline void put_be32 (void *ptr,uint32_t value) {unsigned char *p = ptr;
@@ -187,4 +196,17 @@ static inline void put_be32(void *ptr, uint32_t value) p[3] = value >> 0; } +static inline void put_be64 (void *ptr,uint64_t value) +{ +unsigned char *p = ptr; + p[0 ] = value >>56 ; + p[1 ] = value >>48 ; + p[2 ] = value >>40 ; + p[3 ] = value >>32 ; + p[4 ] = value >>24 ; + p[5 ] = value >>16 ; + p[6 ] = value >>8 ; + p[7 ] = value >>0 ; +} +#endif
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
PATCH v6 2/12 preload-index: add override to enable testing preload-index
In reply to Ben PeartPreload index doesn't run unless it has a minimum number of 1000 files. To enable running tests with fewer files, add an environment variable (GIT_FORCE_PRELOAD_TEST) which will override that minimum and set it to 2.
--- preload-index.c | 2 ++ 1 file changed, 2 insertions(+)
preload-index.c +2 -0
@@ -79,6 +79,8 @@ static void preload_index(struct index_state *index,return ; threads = index->cache_nr /THREAD_COST ; +if ((index->cache_nr >1 ) && (threads <2 ) &&getenv ("GIT_FORCE_PRELOAD_TEST" )) + threads =2 ;if (threads <2 )return ;if (threads >MAX_PARALLEL )
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
PATCH v6 3/12 update-index: add a new --force-write-index option
In reply to Ben PeartAt times, it makes sense to avoid the cost of writing out the index when the only changes can easily be recomputed on demand. This causes problems when trying to write test cases to verify that state as they can't guarantee the state has been persisted to disk.
Add a new option (--force-write-index) to update-index that will ensure the index is written out even if the cache_changed flag is not set.
--- builtin/update-index.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
builtin/update-index.c +4 -1
@@ -915,6 +915,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)struct refresh_params refresh_args = {0 , &has_errors};int lock_error =0 ;int split_index =-1 ; +int force_write =0 ;struct lock_file *lock_file;struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;
@@ -1006,6 +1007,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("test if the filesystem supports untracked cache"), UC_TEST),OPT_SET_INT (0 ,"force-untracked-cache" , &untracked_cache,N_ ("enable untracked cache without testing the filesystem" ),UC_FORCE ), +OPT_SET_INT (0 ,"force-write-index" , &force_write, +N_ ("write out the index even if is not flagged as changed" ),1 ), OPT_END() };
@@ -1147,7 +1150,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)die ("BUG: bad untracked_cache value: %d" , untracked_cache); } -if (active_cache_changed) { +if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )exit (128 );
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
PATCH v6 4/12 fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
In reply to Ben PeartWhen the index is read from disk, the fsmonitor index extension is used to flag the last known potentially dirty index entries. The registered core.fsmonitor command is called with the time the index was last updated and returns the list of files changed since that time. This list is used to flag any additional dirty cache entries and untracked cache directories.
We can then use this valid state to speed up preload_index(), ie_match_stat(), and refresh_cache_ent() as they do not need to lstat() files to detect potential changes for those entries marked CE_FSMONITOR_VALID.
In addition, if the untracked cache is turned on valid_cached_dir() can skip checking directories for new or changed files as fsmonitor will invalidate the cache only for those directories that have been identified as having potential changes.
To keep the CE_FSMONITOR_VALID state accurate during git operations; when git updates a cache entry to match the current state on disk, it will now set the CE_FSMONITOR_VALID bit.
Inversely, anytime git changes a cache entry, the CE_FSMONITOR_VALID bit is cleared and the corresponding untracked cache directory is marked invalid.
--- Makefile | 1 + apply.c | 2 +- builtin/update-index.c | 2 + cache.h | 10 +- config.c | 14 +++ config.h | 1 + diff-lib.c | 2 + dir.c | 27 ++++-- dir.h | 2 + entry.c | 4 +- environment.c | 1 + fsmonitor.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++++ fsmonitor.h | 61 ++++++++++++ preload-index.c | 6 +- read-cache.c | 49 ++++++++-- submodule.c | 2 +- unpack-trees.c | 8 +- 17 files changed, 419 insertions(+), 26 deletions(-) create mode 100644 fsmonitor.c create mode 100644 fsmonitor.h
Makefile +1 -0
@@ -786,6 +786,7 @@ LIB_OBJS += ewah/ewah_rlw.o LIB_OBJS += exec_cmd.o LIB_OBJS += fetch-pack.o LIB_OBJS += fsck.o +LIB_OBJS += fsmonitor.o LIB_OBJS += gettext.o LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o
apply.c +1 -1
@@ -3399,7 +3399,7 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st)return -1 ;return 0 ; } -return ce_match_stat (ce, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); +return ce_match_stat (ce, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); }#define SUBMODULE_PATCH_WITHOUT_INDEX 1
builtin/update-index.c +2 -0
@@ -16,6 +16,7 @@#include "pathspec.h" #include "dir.h" #include "split-index.h" +#include "fsmonitor.h" /* * Default to not allowing changes to the list of files. The
@@ -233,6 +234,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)else active_cache[pos]->ce_flags &= ~flag; active_cache[pos]->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (&the_index, active_cache[pos]);cache_tree_invalidate_path (&the_index, path); active_cache_changed |=CE_ENTRY_CHANGED ;return 0 ;
cache.h +8 -2
@@ -203,6 +203,7 @@ struct cache_entry {#define CE_ADDED (1 << 19)#define CE_HASHED (1 << 20) +#define CE_FSMONITOR_VALID (1 << 21)#define CE_WT_REMOVE (1 << 22)/* remove in work directory */ #define CE_CONFLICTED (1 << 23)
@@ -326,6 +327,7 @@ static inline unsigned int canon_mode(unsigned int mode)#define CACHE_TREE_CHANGED (1 << 5)#define SPLIT_INDEX_ORDERED (1 << 6)#define UNTRACKED_CHANGED (1 << 7) +#define FSMONITOR_CHANGED (1 << 8)struct split_index ;struct untracked_cache ;
@@ -344,6 +346,7 @@ struct index_state {struct hashmap dir_hash;unsigned char sha1[20 ];struct untracked_cache *untracked; +uint64_t fsmonitor_last_update; };extern struct index_state the_index;
@@ -679,8 +682,10 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,#define CE_MATCH_IGNORE_MISSING 0x08/* enable stat refresh */ #define CE_MATCH_REFRESH 0x10 -extern int ie_match_stat (const struct index_state *,const struct cache_entry *,struct stat *,unsigned int ); -extern int ie_modified (const struct index_state *,const struct cache_entry *,struct stat *,unsigned int ); +/* do stat comparison even if CE_FSMONITOR_VALID is true */ +#define CE_MATCH_IGNORE_FSMONITOR 0X20 +extern int ie_match_stat (struct index_state *,const struct cache_entry *,struct stat *,unsigned int ); +extern int ie_modified (struct index_state *,const struct cache_entry *,struct stat *,unsigned int );#define HASH_WRITE_OBJECT 1#define HASH_FORMAT_CHECK 2
@@ -773,6 +778,7 @@ extern int core_apply_sparse_checkout;extern int precomposed_unicode;extern int protect_hfs;extern int protect_ntfs; +extern const char *core_fsmonitor;/* * Include broken refs in all ref iterations, which will
config.c +14 -0
@@ -2165,6 +2165,20 @@ int git_config_get_max_percent_split_change(void)return -1 ;/* default value */ } +int git_config_get_fsmonitor (void ) +{ +if (git_config_get_pathname ("core.fsmonitor" , &core_fsmonitor)) + core_fsmonitor =getenv ("GIT_FSMONITOR_TEST" ); + +if (core_fsmonitor && !*core_fsmonitor) + core_fsmonitor =NULL ; + +if (core_fsmonitor) +return 1 ; + +return 0 ; +} +NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr) {
config.h +1 -0
@@ -211,6 +211,7 @@ extern int git_config_get_pathname(const char *key, const char **dest);extern int git_config_get_untracked_cache (void );extern int git_config_get_split_index (void );extern int git_config_get_max_percent_split_change (void ); +extern int git_config_get_fsmonitor (void );/* This dies if the configured or default date is in the future */ extern int git_config_get_expiry (const char *key,const char **output);
diff-lib.c +2 -0
@@ -12,6 +12,7 @@#include "refs.h" #include "submodule.h" #include "dir.h" +#include "fsmonitor.h" /* * diff-files
@@ -228,6 +229,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)if (!changed && !dirty_submodule) {ce_mark_uptodate (ce); +mark_fsmonitor_valid (ce);if (!DIFF_OPT_TST (&revs->diffopt,FIND_COPIES_HARDER ))continue ; }
dir.c +17 -10
@@ -18,6 +18,7 @@#include "utf8.h" #include "varint.h" #include "ewah/ewok.h" +#include "fsmonitor.h" /* * Tells read_directory_recursive how a file or directory should be treated.
@@ -1688,17 +1689,23 @@ static int valid_cached_dir(struct dir_struct *dir,if (!untracked)return 0 ; -if (stat (path->len ? path->buf :"." , &st)) { -invalidate_directory (dir->untracked, untracked); -memset (&untracked->stat_data,0 ,sizeof (untracked->stat_data)); -return 0 ; - } -if (!untracked->valid || -match_stat_data_racy (istate, &untracked->stat_data, &st)) { -if (untracked->valid) +/* +* With fsmonitor, we can trust the untracked cache's valid field. +*/ +refresh_fsmonitor (istate); +if (!(dir->untracked->use_fsmonitor && untracked->valid)) { +if (stat (path->len ? path->buf :"." , &st)) {invalidate_directory (dir->untracked, untracked); -fill_stat_data (&untracked->stat_data, &st); -return 0 ; +memset (&untracked->stat_data,0 ,sizeof (untracked->stat_data)); +return 0 ; + } +if (!untracked->valid || +match_stat_data_racy (istate, &untracked->stat_data, &st)) { +if (untracked->valid) +invalidate_directory (dir->untracked, untracked); +fill_stat_data (&untracked->stat_data, &st); +return 0 ; + } }if (untracked->check_only != !!check_only) {
dir.h +2 -0
@@ -139,6 +139,8 @@ struct untracked_cache {int gitignore_invalidated;int dir_invalidated;int dir_opened; +/* fsmonitor invalidation data */ + unsigned int use_fsmonitor : 1; };struct dir_struct {
entry.c +3 -1
@@ -4,6 +4,7 @@#include "streaming.h" #include "submodule.h" #include "progress.h" +#include "fsmonitor.h" static void create_directories (const char *path,int path_len,const struct checkout *state)
@@ -357,6 +358,7 @@ static int write_entry(struct cache_entry *ce,lstat (ce->name, &st);fill_stat_cache_info (ce, &st); ce->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (state->istate, ce); state->istate->cache_changed |=CE_ENTRY_CHANGED ; }return 0 ;
@@ -402,7 +404,7 @@ int checkout_entry(struct cache_entry *ce,if (!check_path (path.buf, path.len, &st, state->base_dir_len)) {const struct submodule *sub; -unsigned changed =ce_match_stat (ce, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); +unsigned changed =ce_match_stat (ce, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR );/* * Needs to be checked before !changed returns early, * as the possibly empty directory was not changed
environment.c +1 -0
@@ -76,6 +76,7 @@ int protect_hfs = PROTECT_HFS_DEFAULT;#define PROTECT_NTFS_DEFAULT 0#endif int protect_ntfs =PROTECT_NTFS_DEFAULT ; +const char *core_fsmonitor;/* * The character that begins a commented line in user-editable file
fsmonitor.c new file +253 -0
253 lines not on this page · raw message
fsmonitor.h new file +61 -0
61 lines not on this page · raw message
preload-index.c +5 -1
@@ -4,6 +4,7 @@#include "cache.h" #include "pathspec.h" #include "dir.h" +#include "fsmonitor.h" #ifdef NO_PTHREADS static void preload_index(struct index_state *index,
@@ -55,15 +56,18 @@ static void *preload_thread(void *_data)continue ;if (ce_skip_worktree (ce))continue ; +if (ce->ce_flags &CE_FSMONITOR_VALID ) +continue ;if (!ce_path_match (ce, &p->pathspec,NULL ))continue ;if (threaded_has_symlink_leading_path (&cache, ce->name,ce_namelen (ce)))continue ;if (lstat (ce->name, &st))continue ; -if (ie_match_stat (index, ce, &st,CE_MATCH_RACY_IS_DIRTY )) +if (ie_match_stat (index, ce, &st,CE_MATCH_RACY_IS_DIRTY |CE_MATCH_IGNORE_FSMONITOR ))continue ;ce_mark_uptodate (ce); +mark_fsmonitor_valid (ce); }while (--nr >0 );cache_def_clear (&cache);return NULL ;
read-cache.c +42 -7
168 lines not on this page · raw message
submodule.c +1 -1
@@ -62,7 +62,7 @@ int is_staging_gitmodules_ok(const struct index_state *istate)if ((pos >=0 ) && (pos < istate->cache_nr)) {struct stat st;if (lstat (GITMODULES_FILE , &st) ==0 && -ce_match_stat (istate->cache[pos], &st,0 ) &DATA_CHANGED ) +ce_match_stat (istate->cache[pos], &st,CE_MATCH_IGNORE_FSMONITOR ) &DATA_CHANGED )return 0 ; }
unpack-trees.c +5 -3
@@ -14,6 +14,7 @@#include "dir.h" #include "submodule.h" #include "submodule-config.h" +#include "fsmonitor.h" /* * Error messages expected by scripts out of plumbing commands such as
@@ -408,6 +409,7 @@ static int apply_sparse_checkout(struct index_state *istate, ce->ce_flags &= ~CE_SKIP_WORKTREE ;if (was_skip_worktree !=ce_skip_worktree (ce)) { ce->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (istate, ce); istate->cache_changed |=CE_ENTRY_CHANGED ; }
@@ -1454,7 +1456,7 @@ static int verify_uptodate_1(const struct cache_entry *ce,return 0 ;if (!lstat (ce->name, &st)) { -int flags =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ; +int flags =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ;unsigned changed =ie_match_stat (o->src_index, ce, &st, flags);if (submodule_from_ce (ce)) {
@@ -1610,7 +1612,7 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int leconst struct cache_entry *src; src =index_file_exists (o->src_index, name, len,1 ); -return src && !ie_match_stat (o->src_index, src, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); +return src && !ie_match_stat (o->src_index, src, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); } static int check_ok_to_remove(const char *name, int len, int dtype,
@@ -2134,7 +2136,7 @@ int oneway_merge(const struct cache_entry * const *src,if (o->reset && o->update && !ce_uptodate (old) && !ce_skip_worktree (old)) {struct stat st;if (lstat (old->name, &st) || -ie_match_stat (o->src_index, old, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE )) +ie_match_stat (o->src_index, old, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR )) update |=CE_UPDATE ; }add_entry (o, old, update,0 );
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
RE: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 13 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Friday, September 15, 2017 3:21 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.config.c +3
-0+int git_config_get_fsmonitor (void ) +{ +if (git_config_get_pathname ("core.fsmonitor" , &core_fsmonitor))
config.c int git_config_get_max_percent_split_change(void) · line 2180
Quoted 10 lines
config.c +10
-0+ core_fsmonitor =getenv ("GIT_FSMONITOR_TEST" ); + +if (core_fsmonitor && !*core_fsmonitor) + core_fsmonitor =NULL ; + +if (core_fsmonitor) +return 1 ; + +return 0 ; +}
This functions return values are backwards relative to the rest of the git_config_* functions.
[snip]
+> /*
+> * With fsmonitor, we can trust the untracked cache's valid field.
+> */
[snip]
fsmonitor.c line 24
Quoted 3 lines
fsmonitor.c new file +3
-0+int read_fsmonitor_extension (struct index_state *istate,const void *data, +unsigned long sz) +{
If git_config_get_fsmonitor returns 0, fsmonitor_dirty will leak.
[snip]
fsmonitor.c line 179
Quoted 1 line
fsmonitor.c new file +1
-0+/* a fsmonitor process can return '*' to indicate all entries are invalid */
That's not documented in your documentation. Also, I'm not sure I like it: what
if I have a file whose name starts with '*'? Yeah, that would be silly, but this indicates the need for the protocol to have some sort of signaling mechanism that's out-of-band Maybe have some key\0value\0 pairs and then \0\0 and then the list of files? Or, if you want to keep it really simple, allow an entry of '/' (which is an invalid filename) to mean 'all'.
Quoted 4 lines
fsmonitor.c new file +4
-0+void add_fsmonitor (struct index_state *istate) { +int i; + +if (!istate->fsmonitor_last_update) {
[snip]
fsmonitor.c line 219
Quoted 1 line
fsmonitor.c new file +1
-0+/* reset the untracked cache */
Is this really necessary? Shouldn't the untracked cache be in a correct state already?
Quoted 2 lines
+/*
+ * Clear the given cache entriesCE_FSMONITOR_VALIDbit and invalidate
Nit: "s/entries/entry's/".
Re: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Thanks for taking the time to review/provide feedback!
Quoted 10 lines · David Turner
Quoted 9 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Friday, September 15, 2017 3:21 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 15 lines
config.c int git_config_get_max_percent_split_change(void) · line 2180
Quoted 13 lines
config.c +13
-0+int git_config_get_fsmonitor (void ) +{ +if (git_config_get_pathname ("core.fsmonitor" , &core_fsmonitor)) + core_fsmonitor =getenv ("GIT_FSMONITOR_TEST" ); + +if (core_fsmonitor && !*core_fsmonitor) + core_fsmonitor =NULL ; + +if (core_fsmonitor) +return 1 ; + +return 0 ; +}This functions return values are backwards relative to the rest of the git_config_* functions.
I'm confused. If core.fsmonitor is configured, it returns 1. If it is not configured, it returns 0. I don't make use of the -1 /* default value */ option as I didn't see any use/value in this case. What is backwards?
Quoted 7 lines
[snip]
+> /*
+> * With fsmonitor, we can trust the untracked cache's valid field.
+> */
Did you intend to make a comment here?
Quoted 8 lines
[snip]
fsmonitor.c line 24
Quoted 3 lines
fsmonitor.c new file +3
-0+int read_fsmonitor_extension (struct index_state *istate,const void *data, +unsigned long sz) +{If
git_config_get_fsmonitorreturns 0,fsmonitor_dirtywill leak.
Good catch! Thank you.
Quoted 10 lines
[snip]
fsmonitor.c line 179
Quoted 1 line
fsmonitor.c new file +1
-0+/* a fsmonitor process can return '*' to indicate all entries are invalid */ That's not documented in your documentation. Also, I'm not sure I like it: what
if I have a file whose name starts with '*'? Yeah, that would be silly, but this indicates the need for the protocol to have some sort of signaling mechanism that's out-of-band Maybe have some key\0value\0 pairs and then \0\0 and then the list of files? Or, if you want to keep it really simple, allow an entry of '/' (which is an invalid filename) to mean 'all'.
Yea, this was an optimization I added late in the game to get around an issue in Watchman where it returns the name of every file the first time you query it (rather than the set of files that have actually changed since the requested time).
I didn't realize the wild card '*' was a valid character for a filename.
I'll switch to '/' as you suggest as I don't want to complicate things
unnecessarily to handle this relatively rare optimization. I'll also get it documented properly. Thanks!
Quoted 9 lines
Quoted 4 lines
fsmonitor.c new file +4
-0+void add_fsmonitor (struct index_state *istate) { +int i; + +if (!istate->fsmonitor_last_update) {[snip]
fsmonitor.c line 219
Quoted 1 line
fsmonitor.c new file +1
-0+/* reset the untracked cache */ Is this really necessary? Shouldn't the untracked cache be in a correct state already?
When fsmonitor is not turned on, I'm not explicitly invalidating untracked cache directory entries as git makes changes to files. While I doubt the sequence happens of 1) git making changes to files, *then* 2) turning on fsmonitor - I thought it better safe than sorry to assume that pattern won't ever happen in the future. Especially since turning on the extension is rare and the cost is low.
Quoted 6 lines
Quoted 2 lines
+/*
+ * Clear the given cache entriesCE_FSMONITOR_VALIDbit and invalidateNit: "s/entries/entry's/".
RE: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 39 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Monday, September 18, 2017 9:07 AM
To: David Turner <[email protected]>; 'Ben Peart' <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.Thanks for taking the time to review/provide feedback!
Quoted 10 lines · David Turner
Quoted 9 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Friday, September 15, 2017 3:21 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.Quoted 15 lines
config.c int git_config_get_max_percent_split_change(void) · line 2180
Quoted 13 lines
config.c +13
-0+int git_config_get_fsmonitor (void ) +{ +if (git_config_get_pathname ("core.fsmonitor" , &core_fsmonitor)) + core_fsmonitor =getenv ("GIT_FSMONITOR_TEST" ); + +if (core_fsmonitor && !*core_fsmonitor) + core_fsmonitor =NULL ; + +if (core_fsmonitor) +return 1 ; + +return 0 ; +}This functions return values are backwards relative to the rest of the
Quoted 5 lines
git_config_* functions.
I'm confused. If core.fsmonitor is configured, it returns 1. If it is not configured, it returns 0. I don't make use of the -1 /* default value */ option as I didn't see any use/value in this case. What is backwards?
The other git_config_* functions return 1 for error and 0 for success.
Quoted 8 lines
Quoted 6 lines
[snip]
+> /*
+> * With fsmonitor, we can trust the untracked cache's valid field.
+> */Did you intend to make a comment here?
Sorry. I was going to make a comment that I didn't see how that could work since we weren't touching the untracked cache here, but then I saw the bit
further down. I'm still not sure it works (see comment on 10/12), but at
least it could in theory work.
Re: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 39 lines · David Turner
Quoted 39 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Monday, September 18, 2017 9:07 AM
To: David Turner <[email protected]>; 'Ben Peart' <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.Thanks for taking the time to review/provide feedback!
Quoted 25 lines · David Turner
Quoted 13 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Friday, September 15, 2017 3:21 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v6 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.config.c +3
-0+int git_config_get_fsmonitor (void ) +{ +if (git_config_get_pathname ("core.fsmonitor" , &core_fsmonitor))config.c int git_config_get_max_percent_split_change(void) · line 2180
Quoted 10 lines
config.c +10
-0+ core_fsmonitor =getenv ("GIT_FSMONITOR_TEST" ); + +if (core_fsmonitor && !*core_fsmonitor) + core_fsmonitor =NULL ; + +if (core_fsmonitor) +return 1 ; + +return 0 ; +}This functions return values are backwards relative to the rest of the
Quoted 7 lines
Quoted 5 lines
git_config_* functions.
I'm confused. If core.fsmonitor is configured, it returns 1. If it is not configured, it returns 0. I don't make use of the -1 /* default value */ option as I didn't see any use/value in this case. What is backwards?
The other git_config_* functions return 1 for error and 0 for success.
Hmm, I followed the model (ie copy/paste :)) used by the tracked cache. If you look at how it uses, the return value, it is 0 == false == remove the extension, 1 == true == add the extension. I'm doing the same with fsmonitor.
static void tweak_untracked_cache(struct index_state *istate)
{
switch (git_config_get_untracked_cache()) {
case -1: /* keep: do nothing */
break;
case 0: /* false */
remove_untracked_cache(istate);
break;
case 1: /* true */
add_untracked_cache(istate);
break;
default: /* unknown value: do nothing */
break;
}
}void tweak_fsmonitor(struct index_state *istate)
{
switch (git_config_get_fsmonitor()) {
case -1: /* keep: do nothing */
break;
case 0: /* false */
remove_fsmonitor(istate);
break;
case 1: /* true */
add_fsmonitor(istate);
break;
default: /* unknown value: do nothing */
break;
}
}Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
[snip]
+> /*
+> * With fsmonitor, we can trust the untracked cache's valid field.
+> */
Quoted 8 lines
Quoted 1 line
Did you intend to make a comment here?
Sorry. I was going to make a comment that I didn't see how that could work since we weren't touching the untracked cache here, but then I saw the bit
further down. I'm still not sure it works (see comment on 10/12), but at
least it could in theory work.
The logic here assumes that when the index is written out, it is valid including the untracked cache and the CE_FSMONITOR_VALID bits. Therefore it should still all be valid as of the time the fsmonitor was queried and the index saved.
Another way of thinking about this is that the fsmonitor extension is simply adding another persisted bit on each index entry. It just gets persisted/restored differently than the other persisted bits.
Obviously, before we can use it assuming it reflects the *current* state of the working directory, we have to refresh the bits via the refresh logic.
PATCH v6 5/12 fsmonitor: add documentation for the fsmonitor extension.
In reply to Ben PeartThis includes the core.fsmonitor setting, the query-fsmonitor hook, and the fsmonitor index extension.
--- Documentation/config.txt | 6 ++++++ Documentation/githooks.txt | 23 +++++++++++++++++++++++ Documentation/technical/index-format.txt | 19 +++++++++++++++++++ 3 files changed, 48 insertions(+)
Documentation/config.txt +6 -0
@@ -413,6 +413,12 @@ core.protectNTFS:: 8.3 "short" names. Defaults to `true` on Windows, and `false` elsewhere. +core.fsmonitor:: + If set, the value of this variable is used as a command which + will identify all files that may have changed since the + requested date/time. This information is used to speed up git by + avoiding unnecessary processing of files that have not changed. + core.trustctime:: If false, the ctime differences between the index and the working tree are ignored; useful when the inode change time
Documentation/githooks.txt +23 -0
@@ -456,6 +456,29 @@ non-zero status causes 'git send-email' to abort before sending any e-mails. +[[fsmonitor-watchman]] +fsmonitor-watchman +~~~~~~~~~~~~~~~ + +This hook is invoked when the configuration option core.fsmonitor is +set to .git/hooks/fsmonitor-watchman. It takes two arguments, a version +(currently 1) and the time in elapsed nanoseconds since midnight, +January 1, 1970. + +The hook should output to stdout the list of all files in the working +directory that may have changed since the requested time. The logic +should be inclusive so that it does not miss any potential changes. +The paths should be relative to the root of the working directory +and be separated by a single NUL. + +Git will limit what files it checks for changes as well as which +directories are checked for untracked files based on the path names +given. + +The exit status determines whether git will use the data from the +hook to limit its search. On error, it will fall back to verifying +all files and folders. + GIT --- Part of the linkgit:git[1] suite
Documentation/technical/index-format.txt +19 -0
@@ -295,3 +295,22 @@ The remaining data of each directory block is grouped by type: in the previous ewah bitmap. - One NUL. + +== File System Monitor cache + + The file system monitor cache tracks files for which the core.fsmonitor + hook has told us about changes. The signature for this extension is + { 'F', 'S', 'M', 'N' }. + + The extension starts with + + - 32-bit version number: the current supported version is 1. + + - 64-bit time: the extension data reflects all changes through the given + time which is stored as the nanoseconds elapsed since midnight, + January 1, 1970. + + - 32-bit bitmap size: the size of the CE_FSMONITOR_VALID bitmap. + + - An ewah bitmap, the n-th bit indicates whether the n-th index entry + is not CE_FSMONITOR_VALID.
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
RE: [PATCH v6 05/12] fsmonitor: add documentation for the fsmonitor extension.
Quoted 27 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Friday, September 15, 2017 3:21 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v6 05/12] fsmonitor: add documentation for the fsmonitor extension.This includes the core.fsmonitor setting, the query-fsmonitor hook, and the fsmonitor index extension.
Signed-off-by Ben Peart <[email protected]>--- Documentation/config.txt | 6 ++++++ Documentation/githooks.txt | 23 +++++++++++++++++++++++ Documentation/technical/index-format.txt | 19 +++++++++++++++++++ 3 files changed, 48 insertions(+)Documentation/config.txt index renamed from Documentation/config.txt +0
-0
dc4e3f58a2..c196007a27100644
Documentation/config.txt core.protectNTFS:: · line 420
Quoted 8 lines
Documentation/config.txt +5
-0@@ -413,6 +413,12 @@ core.protectNTFS:: 8.3 "short" names. Defaults to `true` on Windows, and `false` elsewhere. +core.fsmonitor:: + If set, the value of this variable is used as a command which + will identify all files that may have changed since the + requested date/time. This information is used to speed up git by + avoiding unnecessary processing of files that have not changed.
I'm confused here. You have a file called "fsmonitor-watchman", which seems to discuss the protocol for core.fsmonitor scripts in general, and you have this documentation, which does not link to that file. Can you clarify this?
<snip>
Documentation/config.txt core.protectNTFS:: · line 423
Quoted 3 lines
Documentation/config.txt +3
-0+The hook should output to stdout the list of all files in the working +directory that may have changed since the requested time. The logic +should be inclusive so that it does not miss any potential changes.
+"It is OK to include files which have not actually changed. Newly-created and deleted files should also be included. When files are renamed, both the old and the new name should be included."
Also, please discuss case sensitivity issues (e.g. on OS X).
Quoted 2 lines
Documentation/config.txt +2
-0+The paths should be relative to the root of the working directory and +be separated by a single NUL.
<snip>
Documentation/config.txt
Quoted 5 lines
Documentation/config.txt +5
-0+ - 32-bit version number: the current supported version is 1. + + - 64-bit time: the extension data reflects all changes through the given + time which is stored as the nanoseconds elapsed since midnight, + January 1, 1970.
Nit: Please specify signed or unsigned for these. (I expect to be getting out of cryosleep around 2262, and I want to know if my old git repos will keep working...)
Quoted 4 lines
Documentation/config.txt +4
-0+ - 32-bit bitmap size: the size of the CE_FSMONITOR_VALID bitmap. + + - An ewah bitmap, the n-th bit indicates whether the n-th index entry + is not CE_FSMONITOR_VALID.
Re: [PATCH v6 05/12] fsmonitor: add documentation for the fsmonitor extension.
Quoted 30 lines · David Turner
Quoted 30 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Friday, September 15, 2017 3:21 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v6 05/12] fsmonitor: add documentation for the fsmonitor extension.This includes the core.fsmonitor setting, the query-fsmonitor hook, and the fsmonitor index extension.
Signed-off-by Ben Peart <[email protected]>--- Documentation/config.txt | 6 ++++++ Documentation/githooks.txt | 23 +++++++++++++++++++++++ Documentation/technical/index-format.txt | 19 +++++++++++++++++++ 3 files changed, 48 insertions(+)Documentation/config.txt index renamed from Documentation/config.txt +0
-0
dc4e3f58a2..c196007a27100644Documentation/config.txt +0
-0@@ -413,6 +413,12 @@ core.protectNTFS:: 8.3 "short" names. Defaults to `true` on Windows, and `false` elsewhere.
Quoted 7 lines
Documentation/config.txt core.protectNTFS:: · line 420
Quoted 5 lines
Documentation/config.txt +5
-0+core.fsmonitor:: + If set, the value of this variable is used as a command which + will identify all files that may have changed since the + requested date/time. This information is used to speed up git by + avoiding unnecessary processing of files that have not changed.I'm confused here. You have a file called "fsmonitor-watchman", which seems to discuss the protocol for core.fsmonitor scripts in general, and you have this documentation, which does not link to that file. Can you clarify this?
I'll add the missing link to the documentation in githooks.txt. The documentation should be enough for someone to develop another integration script.
The fsmonitor-watchman script allows people to easily use this patch series with the existing Watchman monitor but it can certainly also be used as a sample for how to integrate with another file system monitor.
Quoted 16 lines
<snip>
Documentation/config.txt
Quoted 3 lines
Documentation/config.txt +3
-0+The hook should output to stdout the list of all files in the working +directory that may have changed since the requested time. The logic +should be inclusive so that it does not miss any potential changes.+"It is OK to include files which have not actually changed. Newly-created and deleted files should also be included. When files are renamed, both the old and the new name should be included."
Also, please discuss case sensitivity issues (e.g. on OS X).
Quoted 2 lines
Documentation/config.txt +2
-0+The paths should be relative to the root of the working directory and +be separated by a single NUL.<snip>
Quoted 9 lines
Documentation/config.txt
Quoted 5 lines
Documentation/config.txt +5
-0+ - 32-bit version number: the current supported version is 1. + + - 64-bit time: the extension data reflects all changes through the given + time which is stored as the nanoseconds elapsed since midnight, + January 1, 1970.Nit: Please specify signed or unsigned for these. (I expect to be getting out of cryosleep around 2262, and I want to know if my old git repos will keep working...)
While I'm not opposed to specifying unsigned, I did notice that the only place signed/unsigned is specified today is in "index entry." Everywhere else doesn't specify so I left it off for consistency. I've not seen negative version numbers nor negative time so am not entirely sure it is necessary to clarify. :)
Quoted 5 lines
Quoted 4 lines
Documentation/config.txt +4
-0+ - 32-bit bitmap size: the size of the CE_FSMONITOR_VALID bitmap. + + - An ewah bitmap, the n-th bit indicates whether the n-th index entry + is not CE_FSMONITOR_VALID.
Re: [PATCH v6 05/12] fsmonitor: add documentation for the fsmonitor extension.
In reply to Ben PeartQuoted 3 lines · Ben Peart
+[[fsmonitor-watchman]]
+fsmonitor-watchman
+~~~~~~~~~~~~~~~
I've queued a mini squash on top to make sure the ~~~~ line aligns with the length of the string above it by adding three ~'s here.
Re: [PATCH v6 05/12] fsmonitor: add documentation for the fsmonitor extension.
Quoted 9 lines · Junio C Hamano
Quoted 3 lines · Ben Peart
+[[fsmonitor-watchman]]
+fsmonitor-watchman
+~~~~~~~~~~~~~~~I've queued a mini squash on top to make sure the ~~~~ line aligns with the length of the string above it by adding three ~'s here.
Thanks, I'll do the same assuming there will be another re-roll.
PATCH v6 6/12 ls-files: Add support in ls-files to display the fsmonitor valid bit
In reply to Ben PeartAdd a new command line option (-f) to ls-files to have it use lowercase letters for 'fsmonitor valid' files
--- builtin/ls-files.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
builtin/ls-files.c +6 -2
@@ -31,6 +31,7 @@ static int show_resolve_undo;static int show_modified;static int show_killed;static int show_valid_bit; +static int show_fsmonitor_bit;static int line_terminator ='\n' ;static int debug_mode;static int show_eol;
@@ -86,7 +87,8 @@ static const char *get_tag(const struct cache_entry *ce, const char *tag) {static char alttag[4 ]; -if (tag && *tag && show_valid_bit && (ce->ce_flags &CE_VALID )) { +if (tag && *tag && ((show_valid_bit && (ce->ce_flags &CE_VALID )) || + (show_fsmonitor_bit && (ce->ce_flags &CE_FSMONITOR_VALID )))) {memcpy (alttag, tag,3 );if (isalpha (tag[0 ])) {
@@ -515,6 +517,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("identify the file status with tags")),OPT_BOOL ('v' ,NULL , &show_valid_bit,N_ ("use lowercase letters for 'assume unchanged' files" )), +OPT_BOOL ('f' ,NULL , &show_fsmonitor_bit, +N_ ("use lowercase letters for 'fsmonitor clean' files" )),OPT_BOOL ('c' ,"cached" , &show_cached,N_ ("show cached files in the output (default)" )), OPT_BOOL('d', "deleted", &show_deleted,
@@ -584,7 +588,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)for (i =0 ; i < exclude_list.nr; i++) {add_exclude (exclude_list.items[i].string,"" ,0 , el, --exclude_args); } -if (show_tag || show_valid_bit) { +if (show_tag || show_valid_bit || show_fsmonitor_bit) { tag_cached ="H " ; tag_unmerged ="M " ; tag_removed ="R " ;
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
RE: [PATCH v6 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bit
Quoted 10 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Friday, September 15, 2017 3:21 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v6 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bit
Quoted 2 lines
Add a new command line option (-f) to ls-files to have it use lowercase letters for 'fsmonitor valid' files
Document in man page, please.
PATCH v6 7/12 update-index: add fsmonitor support to update-index
In reply to Ben PeartAdd support in update-index to manually add/remove the fsmonitor extension via --fsmonitor/--no-fsmonitor flags
--- builtin/update-index.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
builtin/update-index.c +19 -0
@@ -918,6 +918,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)int lock_error =0 ;int split_index =-1 ;int force_write =0 ; +int fsmonitor =-1 ;struct lock_file *lock_file;struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;
@@ -1011,6 +1012,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("enable untracked cache without testing the filesystem"), UC_FORCE),OPT_SET_INT (0 ,"force-write-index" , &force_write,N_ ("write out the index even if is not flagged as changed" ),1 ), +OPT_BOOL (0 ,"fsmonitor" , &fsmonitor, +N_ ("enable or disable file system monitor" )), OPT_END() };
@@ -1152,6 +1155,22 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)die ("BUG: bad untracked_cache value: %d" , untracked_cache); } +if (fsmonitor >0 ) { +if (git_config_get_fsmonitor () ==0 ) +warning (_ ("core.fsmonitor is unset; " +"set it if you really want to " +"enable fsmonitor" )); +add_fsmonitor (&the_index); +report (_ ("fsmonitor enabled" )); + }else if (!fsmonitor) { +if (git_config_get_fsmonitor () ==1 ) +warning (_ ("core.fsmonitor is set; " +"remove it if you really want to " +"disable fsmonitor" )); +remove_fsmonitor (&the_index); +report (_ ("fsmonitor disabled" )); + } +if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
PATCH v6 8/12 fsmonitor: add a test tool to dump the index extension
In reply to Ben PeartAdd a test utility (test-dump-fsmonitor) that will dump the fsmonitor index extension.
--- Makefile | 1 + t/helper/test-dump-fsmonitor.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 t/helper/test-dump-fsmonitor.c
Makefile +1 -0
@@ -639,6 +639,7 @@ TEST_PROGRAMS_NEED_X += test-config TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta TEST_PROGRAMS_NEED_X += test-dump-cache-tree +TEST_PROGRAMS_NEED_X += test-dump-fsmonitor TEST_PROGRAMS_NEED_X += test-dump-split-index TEST_PROGRAMS_NEED_X += test-dump-untracked-cache TEST_PROGRAMS_NEED_X += test-fake-ssh
t/helper/test-dump-fsmonitor.c new file +21 -0
@@ -0,0 +1,21 @@ +#include "cache.h" + +int cmd_main (int ac,const char **av) +{ +struct index_state *istate = &the_index; +int i; + +setup_git_directory (); +if (do_read_index (istate,get_index_file (),0 ) <0 ) +die ("unable to read index file" ); +if (!istate->fsmonitor_last_update) { +printf ("no fsmonitor\n" ); +return 0 ; + } +printf ("fsmonitor last update %" PRIuMAX"\n" , istate->fsmonitor_last_update); + +for (i =0 ; i < istate->cache_nr; i++) +printf ((istate->cache[i]->ce_flags &CE_FSMONITOR_VALID ) ?"+" :"-" ); + +return 0 ; +}
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
Re: [PATCH v6 08/12] fsmonitor: add a test tool to dump the index extension
Quoted 11 lines · Ben Peart
t/helper/test-dump-fsmonitor.c new file +5
-0@@ -0,0 +1,21 @@ +#include "cache.h" + +int cmd_main (int ac,const char **av) +{ +struct index_state *istate = &the_index;
t/helper/test-dump-fsmonitor.c line 15
Quoted 10 lines
t/helper/test-dump-fsmonitor.c new file +10
-0+int i; + +setup_git_directory (); +if (do_read_index (istate,get_index_file (),0 ) <0 ) +die ("unable to read index file" ); +if (!istate->fsmonitor_last_update) { +printf ("no fsmonitor\n" ); +return 0 ; + } +printf ("fsmonitor last update %" PRIuMAX"\n" , istate->fsmonitor_last_update);
After pushing this out and had Travis complain, I queued a squash on top of this to cast the argument to (uintmax_t), like you did in an earlier step (I think it was [PATCH 04/12]).
Re: [PATCH v6 08/12] fsmonitor: add a test tool to dump the index extension
Quoted 19 lines · Junio C Hamano
Quoted 17 lines · Ben Peart
t/helper/test-dump-fsmonitor.c new file +11
-0@@ -0,0 +1,21 @@ +#include "cache.h" + +int cmd_main (int ac,const char **av) +{ +struct index_state *istate = &the_index; +int i; + +setup_git_directory (); +if (do_read_index (istate,get_index_file (),0 ) <0 ) +die ("unable to read index file" ); +if (!istate->fsmonitor_last_update) {
Quoted 9 lines
t/helper/test-dump-fsmonitor.c line 15
Quoted 4 lines
t/helper/test-dump-fsmonitor.c new file +4
-0+printf ("no fsmonitor\n" ); +return 0 ; + } +printf ("fsmonitor last update %" PRIuMAX"\n" , istate->fsmonitor_last_update);After pushing this out and had Travis complain, I queued a squash on top of this to cast the argument to (
uintmax_t), like you did in an earlier step (I think it was [PATCH 04/12]).
Thanks. I'll update this to cast it as (uint64_t) as that is what get/put_be64 use. As far as I can tell they both map to the same thing (unsigned long long) so there isn't functional difference.
Re: [PATCH v6 08/12] fsmonitor: add a test tool to dump the index extension
Quoted 33 lines · Ben Peart
Quoted 20 lines · Junio C Hamano
Quoted 18 lines · Ben Peart
t/helper/test-dump-fsmonitor.c new file +12
-0@@ -0,0 +1,21 @@ +#include "cache.h" + +int cmd_main (int ac,const char **av) +{ + struct index_state *istate = &the_index; + int i; + + setup_git_directory(); + if (do_read_index(istate, get_index_file(), 0) < 0) + die("unable to read index file"); + if (!istate->fsmonitor_last_update) { + printf("no fsmonitor\n");Quoted 9 lines
Quoted 4 lines
t/helper/test-dump-fsmonitor.c new file +3
-0+ return 0; + } + printf("fsmonitor last update %"PRIuMAX"\n",
istate->fsmonitor_last_update);After pushing this out and had Travis complain, I queued a squash on top of this to cast the argument to (
uintmax_t), like you did in an earlier step (I think it was [PATCH 04/12]).
Quoted 3 lines
Thanks. I'll update this to cast it as (
uint64_t) as that is what get/put_be64 use. As far as I can tell they both map to the same thing (unsigned long long) so there isn't functional difference.
(Just to double-check): This is the way to print "PRIuMAX" correctly
(on all platforms):
printf("fsmonitor last update %"PRIuMAX"\n",
(uintmax_t)istate->fsmonitor_last_update);
RE: [PATCH v6 08/12] fsmonitor: add a test tool to dump the index extension
Quoted 44 lines
-----Original Message-----
From: Torsten Bögershausen [mailto:[email protected]]
Sent: Monday, September 18, 2017 11:43 AM
To: Ben Peart <[email protected]>; Junio C Hamano <[email protected]>; Ben Peart <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH v6 08/12] fsmonitor: add a test tool to dump the index extensionQuoted 32 lines · Ben Peart
Quoted 28 lines · Junio C Hamano
Quoted 20 lines · Ben Peart
(quoted diff) +0
-0
b/t/helper/test-dump-fsmonitor.cnew file mode 100644 index 0000000000..482d749bb9t/helper/test-dump-fsmonitor.c new file +14
-0@@ -0,0 +1,21 @@ +#include "cache.h" + +int cmd_main (int ac,const char **av) { + struct index_state *istate = &the_index; + int i; + + setup_git_directory(); + if (do_read_index(istate, get_index_file(), 0) < 0) + die("unable to read index file"); + if (!istate->fsmonitor_last_update) { + printf("no fsmonitor\n"); + return 0; + } + printf("fsmonitor last update %"PRIuMAX"\n",Quoted 1 line
istate->fsmonitor_last_update);After pushing this out and had Travis complain, I queued a squash on top of this to cast the argument to (
uintmax_t), like you did in an earlier step (I think it was [PATCH 04/12]).
Quoted 9 lines
Quoted 3 lines
Thanks. I'll update this to cast it as (
uint64_t) as that is what get/put_be64 use. As far as I can tell they both map to the same thing (unsigned long long) so there isn't functional difference.(Just to double-check): This is the way to print "PRIuMAX" correctly (on all platforms):
printf("fsmonitor last update %"PRIuMAX"\n", (uintmax_t)istate-
Quoted 1 line
fsmonitor_last_update);
Should I just make the variable type itself uintmax_t and then just skip the cast altogether? I went with uint64_t because that is what getnanotime returned.
Re: [PATCH v6 08/12] fsmonitor: add a test tool to dump the index extension
Quoted 5 lines
Should I just make the variable type itself
uintmax_tand then just skip the cast altogether? I went withuint64_tbecause that is what getnanotime returned.
That is a bit of taste question (or answer)
Typically you declare the variables in the type you need, and this is uint64_t.
Let's step back a bit:
To print e.g a variable of type uint32_t, you use PRIu32 in the format string, like this:
fprintf(stderr, "Total %"PRIu32" (delta %"PRIu32"),",....
In theory (it is in the later specs, and it exists on many platforms), there is a PRIu64 as well.
We don't seem to use it in Git, probably because uintmax_t is (more) portable and understood by all platforms which support Git. (And beside that, on most platforms uintmax_t is 64 bit).
So my suggestion would be to keep uint64_t and cast the variable into uintmax_t whenever it is printed.
RE: [PATCH v6 08/12] fsmonitor: add a test tool to dump the index extension
Quoted 30 lines
-----Original Message-----
From: Torsten Bögershausen [mailto:[email protected]]
Sent: Tuesday, September 19, 2017 10:16 AM
To: Ben Peart <[email protected]>
Cc: Ben Peart <[email protected]>; Junio C Hamano <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH v6 08/12] fsmonitor: add a test tool to dump the index extensionQuoted 5 lines
Should I just make the variable type itself
uintmax_tand then just skip the cast altogether? I went withuint64_tbecause that is what getnanotime returned.That is a bit of taste question (or answer)
Typically you declare the variables in the type you need, and this is
uint64_t.Let's step back a bit:
To print e.g a variable of typeuint32_t, you use PRIu32 in the format string, like this:fprintf(stderr, "Total %"PRIu32" (delta %"PRIu32"),",....
In theory (it is in the later specs, and it exists on many platforms), there is a PRIu64 as well.
Quoted 7 lines
We don't seem to use it in Git, probably because
uintmax_tis (more) portable and understood by all platforms which support Git. (And beside that, on most platformsuintmax_tis 64 bit).So my suggestion would be to keep
uint64_tand cast the variable intouintmax_twhenever it is printed.
Great! That is the way I have it.
PATCH v6 9/12 split-index: disable the fsmonitor extension when running the split index test
In reply to Ben PeartThe split index test t1700-split-index.sh has hard coded SHA values for the index. Currently it supports index V4 and V3 but assumes there are no index extensions loaded.
When manually forcing the fsmonitor extension to be turned on when running the test suite, the SHA values no longer match which causes the test to fail.
The potential matrix of index extensions and index versions can is quite large so instead disable the extension before attempting to run the test.
--- t/t1700-split-index.sh | 1 + 1 file changed, 1 insertion(+)
t/t1700-split-index.sh +1 -0
@@ -6,6 +6,7 @@ test_description='split index mode tests' # We need total control of index splitting here sane_unset GIT_TEST_SPLIT_INDEX +sane_unset GIT_FSMONITOR_TEST test_expect_success 'enable split index' ' git config splitIndex.maxPercentChange 100 &&
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
Re: [PATCH v6 09/12] split-index: disable the fsmonitor extension when running the split index test
Hi,
Quoted 10 lines · Ben Peart
The split index test
t1700-split-index.shhas hard coded SHA values for the index. Currently it supports index V4 and V3 but assumes there are no index extensions loaded.When manually forcing the fsmonitor extension to be turned on when running the test suite, the SHA values no longer match which causes the test to fail.
The potential matrix of index extensions and index versions can is quite large so instead disable the extension before attempting to run the test.
Thanks for finding and diagnosing this problem.
This feels to me like the wrong fix. Wouldn't it be better for the test not to depend on the precise object ids? See the "Tips for Writing Tests" section in t/README:
And
such drastic changes to the core GIT that even changes these otherwise supposedly stable object IDs should be accompanied by an update to t0000-basic.sh.
However, other tests that simply rely on basic parts of the core GIT working properly should not have that level of intimate knowledge of the core GIT internals. If all the test scripts hardcoded the object IDs like t0000-basic.sh does, that defeats the purpose of t0000-basic.sh, which is to isolate that level of validation in one place. Your test also ends up needing updating when such a change to the internal happens, so do _not_ do it and leave the low level of validation to t0000-basic.sh.
Worse, t1700-split-index.sh doesn't explain where the object ids it uses comes from so it is not even obvious to a casual reader like me how to fix it.
See t/diff-lib.sh for some examples of one way to avoid depending on the object id computation. Another way that is often preferable is to come up with commands to compute the expected hash values, like $(git rev-parse HEAD^{tree}), and use those instead of hard-coded values.
Thanks and hope that helps,
Jonathan
Re: [PATCH v6 09/12] split-index: disable the fsmonitor extension when running the split index test
Quoted 12 lines · Jonathan Nieder
Hi,
Quoted 8 lines · Ben Peart
The split index test
t1700-split-index.shhas hard coded SHA values for the index. Currently it supports index V4 and V3 but assumes there are no index extensions loaded.When manually forcing the fsmonitor extension to be turned on when running the test suite, the SHA values no longer match which causes the test to fail.
Quoted 9 lines
Quoted 2 lines
The potential matrix of index extensions and index versions can is quite large so instead disable the extension before attempting to run the test.
Thanks for finding and diagnosing this problem.
This feels to me like the wrong fix. Wouldn't it be better for the test not to depend on the precise object ids? See the "Tips for Writing Tests" section in t/README:
I completely agree that a better fix would be to rewrite the test to not hard code the SHA values. I'm sure this will come to bite us again as we discuss the migration to a different SHA algorithm.
That said, I think fixing this correctly is outside the scope of this patch series. It has been written this way since it was created back in 2014 (and patched in 2015 to hard code the V4 index SHA).
If desired, this patch can simply be dropped from the series entirely as I doubt anyone other than me will attempt to run it with the fsmonitor extension turned on.
Quoted 27 lines
And
such drastic changes to the core GIT that even changes these otherwise supposedly stable object IDs should be accompanied by an update tot0000-basic.sh.However, other tests that simply rely on basic parts of the core GIT working properly should not have that level of intimate knowledge of the core GIT internals. If all the test scripts hardcoded the object IDs like
t0000-basic.shdoes, that defeats the purpose oft0000-basic.sh, which is to isolate that level of validation in one place. Your test also ends up needing updating when such a change to the internal happens, so do _not_ do it and leave the low level of validation tot0000-basic.sh.Worse,
t1700-split-index.shdoesn't explain where the object ids it uses comes from so it is not even obvious to a casual reader like me how to fix it.See
t/diff-lib.shfor some examples of one way to avoid depending on the object id computation. Another way that is often preferable is to come up with commands to compute the expected hash values, like $(git rev-parse HEAD^{tree}), and use those instead of hard-coded values.Thanks and hope that helps,
Jonathan
Re: [PATCH v6 09/12] split-index: disable the fsmonitor extension when running the split index test
Hi,
Quoted 9 lines · Ben Peart
Quoted 3 lines · Jonathan Nieder
This feels to me like the wrong fix. Wouldn't it be better for the test not to depend on the precise object ids? See the "Tips for Writing Tests" section in t/README:
I completely agree that a better fix would be to rewrite the test to not hard code the SHA values. I'm sure this will come to bite us again as we discuss the migration to a different SHA algorithm.
nit: the kind of change I'm proposing does not entail a full rewrite. :)
The SHA migration aspect is true, but that's actually the least of my worries. I intend to introduce a SHA1 test prereq that crazy tests which want to depend on the hash function can declare a dependency on.
My actual worry is that tests hard-coding object ids are (1) hard to understand, as illustrated by my having no clue what these particular object ids refer to and (2) very brittle, since an object id changes whenever a timestamp or any of the history leading to an object changes. They create a trap for anyone wanting to change the test later. They are basically change detector tests, which is generally accepted to be a bad practice.
Quoted 4 lines
That said, I think fixing this correctly is outside the scope of this patch series. It has been written this way since it was created back in 2014 (and patched in 2015 to hard code the V4 index SHA).
Fair enough.
Quoted 3 lines
If desired, this patch can simply be dropped from the series entirely as I doubt anyone other than me will attempt to run it with the fsmonitor extension turned on.
*shrug*
My motivations in the context of the review were:
* now that we noticed the problem, we have an opportunity to fix it! (i.e. a fix would not have to be part of this series and would not necessarily have to be written by you)
* if we include this non-fix, the commit message really needs to say something about it. Otherwise people are likely to cargo-cult it in other contexts and make the problem worse.
Thanks,
Jonathan
Re: [PATCH v6 09/12] split-index: disable the fsmonitor extension when running the split index test
Quoted 42 lines · Jonathan Nieder
Hi,
Quoted 9 lines · Ben Peart
Quoted 3 lines · Jonathan Nieder
This feels to me like the wrong fix. Wouldn't it be better for the test not to depend on the precise object ids? See the "Tips for Writing Tests" section in t/README:
I completely agree that a better fix would be to rewrite the test to not hard code the SHA values. I'm sure this will come to bite us again as we discuss the migration to a different SHA algorithm.
nit: the kind of change I'm proposing does not entail a full rewrite. :)
The SHA migration aspect is true, but that's actually the least of my worries. I intend to introduce a SHA1 test prereq that crazy tests which want to depend on the hash function can declare a dependency on.
My actual worry is that tests hard-coding object ids are (1) hard to understand, as illustrated by my having no clue what these particular object ids refer to and (2) very brittle, since an object id changes whenever a timestamp or any of the history leading to an object changes. They create a trap for anyone wanting to change the test later. They are basically change detector tests, which is generally accepted to be a bad practice.
Quoted 4 lines
That said, I think fixing this correctly is outside the scope of this patch series. It has been written this way since it was created back in 2014 (and patched in 2015 to hard code the V4 index SHA).
Fair enough.
Quoted 3 lines
If desired, this patch can simply be dropped from the series entirely as I doubt anyone other than me will attempt to run it with the fsmonitor extension turned on.
*shrug*
My motivations in the context of the review were:
Quoted 8 lines
* now that we noticed the problem, we have an opportunity to fix it! (i.e. a fix would not have to be part of this series and would not necessarily have to be written by you)
* if we include this non-fix, the commit message really needs to say something about it. Otherwise people are likely to cargo-cult it in other contexts and make the problem worse.
I'll update the commit message to indicate this is a temporary workaround until the underlying hard coded SHA issue is fixed.
Quoted 3 lines
Thanks,
Jonathan
PATCH v6 10/12 fsmonitor: add test cases for fsmonitor extension
In reply to Ben PeartTest the ability to add/remove the fsmonitor index extension via update-index.
Test that dirty files returned from the integration script are properly represented in the index extension and verify that ls-files correctly reports their state.
Test that ensure status results are correct when using the new fsmonitor extension. Test untracked, modified, and new files by ensuring the results are identical to when not using the extension.
Test that if the fsmonitor extension doesn't tell git about a change, it doesn't discover it on its own. This ensures git is honoring the extension and that we get the performance benefits desired.
Three test integration scripts are provided:
fsmonitor-all - marks all files as dirty fsmonitor-none - marks no files as dirty fsmonitor-watchman - integrates with Watchman with debug logging
To run tests in the test suite while utilizing fsmonitor:
First copy t/t7519/fsmonitor-all to a location in your path and then set GIT_FORCE_PRELOAD_TEST=true and GIT_FSMONITOR_TEST=fsmonitor-all and run your tests.
Note: currently when using the test script fsmonitor-watchman on Windows, many tests fail due to a reported but not yet fixed bug in Watchman where it holds on to handles for directories and files which prevents the test directory from being cleaned up properly.
--- t/t7519-status-fsmonitor.sh | 259 ++++++++++++++++++++++++++++++++++++++++++++ t/t7519/fsmonitor-all | 23 ++++ t/t7519/fsmonitor-none | 21 ++++ t/t7519/fsmonitor-watchman | 128 ++++++++++++++++++++++ 4 files changed, 431 insertions(+) create mode 100755 t/t7519-status-fsmonitor.sh create mode 100755 t/t7519/fsmonitor-all create mode 100755 t/t7519/fsmonitor-none create mode 100755 t/t7519/fsmonitor-watchman
t/t7519-status-fsmonitor.sh new file +259 -0
@@ -0,0 +1,259 @@ +#!/bin/sh + +test_description='git status with file system watcher' + +. ./test-lib.sh + +# +# To run the entire git test suite using fsmonitor: +# +# copy t/t7519/fsmonitor-all to a location in your path and then set +# GIT_FSMONITOR_TEST=fsmonitor-all and run your tests. +# + +# Note, after "git reset --hard HEAD" no extensions exist other than 'TREE' +# "git update-index --fsmonitor" can be used to get the extension written +# before testing the results. + +clean_repo () { + git reset --hard HEAD && + git clean -fd +} + +dirty_repo () { + : >untracked && + : >dir1/untracked && + : >dir2/untracked && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + echo 4 >new && + echo 5 >dir1/new && + echo 6 >dir2/new +} + +write_integration_script() { + write_script .git/hooks/fsmonitor-test<<-\EOF + if [ "$#" -ne 2 ]; then + echo "$0: exactly 2 arguments expected" + exit 2 + fi + if [ "$1" != 1 ]; then + echo -e "Unsupported core.fsmonitor hook version.\n" >&2 + exit 1 + fi + printf "untracked\0" + printf "dir1/untracked\0" + printf "dir2/untracked\0" + printf "modified\0" + printf "dir1/modified\0" + printf "dir2/modified\0" + printf "new\0" + printf "dir1/new\0" + printf "dir2/new\0" + EOF +} + +test_lazy_prereq UNTRACKED_CACHE ' + { git update-index --test-untracked-cache; ret=$?; } && + test $ret -ne 1 +' + +test_expect_success 'setup' ' + mkdir -p .git/hooks && + : >tracked && + : >modified && + mkdir dir1 && + : >dir1/tracked && + : >dir1/modified && + mkdir dir2 && + : >dir2/tracked && + : >dir2/modified && + git -c core.fsmonitor= add . && + git -c core.fsmonitor= commit -m initial && + git config core.fsmonitor .git/hooks/fsmonitor-test && + cat >.gitignore <<-\EOF + .gitignore + expect* + actual* + marker* + EOF +' + +# test that the fsmonitor extension is off by default +test_expect_success 'fsmonitor extension is off by default' ' + test-dump-fsmonitor >actual && + grep "^no fsmonitor" actual +' + +# test that "update-index --fsmonitor" adds the fsmonitor extension +test_expect_success 'update-index --fsmonitor" adds the fsmonitor extension' ' + git update-index --fsmonitor && + test-dump-fsmonitor >actual && + grep "^fsmonitor last update" actual +' + +# test that "update-index --no-fsmonitor" removes the fsmonitor extension +test_expect_success 'update-index --no-fsmonitor" removes the fsmonitor extension' ' + git update-index --no-fsmonitor && + test-dump-fsmonitor >actual && + grep "^no fsmonitor" actual +' + +cat >expect <<EOF && +H dir1/modified +H dir1/tracked +H dir2/modified +H dir2/tracked +H modified +H tracked +EOF + +# test that all files returned by the script get flagged as invalid +test_expect_success 'all files returned by integration script get flagged as invalid' ' + write_integration_script && + dirty_repo && + git update-index --fsmonitor && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +h dir1/new +H dir1/tracked +H dir2/modified +h dir2/new +H dir2/tracked +H modified +h new +H tracked +EOF + +# test that newly added files are marked valid +test_expect_success 'newly added files are marked valid' ' + git add new && + git add dir1/new && + git add dir2/new && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +h dir1/new +h dir1/tracked +H dir2/modified +h dir2/new +h dir2/tracked +H modified +h new +h tracked +EOF + +# test that all unmodified files get marked valid +test_expect_success 'all unmodified files get marked valid' ' + # modified files result in update-index returning 1 + test_must_fail git update-index --refresh --force-write-index && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +h dir1/tracked +h dir2/modified +h dir2/tracked +h modified +h tracked +EOF + +# test that *only* files returned by the integration script get flagged as invalid +test_expect_success '*only* files returned by the integration script get flagged as invalid' ' + write_script .git/hooks/fsmonitor-test<<-\EOF && + printf "dir1/modified\0" + EOF + clean_repo && + git update-index --refresh --force-write-index && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + test_must_fail git update-index --refresh --force-write-index && + git ls-files -f >actual && + test_cmp expect actual +' + +# Ensure commands that call refresh_index() to move the index back in time +# properly invalidate the fsmonitor cache +test_expect_success 'refresh_index() invalidates fsmonitor cache' ' + write_script .git/hooks/fsmonitor-test<<-\EOF && + EOF + clean_repo && + dirty_repo && + git add . && + git commit -m "to reset" && + git reset HEAD~1 && + git status >actual && + git -c core.fsmonitor= status >expect && + test_i18ncmp expect actual +' + +# test fsmonitor with and without preloadIndex +preload_values="false true" +for preload_val in $preload_values +do + test_expect_success "setup preloadIndex to $preload_val" ' + git config core.preloadIndex $preload_val && + if [ $preload_val -eq true ] + then + GIT_FORCE_PRELOAD_TEST=$preload_val; export GIT_FORCE_PRELOAD_TEST + else + unset GIT_FORCE_PRELOAD_TEST + fi + ' + + # test fsmonitor with and without the untracked cache (if available) + uc_values="false" + test_have_prereq UNTRACKED_CACHE && uc_values="false true" + for uc_val in $uc_values + do + test_expect_success "setup untracked cache to $uc_val" ' + git config core.untrackedcache $uc_val + ' + + # Status is well tested elsewhere so we'll just ensure that the results are + # the same when using core.fsmonitor. + test_expect_success 'compare status with and without fsmonitor' ' + write_integration_script && + clean_repo && + dirty_repo && + git add new && + git add dir1/new && + git add dir2/new && + git status >actual && + git -c core.fsmonitor= status >expect && + test_i18ncmp expect actual + ' + + # Make sure it's actually skipping the check for modified and untracked + # (if enabled) files unless it is told about them. + test_expect_success "status doesn't detect unreported modifications" ' + write_script .git/hooks/fsmonitor-test<<-\EOF && + :>marker + EOF + clean_repo && + git status && + test_path_is_file marker && + dirty_repo && + rm -f marker && + git status >actual && + test_path_is_file marker && + test_i18ngrep ! "Changes not staged for commit:" actual && + if [ $uc_val -eq true ]; then test_i18ngrep ! "Untracked files:" actual; fi && + if [ $uc_val -eq false ]; then test_i18ngrep "Untracked files:" actual; fi && + rm -f marker + ' + done +done + +test_done
t/t7519/fsmonitor-all new file +23 -0
@@ -0,0 +1,23 @@ +#!/bin/sh +# +# An test hook script to integrate with git to test fsmonitor. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +#echo "$0 $*" >&2 + +if [ "$#" -ne 2 ] ; then + echo -e "$0: exactly 2 arguments expected\n" >&2 + exit 2 +fi + +if [ "$1" != 1 ] +then + echo -e "Unsupported core.fsmonitor hook version.\n" >&2 + exit 1 +fi + +echo "*"
t/t7519/fsmonitor-none new file +21 -0
@@ -0,0 +1,21 @@ +#!/bin/sh +# +# An test hook script to integrate with git to test fsmonitor. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +#echo "$0 $*" >&2 + +if [ "$#" -ne 2 ] ; then + echo -e "$0: exactly 2 arguments expected\n" >&2 + exit 2 +fi + +if [ "$1" != 1 ] +then + echo -e "Unsupported core.fsmonitor hook version.\n" >&2 + exit 1 +fi
t/t7519/fsmonitor-watchman new file +128 -0
129 lines not on this page · raw message
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
RE: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extension
t/t7519-status-fsmonitor.sh line 32
Quoted 11 lines
-----Original Message-----
t/t7519-status-fsmonitor.sh new file +10
-0+dirty_repo () { + : >untracked && + : >dir1/untracked && + : >dir2/untracked && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + echo 4 >new && + echo 5 >dir1/new && + echo 6 >dir2/new
If I add an untracked file named dir3/untracked to dirty_repo
(and write_integration_script), then "status doesn't detect
unreported modifications", below, fails. Did I do something wrong, or does this turn up a bug?
t/t7519-status-fsmonitor.sh line 207
Quoted 3 lines
t/t7519-status-fsmonitor.sh new file +3
-0+ test_expect_success "setup preloadIndex to $preload_val" ' + git config core.preloadIndex $preload_val && + if [ $preload_val -eq true ]
"-eq" is for numeric equality in POSIX shell. So this works if your /bin/sh is bash but not if it's e.g. dash. This happens twice more below. Use "=" instead.
RE: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extension
I think my comment here might have gotten lost, and I don't want it to because it's something I'm really worried about:
Quoted 26 lines
-----Original Message-----
From: David Turner
Sent: Friday, September 15, 2017 6:00 PM
To: 'Ben Peart' <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: RE: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extensiont/t7519-status-fsmonitor.sh line 32
Quoted 11 lines
-----Original Message-----
t/t7519-status-fsmonitor.sh new file +10
-0+dirty_repo () { + : >untracked && + : >dir1/untracked && + : >dir2/untracked && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + echo 4 >new && + echo 5 >dir1/new && + echo 6 >dir2/newIf I add an untracked file named dir3/untracked to
dirty_repo(andwrite_integration_script), then "status doesn't detect unreported modifications", below, fails. Did I do something wrong, or does this turn up a bug?
Re: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extension
Quoted 2 lines · David Turner
I think my comment here might have gotten lost, and I don't want it to because it's something I'm really worried about:
You have to update the test completely to ensure it passes. If you run the test with the '-v' option you will see the cause of the failure:
t7519-status-fsmonitor.sh: line 27: dir3/untracked: No such file or directory
To fix this, you will also need to update the 'setup' test to create the directory for the new untracked file to get created into. Then you will need to drop at least one file in it so that the directory is preserved through the 'git reset --hard' Then you have to update the several 'cat >expect' blocks to expect the new file.
In addition, the ability to avoid scanning for untracked files relies on the untracked cache. If you don't have another file that git is aware of in that directory then there won't be a cache entry and git will do the required scan and detect the new untracked file (by design).
Here is a patch to the test that updates it to meet all these requirements. I hope this helps.
t/t7519-status-fsmonitor.sh +16 -0
@@ -24,12 +24,14 @@ dirty_repo () { : >untracked && : >dir1/untracked && : >dir2/untracked && + : >dir3/untracked && echo 1 >modified && echo 2 >dir1/modified && echo 3 >dir2/modified && echo 4 >new && echo 5 >dir1/new && echo 6 >dir2/new + echo 7 >dir3/new } write_integration_script () {
@@ -47,12 +49,14 @@ write_integration_script () { printf "untracked\0" printf "dir1/untracked\0" printf "dir2/untracked\0" + printf "dir3/untracked\0" printf "modified\0" printf "dir1/modified\0" printf "dir2/modified\0" printf "new\0" printf "dir1/new\0" printf "dir2/new\0" + printf "dir3/new\0" EOF }
@@ -71,6 +75,8 @@ test_expect_success 'setup' ' mkdir dir2 && : >dir2/tracked && : >dir2/modified && + mkdir dir3 && + : >dir3/tracked && git -c core.fsmonitor= add . && git -c core.fsmonitor= commit -m initial && git config core.fsmonitor .git/hooks/fsmonitor-test &&
@@ -107,6 +113,7 @@ h dir1/modified H dir1/tracked h dir2/modified H dir2/tracked +H dir3/tracked h modified H tracked EOF
@@ -126,6 +133,7 @@ H dir1/modified H dir1/tracked H dir2/modified H dir2/tracked +H dir3/tracked H modified H tracked EOF
@@ -144,6 +152,7 @@ H dir1/modified H dir1/tracked H dir2/modified H dir2/tracked +H dir3/tracked H modified H tracked EOF
@@ -164,6 +173,8 @@ H dir1/tracked H dir2/modified h dir2/new H dir2/tracked +h dir3/new +H dir3/tracked H modified h new H tracked
@@ -174,6 +185,7 @@ test_expect_success 'newly added files are marked valid' ' git add new && git add dir1/new && git add dir2/new && + git add dir3/new && git ls-files -f >actual && test_cmp expect actual '
@@ -185,6 +197,8 @@ h dir1/tracked H dir2/modified h dir2/new h dir2/tracked +h dir3/new +h dir3/tracked H modified h new h tracked
@@ -203,6 +217,7 @@ H dir1/modified h dir1/tracked h dir2/modified h dir2/tracked +h dir3/tracked h modified h tracked EOF
@@ -269,6 +284,7 @@ do git add new && git add dir1/new && git add dir2/new && + git add dir3/new && git status >actual && git -c core.fsmonitor= status >expect && test_i18ncmp expect actual
Quoted 28 lines
Quoted 26 lines
-----Original Message-----
From: David Turner
Sent: Friday, September 15, 2017 6:00 PM
To: 'Ben Peart' <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: RE: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extensionReview
Quoted 11 lines
-----Original Message-----
(quoted diff) +10
-0+dirty_repo () { + : >untracked && + : >dir1/untracked && + : >dir2/untracked && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + echo 4 >new && + echo 5 >dir1/new && + echo 6 >dir2/newIf I add an untracked file named dir3/untracked to
dirty_repo(andwrite_integration_script), then "status doesn't detect unreported modifications", below, fails. Did I do something wrong, or does this turn up a bug?
Re: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extension
In reply to Ben Peartt/t7519/fsmonitor-all line 21
Quoted 5 lines · Ben Peart
t/t7519/fsmonitor-all new file +5
-0+if [ "$1" != 1 ] +then + echo -e "Unsupported core.fsmonitor hook version.\n" >&2 + exit 1 +fi
The echo -e not portable
(It was detected by a tighter version of the lint script,
which I have here, but not yet send to the list :-(
This will do:
echo "Unsupported core.fsmonitor hook version." >&2
PATCH 1/1 test-lint: echo -e (or -E) is not portable
From: Torsten Bögershausen <[email protected]>
Some implementations of echo support the '-e' option to enable backslash interpretation of the following string.
As an addition, they support '-E' to turn it off.
However, none of these are portable, POSIX doesn't even mention them, and many implementations don't support them.
A check for '-n' is already done in check-non-portable-shell.pl, extend it to cover '-n', '-e' or '-E-'
--- t/check-non-portable-shell.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
t/check-non-portable-shell.pl +1 -1
@@ -17,7 +17,7 @@ sub err { while (<>) { chomp; /\bsed\s+-i/ and err 'sed -i is not portable'; - /\becho\s+-n/ and err 'echo -n is not portable (please use printf)'; + /\becho\s+-[neE]/ and err 'echo with option is not portable (please use printf)'; /^\s*declare\s+/ and err 'arrays/declare not portable'; /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)'; /\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
Signature · 1 line
2.14.1.145.gb3622a4ee9
Re: [PATCH v1 1/1] test-lint: echo -e (or -E) is not portable
Quoted 3 lines · Torsten Bögershausen
Some implementations of
echosupport the '-e' option to enable backslash interpretation of the following string.
As an addition, they support '-E' to turn it off.
nit: please wrap the commit message to a consistent line width.
Quoted 10 lines
However, none of these are portable, POSIX doesn't even mention them, and many implementations don't support them.
A check for '-n' is already done in check-non-portable-shell.pl, extend it to cover '-n', '-e' or '-E-'
Signed-off-by Torsten Bögershausen <[email protected]>--- t/check-non-portable-shell.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
An excellent change. Thanks for noticing and fixing this.
Re: [PATCH v1 1/1] test-lint: echo -e (or -E) is not portable
Quoted 14 lines · Jonathan Nieder
Quoted 3 lines · Torsten Bögershausen
Some implementations of
echosupport the '-e' option to enable backslash interpretation of the following string.
As an addition, they support '-E' to turn it off.nit: please wrap the commit message to a consistent line width.
Quoted 6 lines
However, none of these are portable, POSIX doesn't even mention them, and many implementations don't support them.
A check for '-n' is already done in check-non-portable-shell.pl, extend it to cover '-n', '-e' or '-E-'
Quoted 8 lines
Quoted 4 lines
Signed-off-by Torsten Bögershausen <[email protected]>--- t/check-non-portable-shell.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)An excellent change. Thanks for noticing and fixing this.
Reviewed-by Jonathan Nieder <[email protected]>
Thanks for the review.
Junio, if you wouldn't mind to squash that in, another fix is needed as well(trailing '-' after '-E') :
s/'-n', '-e' or '-E-'/'-n', '-e' or '-E'
^
Re: [PATCH v1 1/1] test-lint: echo -e (or -E) is not portable
Quoted 4 lines · Torsten Bögershausen
Junio, if you wouldn't mind to squash that in, another fix is needed as well(trailing '-' after '-E') :
s/'-n', '-e' or '-E-'/'-n', '-e' or '-E'
Yup. Thanks all.
Re: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extension
In reply to Torsten BögershausenQuoted 11 lines · Torsten Bögershausen
t/t7519/fsmonitor-all line 21
Quoted 5 lines · Ben Peart
t/t7519/fsmonitor-all new file +5
-0+if [ "$1" != 1 ] +then + echo -e "Unsupported core.fsmonitor hook version.\n" >&2 + exit 1 +fiThe echo -e not portable
(It was detected by a tighter version of the lint script,
which I have here, but not yet send to the list :-(
Quoted 3 lines
This will do:
echo "Unsupported core.fsmonitor hook version." >&2
Thanks, I'll fix these and the ones in the t/t7519 directory.
Re: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extension
In reply to Ben Peartt/t7519-status-fsmonitor.sh line 44
Quoted 10 lines · Ben Peart
t/t7519-status-fsmonitor.sh new file +10
-0+write_integration_script() { + write_script .git/hooks/fsmonitor-test<<-\EOF + if [ "$#" -ne 2 ]; then + echo "$0: exactly 2 arguments expected" + exit 2 + fi + if [ "$1" != 1 ]; then + echo -e "Unsupported core.fsmonitor hook version.\n" >&2 + exit 1 + fi
In addition to "echo -e" thing pointed out earlier, these look somewhat unusual in our shell scripts, relative to what Documentation/CodingGuidelines tells us to do:
- We prefer a space between the function name and the parentheses, and no space inside the parentheses. The opening "{" should also be on the same line.
(incorrect)
my_function(){
...
(correct)
my_function () {
...- We prefer "test" over "[ ... ]".
- Do not write control structures on a single line with semicolon. "then" should be on the next line for if statements, and "do" should be on the next line for "while" and "for".
(incorrect)
if test -f hello; then
do this
fi
(correct)
if test -f hello
then
do this
fi
Quoted 15 lines
t/t7519/fsmonitor-watchman new file +9
-0@@ -0,0 +1,128 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + ... + open (my $fh, ">", ".git/watchman-query.json"); + print $fh "[\"query\", \"$git_work_tree\", { \ + \"since\": $time, \
t/t7519/fsmonitor-watchman line 19
Quoted 10 lines
t/t7519/fsmonitor-watchman new file +10
-0+ \"fields\": [\"name\"], \ + \"expression\": [\"not\", [\"allof\", [\"since\", $time, \"cclock\"], [\"not\", \"exists\"]]] \ + }]"; + close $fh; + + print CHLD_IN "[\"query\", \"$git_work_tree\", { \ + \"since\": $time, \ + \"fields\": [\"name\"], \ + \"expression\": [\"not\", [\"allof\", [\"since\", $time, \"cclock\"], [\"not\", \"exists\"]]] \ + }]";
This look painful to read, write and maintain. IIRC, Perl supports the <<HERE document syntax quite similar to shell; would it make these "print" we see above easier?
t/t7519/fsmonitor-watchman line 20
Quoted 2 lines
t/t7519/fsmonitor-watchman new file +1
-0+}
Oops.
Thanks.
Re: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extension
Quoted 16 lines · Junio C Hamano
t/t7519-status-fsmonitor.sh line 44
Quoted 10 lines · Ben Peart
t/t7519-status-fsmonitor.sh new file +10
-0+write_integration_script() { + write_script .git/hooks/fsmonitor-test<<-\EOF + if [ "$#" -ne 2 ]; then + echo "$0: exactly 2 arguments expected" + exit 2 + fi + if [ "$1" != 1 ]; then + echo -e "Unsupported core.fsmonitor hook version.\n" >&2 + exit 1 + fiIn addition to "echo -e" thing pointed out earlier, these look somewhat unusual in our shell scripts, relative to what
Documentation/CodingGuidelinestells us to do:
I'm happy to make these changes. I understand the difficulty of creating a consistent coding style especially after the fact.
<soapbox>
Copy/paste is usually a developers best friend as it allows you to avoid a lot of errors by reusing existing tested code. One of the times it backfires is when that code doesn't match the current desired coding style.
I only point these out to help lend some additional impetus to the effort to formalize the coding style and to provide tooling to handle what should mostly be a mechanical process. IMO, the goal should be to save the maintainer and contributors the cost of having to write up and respond to formatting feedback. :)
Some stats on these same coding style errors in the current bash scripts:
298 instances of "[a-z]\(\).*\{" ie "function_name() {" (no space) 140 instances of "if \[ .* \]" (not using the preferred "test") 293 instances of "if .*; then"
Wouldn't it be great not to have to write up style feedback for when these all get copy/pasted into new scripts? :)
</soapbox>
Quoted 51 lines
- We prefer a space between the function name and the parentheses,
and no space inside the parentheses. The opening "{" should also be on the same line. (incorrect) my_function(){ ... (correct) my_function () { ...- We prefer "test" over "[ ... ]".
- Do not write control structures on a single line with semicolon. "then" should be on the next line for if statements, and "do" should be on the next line for "while" and "for".
(incorrect)
if test -f hello; then
do this
fi(correct)
if test -f hello
then
do this
fiQuoted 21 lines
t/t7519/fsmonitor-watchman new file +15
-0@@ -0,0 +1,128 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + ... + open (my $fh, ">", ".git/watchman-query.json"); + print $fh "[\"query\", \"$git_work_tree\", { \ + \"since\": $time, \ + \"fields\": [\"name\"], \ + \"expression\": [\"not\", [\"allof\", [\"since\", $time, \"cclock\"], [\"not\", \"exists\"]]] \ + }]"; + close $fh; + + print CHLD_IN "[\"query\", \"$git_work_tree\", { \
Quoted 9 lines
t/t7519/fsmonitor-watchman line 65
Quoted 4 lines
t/t7519/fsmonitor-watchman new file +4
-0+ \"since\": $time, \ + \"fields\": [\"name\"], \ + \"expression\": [\"not\", [\"allof\", [\"since\", $time, \"cclock\"], [\"not\", \"exists\"]]] \ + }]";This look painful to read, write and maintain. IIRC, Perl supports the <<HERE document syntax quite similar to shell; would it make these "print" we see above easier?
I agree! I'm definitely *not* a perl developer so was unaware of this construct. A few minutes with stack overflow and now I can clean this up.
Quoted 7 lines
t/t7519/fsmonitor-watchman line 20
Quoted 2 lines
t/t7519/fsmonitor-watchman new file +1
-0+}Oops.
Thanks.
Re: [PATCH v6 10/12] fsmonitor: add test cases for fsmonitor extension
Quoted 8 lines · Ben Peart
Some stats on these same coding style errors in the current bash scripts:
298 instances of "[a-z]\(\).*\{" ie "
function_name(){" (no space) 140 instances of "if \[ .* \]" (not using the preferred "test") 293 instances of "if .*; then"Wouldn't it be great not to have to write up style feedback for when these all get copy/pasted into new scripts?
Agreed. Care to write patches for these? :) (I think three patches, one for each issue, would do the trick.)
Thanks,
Jonathan
PATCH v6 11/12 fsmonitor: add a sample integration script for Watchman
In reply to Ben PeartThis script integrates the new fsmonitor capabilities of git with the cross platform Watchman file watching service. To use the script:
Download and install Watchman from https://facebook.github.io/watchman/.
Rename the sample integration hook from fsmonitor-watchman.sample to fsmonitor-watchman. Configure git to use the extension:
git config core.fsmonitor .git/hooks/fsmonitor-watchman
Optionally turn on the untracked cache for optimal performance.
--- templates/hooks--fsmonitor-watchman.sample | 119 +++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100755 templates/hooks--fsmonitor-watchman.sample
templates/hooks--fsmonitor-watchman.sample new file +119 -0
@@ -0,0 +1,119 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $time) = @ARGV; + +# Check the hook interface version + +if ($version == 1) { + # convert nanoseconds to seconds + $time = int $time / 1000000000; +} else { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +# Convert unix style paths to escaped Windows style paths when running +# in Windows command prompt + +my $system = `uname -s`; +$system =~ s/[\r\n]+//g; +my $git_work_tree; + +if ($system =~ m/^MSYS_NT/) { + $git_work_tree = `cygpath -aw "\$PWD"`; + $git_work_tree =~ s/[\r\n]+//g; + $git_work_tree =~ s,\\,/,g; +} else { + $git_work_tree = $ENV{'PWD'}; +} + +my $retry = 1; + +launch_watchman(); + +sub launch_watchman { + + # Set input record separator + local $/ = 0666; + + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $time but were not transient (ie created after + # $time but no longer exist). + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + # + # The category of transient files that we want to ignore will have a + # creation clock (cclock) newer than $time_t value and will also not + # currently exist. + + print CHLD_IN "[\"query\", \"$git_work_tree\", { \ + \"since\": $time, \ + \"fields\": [\"name\"], \ + \"expression\": [\"not\", [\"allof\", [\"since\", $time, \"cclock\"], [\"not\", \"exists\"]]] \ + }]"; + + my $response = <CHLD_OUT>; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + my $json_pkg; + eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; + } or do { + require JSON::PP; + $json_pkg = "JSON::PP"; + }; + + my $o = $json_pkg->new->utf8->decode($response); + + if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) { + print STDERR "Adding '$git_work_tree' to watchman's watch list.\n"; + $retry--; + qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + print "*\0"; + eval { launch_watchman() }; + exit 0; + } + + die "Watchman: $o->{error}.\n" . + "Falling back to scanning...\n" if $o->{error}; + + binmode STDOUT, ":utf8"; + local $, = "\0"; + print @{$o->{files}}; +}
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
PATCH v6 12/12 fsmonitor: add a performance test
In reply to Ben PeartAdd a test utility (test-drop-caches) that flushes all changes to disk then drops file system cache on Windows, Linux, and OSX.
Add a perf test (p7519-fsmonitor.sh) for fsmonitor.
By default, the performance test will utilize the Watchman file system monitor if it is installed. If Watchman is not installed, it will use a dummy integration script that does not report any new or modified files. The dummy script has very little overhead which provides optimistic results.
The performance test will also use the untracked cache feature if it is available as fsmonitor uses it to speed up scanning for untracked files.
There are 3 environment variables that can be used to alter the default behavior of the performance test:
GIT_PERF_7519_UNTRACKED_CACHE: used to configure core.untrackedCacheGIT_PERF_7519_SPLIT_INDEX: used to configure core.splitIndexGIT_PERF_7519_FSMONITOR: used to configure core.fsMonitor
The big win for using fsmonitor is the elimination of the need to scan the working directory looking for changed and untracked files. If the file information is all cached in RAM, the benefits are reduced.
GIT_PERF_7519_DROP_CACHE: if set, the OS caches are dropped between tests
--- Makefile | 1 + t/helper/.gitignore | 1 + t/helper/test-drop-caches.c | 161 ++++++++++++++++++++++++++++++++++++++ t/perf/p7519-fsmonitor.sh | 184 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+) create mode 100644 t/helper/test-drop-caches.c create mode 100755 t/perf/p7519-fsmonitor.sh
Makefile +1 -0
@@ -638,6 +638,7 @@ TEST_PROGRAMS_NEED_X += test-ctype TEST_PROGRAMS_NEED_X += test-config TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta +TEST_PROGRAMS_NEED_X += test-drop-caches TEST_PROGRAMS_NEED_X += test-dump-cache-tree TEST_PROGRAMS_NEED_X += test-dump-fsmonitor TEST_PROGRAMS_NEED_X += test-dump-split-index
t/helper/.gitignore +1 -0
@@ -3,6 +3,7 @@ /test-config /test-date /test-delta +/test-drop-caches /test-dump-cache-tree /test-dump-split-index /test-dump-untracked-cache
t/helper/test-drop-caches.c new file +161 -0
161 lines not on this page · raw message
t/perf/p7519-fsmonitor.sh new file +184 -0
184 lines not on this page · raw message
Signature · 1 line
2.14.1.548.ge54b1befee.dirty
RE: [PATCH v6 12/12] fsmonitor: add a performance test
t/perf/p7519-fsmonitor.sh line 98
Quoted 3 lines
-----Original Message-----
t/perf/p7519-fsmonitor.sh new file +1
-0+ # Choose integration script based on existance of Watchman.
Spelling: existence
Re: [PATCH v6 12/12] fsmonitor: add a performance test
In reply to Ben PeartHi Ben,
sorry for not catching this earlier:
Quoted 7 lines · Ben Peart
[...]
(quoted diff) +6
-0+ +int cmd_dropcaches(void) +{ + HANDLE hProcess = GetCurrentProcess(); + HANDLE hToken; + int status;
Review
Quoted 10 lines
(quoted diff) +10
-0+ + if (!OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) + return error("Can't open current process token"); + + if (!GetPrivilege(hToken, "SeProfileSingleProcessPrivilege", 1)) + return error("Can't get SeProfileSingleProcessPrivilege"); + + CloseHandle(hToken); + + HMODULE ntdll = LoadLibrary("ntdll.dll");
Git's source code still tries to abide by C90, and for simplicity's sake, this extends to the Windows-specific part. Therefore, the ntdll variable needs to be declared at the beginning of the function (I do agree that it makes for better code to reduce the scope of variables, but C90 simply did not allow variables to be declared in the middle of functions).
I wanted to send a patch address this in the obvious way, but then I encountered these lines:
Review
Quoted 4 lines
(quoted diff) +4
-0+ DWORD(WINAPI *NtSetSystemInformation)(INT, PVOID, ULONG) = + (DWORD(WINAPI *)(INT, PVOID, ULONG))GetProcAddress(ntdll, "NtSetSystemInformation"); + if (!NtSetSystemInformation) + return error("Can't get function addresses, wrong Windows version?");
It turns out that we have seen this plenty of times in Git for Windows' fork, so much so that we came up with a nice helper to make this all a bit more robust and a bit more obvious, too: the DECLARE_PROC_ADDR and INIT_PROC_ADDR helpers in compat/win32/lazyload.h.
Maybe this would be the perfect excuse to integrate this patch into upstream Git? This would be the patch (you can also cherry-pick it from 25c4dc3a73352e72e995594cf1b4afa46e93d040 in https://github.com/dscho/git):
-- snip --
From 25c4dc3a73352e72e995594cf1b4afa46e93d040 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <[email protected]>
Date: Tue, 10 Jan 2017 23:14:20 +0100
Subject: [PATCH] Win32: simplify loading of DLL functions
Dynamic loading of DLL functions is duplicated in several places in Git for Windows' source code.
This patch adds a pair of macros to simplify the process: the DECLARE_PROC_ADDR(<dll>, <return-type>, <function-name>, ...<function-parameter-types>...) macro to be used at the beginning of a code block, and the INIT_PROC_ADDR(<function-name>) macro to call before using the declared function. The return value of the INIT_PROC_ADDR() call has to be checked; If it is NULL, the function was not found in the specified DLL.
Example:
DECLARE_PROC_ADDR(kernel32.dll, BOOL, CreateHardLinkW,
LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
if (!INIT_PROC_ADDR(CreateHardLinkW))
return error("Could not find CreateHardLinkW() function";
if (!CreateHardLinkW(source, target, NULL))
return error("could not create hardlink from %S to %S",
source, target);
return 0;--- compat/win32/lazyload.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 compat/win32/lazyload.h
compat/win32/lazyload.h new file +44 -0
@@ -0,0 +1,44 @@ +#ifndef LAZYLOAD_H +#define LAZYLOAD_H + +/* simplify loading of DLL functions */ + +struct proc_addr { +const char *const dll; +const char *const function; +FARPROC pfunction; +unsigned initialized :1 ; +}; + +/* Declares a function to be loaded dynamically from a DLL. */ +#define DECLARE_PROC_ADDR (dll, rettype, function, ...) \ + static struct proc_addr proc_addr_##function = \ + { #dll, #function, NULL, 0 }; \ + static rettype (WINAPI *function)(__VA_ARGS__) + +/* +* Loads a function from a DLL (once-only). +* Returns non-NULL function pointer on success. +* Returns NULL + errno == ENOSYS on failure. +*/ +#define INIT_PROC_ADDR (function) \ + (function = get_proc_addr(&proc_addr_##function)) + +static inline void *get_proc_addr (struct proc_addr *proc) +{ +/* only do this once */ +if (!proc->initialized) { +HANDLE hnd; + proc->initialized =1 ; + hnd =LoadLibraryExA (proc->dll,NULL , +LOAD_LIBRARY_SEARCH_SYSTEM32 ); +if (hnd) + proc->pfunction =GetProcAddress (hnd, proc->function); + } +/* set ENOSYS if DLL or function was not found */ +if (!proc->pfunction) + errno =ENOSYS ; +return proc->pfunction; +} + +#endif
-- snap --
With this patch, this fixup to your patch would make things compile (you can also cherry-pick d05996fb61027512b8ab31a36c4a7a677dea11bb from my fork):
-- snipsnap --
From d05996fb61027512b8ab31a36c4a7a677dea11bb Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <[email protected]>
Date: Mon, 18 Sep 2017 14:56:40 +0200
Subject: [PATCH] fixup! fsmonitor: add a performance test
--- t/helper/test-drop-caches.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-)
t/helper/test-drop-caches.c +6 -8
@@ -1,6 +1,7 @@#include "git-compat-util.h" #if defined(GIT_WINDOWS_NATIVE ) +#include "compat/win32/lazyload.h" int cmd_sync (void ) {
@@ -82,6 +83,9 @@ int cmd_dropcaches(void)HANDLE hProcess =GetCurrentProcess ();HANDLE hToken;int status; +SYSTEM_MEMORY_LIST_COMMAND command; +DECLARE_PROC_ADDR (ntll, +DWORD , NtSetSystemInformation,INT ,PVOID ,ULONG );if (!OpenProcessToken (hProcess,TOKEN_QUERY |TOKEN_ADJUST_PRIVILEGES , &hToken))return error ("Can't open current process token" );
@@ -91,16 +95,10 @@ int cmd_dropcaches(void)CloseHandle (hToken); -HMODULE ntdll =LoadLibrary ("ntdll.dll" ); -if (!ntdll) - return error("Can't load ntdll.dll, wrong Windows version?"); - - DWORD(WINAPI *NtSetSystemInformation)(INT, PVOID, ULONG) = - (DWORD(WINAPI *)(INT, PVOID, ULONG))GetProcAddress(ntdll, "NtSetSystemInformation"); - if (!NtSetSystemInformation) + if (!INIT_PROC_ADDR(NtSetSystemInformation)) return error("Can't get function addresses, wrong Windows version?"); - SYSTEM_MEMORY_LIST_COMMAND command = MemoryPurgeStandbyList; + command = MemoryPurgeStandbyList; status = NtSetSystemInformation(
SystemMemoryListInformation,
&command,
@@ -111,8 +109,6 @@ int cmd_dropcaches(void)
else if (status != STATUS_SUCCESS)
error("Unable to execute the memory list command %d", status);
- FreeLibrary(ntdll);
-
return status;
}
Signature · 1 line
2.14.1.windows.1.510.g0cb6d35d23
RE: [PATCH v6 12/12] fsmonitor: add a performance test
Quoted 26 lines
-----Original Message-----
From: Johannes Schindelin [mailto:[email protected]]
Sent: Monday, September 18, 2017 10:25 AM
To: Ben Peart <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH v6 12/12] fsmonitor: add a performance testHi Ben,
sorry for not catching this earlier:
Review
Quoted 9 lines · Ben Peart
[...]
(quoted diff) +8
-0+ +int cmd_dropcaches(void) +{ + HANDLE hProcess = GetCurrentProcess(); + HANDLE hToken; + int status; + + if (!OpenProcessToken(hProcess, TOKEN_QUERY |
TOKEN_ADJUST_PRIVILEGES, &hToken))
Quoted 9 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ return error("Can't open current process token"); + + if (!GetPrivilege(hToken, "SeProfileSingleProcessPrivilege", 1)) + return error("Can't get SeProfileSingleProcessPrivilege"); + + CloseHandle(hToken); + + HMODULE ntdll = LoadLibrary("ntdll.dll");
Thanks Johannes, I'll fix that.
Quoted 21 lines
Git's source code still tries to abide by C90, and for simplicity's sake, this extends to the Windows-specific part. Therefore, the
ntdllvariable needs to be declared at the beginning of the function (I do agree that it makes for better code to reduce the scope of variables, but C90 simply did not allow variables to be declared in the middle of functions).I wanted to send a patch address this in the obvious way, but then I encountered these lines:
Review
Quoted 2 lines
(quoted diff) +2
-0+ DWORD(WINAPI *NtSetSystemInformation)(INT, PVOID, ULONG) = + (DWORD(WINAPI *)(INT, PVOID,ULONG))GetProcAddress(ntdll, "NtSetSystemInformation");
Review
Quoted 3 lines
(quoted diff) +3
-0+ if (!NtSetSystemInformation) + return error("Can't get function addresses, wrong Windows +version?");It turns out that we have seen this plenty of times in Git for Windows' fork, so much so that we came up with a nice helper to make this all a bit more robust and a bit more obvious, too: the
DECLARE_PROC_ADDRandINIT_PROC_ADDRhelpers incompat/win32/lazyload.h.
Quoted 2 lines
Maybe this would be the perfect excuse to integrate this patch into upstream Git?
This patch is pretty hefty already. How about you push this capability upstream and I take advantage of it in a later patch. :)
This would be the patch (you can also cherry-pick it from
Quoted 172 lines
25c4dc3a73352e72e995594cf1b4afa46e93d040in
https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.
com%2Fdscho%2Fgit&data=02%7C01%7CBen.Peart%40microsoft.com%7C96 4027bdc1f34a033c1f08d4fea1056e%7C72f988bf86f141af91ab2d7cd011db47 %7C1%7C0%7C636413414914282865&sdata=jyvu6G7myRY9UA1XxWx2tDZ% 2BWsIWqLTRMT8WfzEGe5g%3D&reserved=0):-- snip --
From25c4dc3a73352e72e995594cf1b4afa46e93d040Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <[email protected]>
Date: Tue, 10 Jan 2017 23:14:20 +0100
Subject: [PATCH] Win32: simplify loading of DLL functionsDynamic loading of DLL functions is duplicated in several places in Git for Windows' source code.
This patch adds a pair of macros to simplify the process: the
DECLARE_PROC_ADDR(<dll>, <return-type>, <function-name>, ...<function-parameter-types>...)macro to be used at the beginning of a code block, and theINIT_PROC_ADDR(<function-name>)macro to call before using the declared function. The return value of theINIT_PROC_ADDR()call has to be checked; If it is NULL, the function was not found in the specified DLL.Example:
DECLARE_PROC_ADDR(kernel32.dll, BOOL, CreateHardLinkW, LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); if (!INIT_PROC_ADDR(CreateHardLinkW)) return error("Could not find CreateHardLinkW() function"; if (!CreateHardLinkW(source, target, NULL)) return error("could not create hardlink from %S to %S", source, target); return 0;Signed-off-by Karsten Blees <[email protected]>Signed-off-by Johannes Schindelin <[email protected]>--- compat/win32/lazyload.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 compat/win32/lazyload.hcompat/win32/lazyload.h new file renamed from compat/win32/lazyload.h +0
-0mode 100644 index 00000000000..91c10dad2fb
compat/win32/lazyload.h new file +35
-0@@ -0,0 +1,44 @@ +#ifndef LAZYLOAD_H +#define LAZYLOAD_H + +/* simplify loading of DLL functions */ + +struct proc_addr { +const char *const dll; +const char *const function; +FARPROC pfunction; +unsigned initialized :1 ; +}; + +/* Declares a function to be loaded dynamically from a DLL. */ #define +DECLARE_PROC_ADDR (dll, rettype, function, ...) \ + static struct proc_addr proc_addr_##function = \ + { #dll, #function, NULL, 0 }; \ + static rettype (WINAPI *function)(__VA_ARGS__) + +/* +* Loads a function from a DLL (once-only). +* Returns non-NULL function pointer on success. +* Returns NULL + errno == ENOSYS on failure. +*/ +#define INIT_PROC_ADDR (function) \ + (function = get_proc_addr(&proc_addr_##function)) + +static inline void *get_proc_addr (struct proc_addr *proc) { +/* only do this once */ +if (!proc->initialized) { +HANDLE hnd; + proc->initialized =1 ; + hnd =LoadLibraryExA (proc->dll,NULL , +LOAD_LIBRARY_SEARCH_SYSTEM32 ); +if (hnd) + proc->pfunction = GetProcAddress(hnd, proc-Quoted 1 line
function);
compat/win32/lazyload.h new file +8
-1+ } +/* set ENOSYS if DLL or function was not found */ +if (!proc->pfunction) + errno =ENOSYS ; +return proc->pfunction; +} + +#endif -- snap --With this patch, this fixup to your patch would make things compile (you can also cherry-pick
d05996fb61027512b8ab31a36c4a7a677dea11bbfrom my fork):-- snipsnap --
Fromd05996fb61027512b8ab31a36c4a7a677dea11bbMon Sep 17 00:00:00 2001
From: Johannes Schindelin <[email protected]>
Date: Mon, 18 Sep 2017 14:56:40 +0200
Subject: [PATCH] fixup! fsmonitor: add a performance test--- t/helper/test-drop-caches.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-)t/helper/test-drop-caches.c index renamed from t/helper/test-drop-caches.c +0
-0
717079865cb..b27358528f7100644t/helper/test-drop-caches.c +4
-0@@ -1,6 +1,7 @@#include "git-compat-util.h" #if defined(GIT_WINDOWS_NATIVE ) +#include "compat/win32/lazyload.h" int cmd_sync (void ) {@@ -82,6 +83,9 @@ int cmd_dropcaches(void)HANDLE hProcess =GetCurrentProcess ();HANDLE hToken;int status; +SYSTEM_MEMORY_LIST_COMMAND command; +DECLARE_PROC_ADDR (ntll, +DWORD , NtSetSystemInformation,INT ,PVOID ,ULONG ); if (!OpenProcessToken(hProcess, TOKEN_QUERY |
TOKEN_ADJUST_PRIVILEGES, &hToken))t/helper/test-drop-caches.c +1
-6return error("Can't open current process token"); @@ -91,16 +95,10 @@ int cmd_dropcaches(void) CloseHandle(hToken); -HMODULE ntdll =LoadLibrary ("ntdll.dll" ); - if (!ntdll) - return error("Can't load ntdll.dll, wrong Windows version?"); - - DWORD(WINAPI *NtSetSystemInformation)(INT, PVOID, ULONG) = - (DWORD(WINAPI *)(INT, PVOID,ULONG))GetProcAddress(ntdll,
t/helper/test-drop-caches.c +1
-1"NtSetSystemInformation"); -if (!NtSetSystemInformation) +if (!INIT_PROC_ADDR (NtSetSystemInformation)) return error("Can't get function addresses, wrong Windowsversion?");
t/helper/test-drop-caches.c +0
-1- SYSTEM_MEMORY_LIST_COMMAND command =MemoryPurgeStandbyList;
t/helper/test-drop-caches.c +1
-3+ command = MemoryPurgeStandbyList; status = NtSetSystemInformation( SystemMemoryListInformation, &command,@@ -111,8 +109,6 @@ int cmd_dropcaches(void)else if (status !=STATUS_SUCCESS )error ("Unable to execute the memory list command %d" , status); -FreeLibrary (ntdll); -return status; } --2.14.1.windows.1.510.g0cb6d35d23
RE: [PATCH v6 12/12] fsmonitor: add a performance test
Hi Ben,
Quoted 15 lines · Ben Peart
Quoted 15 lines
From: Johannes Schindelin [mailto:[email protected]]
Review
Quoted 2 lines · Ben Peart
(quoted diff) +2
-0+ DWORD(WINAPI *NtSetSystemInformation)(INT, PVOID, ULONG) = + (DWORD(WINAPI *)(INT, PVOID,ULONG))GetProcAddress(ntdll, "NtSetSystemInformation");
Review
Quoted 3 lines
(quoted diff) +3
-0+ if (!NtSetSystemInformation) + return error("Can't get function addresses, wrong Windows +version?");It turns out that we have seen this plenty of times in Git for Windows' fork, so much so that we came up with a nice helper to make this all a bit more robust and a bit more obvious, too: the
DECLARE_PROC_ADDRandINIT_PROC_ADDRhelpers incompat/win32/lazyload.h.
Quoted 5 lines
Quoted 2 lines
Maybe this would be the perfect excuse to integrate this patch into upstream Git?
This patch is pretty hefty already. How about you push this capability upstream and I take advantage of it in a later patch. :)
Ciao,
Johannes
v7 cover Fast git status via a file system watcher
In reply to Ben PeartSubject: Fast git status via a file system watcher
Thanks to everyone who provided feedback. There are lots of minor style changes, documentation updates and a fixed leak.
The only functional change is the addition of support to set/clear the fsmonitor valid bit via 'git update-index --[no-]fsmonitor-valid' with the associated documentation and tests.
Interdiff between V6 and V7:
Documentation/config.txt +1 -0
@@ -418,6 +418,7 @@ core.fsmonitor:: will identify all files that may have changed since the requested date/time. This information is used to speed up git by avoiding unnecessary processing of files that have not changed. + See the "fsmonitor-watchman" section of linkgit:githooks[5]. core.trustctime:: If false, the ctime differences between the index and the
Documentation/git-ls-files.txt +6 -1
@@ -9,7 +9,7 @@ git-ls-files - Show information about files in the index and the working tree SYNOPSIS -------- [verse] -'git ls-files' [-z] [-t] [-v] +'git ls-files' [-z] [-t] [-v] [-f] (--[cached|deleted|others|ignored|stage|unmerged|killed|modified])* (-[c|d|o|i|s|u|k|m])* [--eol]
@@ -133,6 +133,11 @@ a space) at the start of each line: that are marked as 'assume unchanged' (see linkgit:git-update-index[1]). +-f:: + Similar to `-t`, but use lowercase letters for files + that are marked as 'fsmonitor valid' (see + linkgit:git-update-index[1]). + --full-name:: When run from a subdirectory, the command usually outputs paths relative to the current directory. This
Documentation/git-update-index.txt +45 -0
72 lines not on this page · raw message
Documentation/githooks.txt +8 -3
@@ -455,10 +455,8 @@ the name of the file that holds the e-mail to be sent. Exiting with a non-zero status causes 'git send-email' to abort before sending any e-mails. - -[[fsmonitor-watchman]] fsmonitor-watchman -~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~ This hook is invoked when the configuration option core.fsmonitor is set to .git/hooks/fsmonitor-watchman. It takes two arguments, a version
@@ -471,10 +469,17 @@ should be inclusive so that it does not miss any potential changes. The paths should be relative to the root of the working directory and be separated by a single NUL. +It is OK to include files which have not actually changed. All changes +including newly-created and deleted files should be included. When +files are renamed, both the old and the new name should be included. + Git will limit what files it checks for changes as well as which directories are checked for untracked files based on the path names given. +An optimized way to tell git "all files have changed" is to return +the filename '/'. + The exit status determines whether git will use the data from the hook to limit its search. On error, it will fall back to verifying all files and folders.
builtin/update-index.c +13 -1
@@ -33,6 +33,7 @@ static int force_remove;static int verbose;static int mark_valid_only;static int mark_skip_worktree_only; +static int mark_fsmonitor_only;#define MARK_FLAG 1#define UNMARK_FLAG 2static struct strbuf mtime_dir =STRBUF_INIT ;
@@ -229,12 +230,12 @@ static int mark_ce_flags(const char *path, int flag, int mark)int namelen =strlen (path);int pos =cache_name_pos (path, namelen);if (0 <= pos) { +mark_fsmonitor_invalid (&the_index, active_cache[pos]);if (mark) active_cache[pos]->ce_flags |= flag;else active_cache[pos]->ce_flags &= ~flag; active_cache[pos]->ce_flags |=CE_UPDATE_IN_BASE ; -mark_fsmonitor_invalid (&the_index, active_cache[pos]);cache_tree_invalidate_path (&the_index, path); active_cache_changed |=CE_ENTRY_CHANGED ;return 0 ;
@@ -460,6 +461,11 @@ static void update_one(const char *path)die ("Unable to mark file %s" , path);return ; } +if (mark_fsmonitor_only) { +if (mark_ce_flags (path,CE_FSMONITOR_VALID , mark_fsmonitor_only ==MARK_FLAG )) +die ("Unable to mark file %s" , path); +return ; + }if (force_remove) {if (remove_file_from_cache (path))
@@ -1014,6 +1020,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("write out the index even if is not flagged as changed"), 1), OPT_BOOL(0, "fsmonitor", &fsmonitor, N_("enable or disable file system monitor")), + {OPTION_SET_INT ,0 ,"fsmonitor-valid" , &mark_fsmonitor_only,NULL , +N_ ("mark files as fsmonitor valid" ), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, + {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL, +N_ ("clear fsmonitor valid bit" ), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},OPT_END () };
fsmonitor.c +2 -2
@@ -57,12 +57,12 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data,/* Mark all previously saved entries as dirty */ ewah_each_bit (fsmonitor_dirty, fsmonitor_ewah_callback, istate); -ewah_free (fsmonitor_dirty);/* Now mark the untracked cache for fsmonitor usage */ if (istate->untracked) istate->untracked->use_fsmonitor =1 ; } +ewah_free (fsmonitor_dirty);trace_printf_key (&trace_fsmonitor,"read fsmonitor extension successful" );return 0 ;
@@ -177,7 +177,7 @@ void refresh_fsmonitor(struct index_state *istate) }/* a fsmonitor process can return '*' to indicate all entries are invalid */ -if (query_success && query_result.buf[0 ] !='*' ) { +if (query_success && query_result.buf[0 ] !='/' ) {/* Mark all entries returned by the monitor as dirty */ buf = query_result.buf; bol =0 ;
fsmonitor.h +1 -1
@@ -46,7 +46,7 @@ static inline void mark_fsmonitor_valid(struct cache_entry *ce) }/* -* Clear the given cache entries CE_FSMONITOR_VALID bit and invalidate any +* Clear the given cache entry's CE_FSMONITOR_VALID bit and invalidate any * corresponding untracked cache directory structures. */ static inline void mark_fsmonitor_invalid (struct index_state *istate,struct cache_entry *ce)
t/helper/test-drop-caches.c +11 -10
@@ -2,7 +2,7 @@#if defined(GIT_WINDOWS_NATIVE ) -int cmd_sync (void ) +static int cmd_sync (void ) {char Buffer[MAX_PATH ];DWORD dwRet;
@@ -49,7 +49,7 @@ typedef enum _SYSTEM_MEMORY_LIST_COMMAND { MemoryCommandMax }SYSTEM_MEMORY_LIST_COMMAND ; -BOOL GetPrivilege (HANDLE TokenHandle,LPCSTR lpName,int flags) +static BOOL GetPrivilege (HANDLE TokenHandle,LPCSTR lpName,int flags) {BOOL bResult;DWORD dwBufferLength;
@@ -77,10 +77,11 @@ BOOL GetPrivilege(HANDLE TokenHandle, LPCSTR lpName, int flags)return bResult; } -int cmd_dropcaches (void ) +static int cmd_dropcaches (void ) {HANDLE hProcess =GetCurrentProcess ();HANDLE hToken; +HMODULE ntdll;int status;if (!OpenProcessToken (hProcess,TOKEN_QUERY |TOKEN_ADJUST_PRIVILEGES , &hToken))
@@ -91,7 +92,7 @@ int cmd_dropcaches(void)CloseHandle (hToken); -HMODULE ntdll =LoadLibrary ("ntdll.dll" ); + ntdll =LoadLibrary ("ntdll.dll" );if (!ntdll)return error ("Can't load ntdll.dll, wrong Windows version?" );
@@ -118,36 +119,36 @@ int cmd_dropcaches(void)#elif defined(__linux__) -int cmd_sync (void ) +static int cmd_sync (void ) {return system ("sync" ); } -int cmd_dropcaches (void ) +static int cmd_dropcaches (void ) {return system ("echo 3 | sudo tee /proc/sys/vm/drop_caches" ); }#elif defined(__APPLE__) -int cmd_sync (void ) +static int cmd_sync (void ) {return system ("sync" ); } -int cmd_dropcaches (void ) +static int cmd_dropcaches (void ) {return system ("sudo purge" ); }#else -int cmd_sync (void ) +static int cmd_sync (void ) {return 0 ; } -int cmd_dropcaches (void ) +static int cmd_dropcaches (void ) {return error ("drop caches not implemented on this platform" ); }
t/helper/test-dump-fsmonitor.c +1 -1
@@ -12,7 +12,7 @@ int cmd_main(int ac, const char **av)printf ("no fsmonitor\n" );return 0 ; } -printf ("fsmonitor last update %" PRIuMAX"\n" , istate->fsmonitor_last_update); +printf ("fsmonitor last update %" PRIuMAX"\n" , (uintmax_t )istate->fsmonitor_last_update);for (i =0 ; i < istate->cache_nr; i++)printf ((istate->cache[i]->ce_flags &CE_FSMONITOR_VALID ) ?"+" :"-" );
t/perf/p7519-fsmonitor.sh +1 -1
@@ -95,7 +95,7 @@ test_expect_success "setup for fsmonitor" ' INTEGRATION_SCRIPT="$GIT_PERF_7519_FSMONITOR" else # - # Choose integration script based on existance of Watchman. + # Choose integration script based on existence of Watchman. # If Watchman exists, watch the work tree and attempt a query. # If everything succeeds, use Watchman integration script, # else fall back to an empty integration script.
t/t7519-status-fsmonitor.sh +52 -7
87 lines not on this page · raw message
t/t7519/fsmonitor-all +6 -5
@@ -9,15 +9,16 @@ # #echo "$0 $*" >&2 -if [ "$#" -ne 2 ] ; then - echo -e "$0: exactly 2 arguments expected\n" >&2 +if test "$#" -ne 2 +then + echo "$0: exactly 2 arguments expected" >&2 exit 2 fi -if [ "$1" != 1 ] +if test "$1" != 1 then - echo -e "Unsupported core.fsmonitor hook version.\n" >&2 + echo "Unsupported core.fsmonitor hook version." >&2 exit 1 fi -echo "*" +echo "/"
t/t7519/fsmonitor-none +5 -4
@@ -9,13 +9,14 @@ # #echo "$0 $*" >&2 -if [ "$#" -ne 2 ] ; then - echo -e "$0: exactly 2 arguments expected\n" >&2 +if test "$#" -ne 2 +then + echo "$0: exactly 2 arguments expected" >&2 exit 2 fi -if [ "$1" != 1 ] +if test "$1" != 1 then - echo -e "Unsupported core.fsmonitor hook version.\n" >&2 + echo "Unsupported core.fsmonitor hook version." >&2 exit 1 fi
t/t7519/fsmonitor-watchman +36 -24
99 lines not on this page · raw message
templates/hooks--fsmonitor-watchman.sample +10 -7
@@ -69,12 +69,15 @@ sub launch_watchman { # creation clock (cclock) newer than $time_t value and will also not # currently exist. - print CHLD_IN "[\"query\", \"$git_work_tree\", { \ - \"since\": $time, \ - \"fields\": [\"name\"], \ - \"expression\": [\"not\", [\"allof\", [\"since\", $time, \"cclock\"], [\"not\", \"exists\"]]] \ - }]"; - + my $query = <<" END"; + ["query", "$git_work_tree", { + "since": $time, + "fields": ["name"], + "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]] + }] + END + + print CHLD_IN $query; my $response = <CHLD_OUT>; die "Watchman: command returned no output.\n" .
@@ -105,7 +108,7 @@ sub launch_watchman { # return the fast "everything is dirty" flag to git and do the # Watchman query just to get it over with now so we won't pay # the cost in git to look up each individual file. - print "*\0"; + print "/\0"; eval { launch_watchman() }; exit 0; }
Ben Peart (12):
bswap: add 64 bit endianness helper get_be64
preload-index: add override to enable testing preload-index
update-index: add a new --force-write-index option
fsmonitor: teach git to optionally utilize a file system monitor to
speed up detecting new or changed files.
fsmonitor: add documentation for the fsmonitor extension.
ls-files: Add support in ls-files to display the fsmonitor valid bit
update-index: add fsmonitor support to update-index
fsmonitor: add a test tool to dump the index extension
split-index: disable the fsmonitor extension when running the split
index test
fsmonitor: add test cases for fsmonitor extension
fsmonitor: add a sample integration script for Watchman
fsmonitor: add a performance test
Documentation/config.txt | 7 + Documentation/git-ls-files.txt | 7 +- Documentation/git-update-index.txt | 45 +++++ Documentation/githooks.txt | 28 +++ Documentation/technical/index-format.txt | 19 ++ Makefile | 3 + apply.c | 2 +- builtin/ls-files.c | 8 +- builtin/update-index.c | 38 +++- cache.h | 10 +- compat/bswap.h | 22 +++ config.c | 14 ++ config.h | 1 + diff-lib.c | 2 + dir.c | 27 ++- dir.h | 2 + entry.c | 4 +- environment.c | 1 + fsmonitor.c | 253 ++++++++++++++++++++++++ fsmonitor.h | 61 ++++++ preload-index.c | 8 +- read-cache.c | 49 ++++- submodule.c | 2 +- t/helper/.gitignore | 1 + t/helper/test-drop-caches.c | 162 +++++++++++++++ t/helper/test-dump-fsmonitor.c | 21 ++ t/perf/p7519-fsmonitor.sh | 184 +++++++++++++++++ t/t1700-split-index.sh | 1 + t/t7519-status-fsmonitor.sh | 304 +++++++++++++++++++++++++++++ t/t7519/fsmonitor-all | 24 +++ t/t7519/fsmonitor-none | 22 +++ t/t7519/fsmonitor-watchman | 140 +++++++++++++ templates/hooks--fsmonitor-watchman.sample | 122 ++++++++++++ unpack-trees.c | 8 +- 34 files changed, 1572 insertions(+), 30 deletions(-) create mode 100644 fsmonitor.c create mode 100644 fsmonitor.h create mode 100644 t/helper/test-drop-caches.c create mode 100644 t/helper/test-dump-fsmonitor.c create mode 100755 t/perf/p7519-fsmonitor.sh create mode 100755 t/t7519-status-fsmonitor.sh create mode 100755 t/t7519/fsmonitor-all create mode 100755 t/t7519/fsmonitor-none create mode 100755 t/t7519/fsmonitor-watchman create mode 100755 templates/hooks--fsmonitor-watchman.sample
Signature · 1 line
2.14.1.windows.1
PATCH v7 1/12 bswap: add 64 bit endianness helper get_be64
Add a new get_be64 macro to enable 64 bit endian conversions on memory that may or may not be aligned.
--- compat/bswap.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
compat/bswap.h +22 -0
@@ -158,7 +158,9 @@ static inline uint64_t git_bswap64(uint64_t x)#define get_be16 (p) ntohs(*(unsigned short *)(p))#define get_be32 (p) ntohl(*(unsigned int *)(p)) +#define get_be64 (p) ntohll(*(uint64_t *)(p))#define put_be32 (p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) +#define put_be64 (p, v) do { *(uint64_t *)(p) = htonll(v); } while (0)#else
@@ -178,6 +180,13 @@ static inline uint32_t get_be32(const void *ptr) (uint32_t )p[3 ] <<0 ; } +static inline uint64_t get_be64 (const void *ptr) +{ +const unsigned char *p = ptr; +return (uint64_t )get_be32 (p[0 ]) <<32 | + (uint64_t )get_be32 (p[4 ]) <<0 ; +} +static inline void put_be32 (void *ptr,uint32_t value) {unsigned char *p = ptr;
@@ -187,4 +196,17 @@ static inline void put_be32(void *ptr, uint32_t value) p[3] = value >> 0; } +static inline void put_be64 (void *ptr,uint64_t value) +{ +unsigned char *p = ptr; + p[0 ] = value >>56 ; + p[1 ] = value >>48 ; + p[2 ] = value >>40 ; + p[3 ] = value >>32 ; + p[4 ] = value >>24 ; + p[5 ] = value >>16 ; + p[6 ] = value >>8 ; + p[7 ] = value >>0 ; +} +#endif
Signature · 1 line
2.14.1.windows.1
PATCH v7 2/12 preload-index: add override to enable testing preload-index
In reply to Ben PeartPreload index doesn't run unless it has a minimum number of 1000 files. To enable running tests with fewer files, add an environment variable (GIT_FORCE_PRELOAD_TEST) which will override that minimum and set it to 2.
--- preload-index.c | 2 ++ 1 file changed, 2 insertions(+)
preload-index.c +2 -0
@@ -79,6 +79,8 @@ static void preload_index(struct index_state *index,return ; threads = index->cache_nr /THREAD_COST ; +if ((index->cache_nr >1 ) && (threads <2 ) &&getenv ("GIT_FORCE_PRELOAD_TEST" )) + threads =2 ;if (threads <2 )return ;if (threads >MAX_PARALLEL )
Signature · 1 line
2.14.1.windows.1
Re: [PATCH v7 02/12] preload-index: add override to enable testing preload-index
Quoted 3 lines · Ben Peart
Preload index doesn't run unless it has a minimum number of 1000 files. To enable running tests with fewer files, add an environment variable (
GIT_FORCE_PRELOAD_TEST) which will override that minimum and set it to 2.
'it' being the number of threads ('it' was not mentioned before, so reading the commit message confused me initially)
Quoted 11 lines
Signed-off-by Ben Peart <[email protected]>--- preload-index.c | 2 ++ 1 file changed, 2 insertions(+)preload-index.c +0
-0
preload-index.c static void preload_index(struct index_state *index, · line 83
Quoted 5 lines
preload-index.c +2
-0@@ -79,6 +79,8 @@ static void preload_index(struct index_state *index,return ; threads = index->cache_nr /THREAD_COST ; +if ((index->cache_nr >1 ) && (threads <2 ) &&getenv ("GIT_FORCE_PRELOAD_TEST" )) + threads =2 ;
Adding these lines is just a bandaid to trick
preload-index.c static void preload_index(struct index_state *index, · line 85
Quoted 2 lines
preload-index.c +0
-0if (threads <2 )return ;
to not return early as the commit message does not discuss why we set it to 2.
Do we need threads at all for these tests, or would a patch like
- if (threads < 2)
+ if (threads < 2 && !GIT_FORCE_PRELOAD_TEST)
return;
work as well?
That way tests can use any number of threads, though they currently have no way of overriding the heuristic, yet.
With this alternative patch, it sounds to me as if the issues kept more orthogonal. (specifically "Do we use preload?" and "How many threads?". One could imagine that we later want to introduce GIT_PRELOAD_THREADS for $reasons and that would go over well in combination with GIT_FORCE_PRELOAD_TEST)
It seems to be only an internal test variable, such that we do not need documentation. (Is that worth mentioning in the commit message?)
The test to make use of this new variable is found in another patch I presume?
Stefan
Re: [PATCH v7 02/12] preload-index: add override to enable testing preload-index
Quoted 27 lines · Stefan Beller
Quoted 3 lines · Ben Peart
Preload index doesn't run unless it has a minimum number of 1000 files. To enable running tests with fewer files, add an environment variable (
GIT_FORCE_PRELOAD_TEST) which will override that minimum and set it to 2.'it' being the number of threads ('it' was not mentioned before, so reading the commit message confused me initially)
Quoted 11 lines
Signed-off-by Ben Peart <[email protected]>--- preload-index.c | 2 ++ 1 file changed, 2 insertions(+)preload-index.c +0
-0preload-index.c static void preload_index(struct index_state *index, · line 83
Quoted 5 lines
preload-index.c +2
-0@@ -79,6 +79,8 @@ static void preload_index(struct index_state *index,return ; threads = index->cache_nr /THREAD_COST ; +if ((index->cache_nr >1 ) && (threads <2 ) &&getenv ("GIT_FORCE_PRELOAD_TEST" )) + threads =2 ;Adding these lines is just a bandaid to trick
Quoted 6 lines
preload-index.c static void preload_index(struct index_state *index, · line 85
Quoted 2 lines
preload-index.c +0
-0if (threads <2 )return ;to not return early as the commit message does not discuss why we set it to 2.
To execute the preload code path, we need a minimum of 2 cache entries and 2 threads so that each thread actually has work to do. Otherwise the logic below that divides the work up would need to be updated as well. The additional complexity didn't seem worth it just to enable the code path to execute with a single thread on a single cache entry.
Quoted 7 lines
Do we need threads at all for these tests, or would a patch like
(quoted diff) +1
-1- if (threads < 2) + if (threads < 2 && !GIT_FORCE_PRELOAD_TEST) return;work as well?
That would require a larger patch that would update the work division and thread creation logic for little to no gain.
Quoted 11 lines
That way tests can use any number of threads, though they currently have no way of overriding the heuristic, yet.
With this alternative patch, it sounds to me as if the issues kept more orthogonal. (specifically "Do we use preload?" and "How many threads?". One could imagine that we later want to introduce
GIT_PRELOAD_THREADSfor $reasons and that would go over well in combination withGIT_FORCE_PRELOAD_TEST)
I'm not sure why someone would want to test varying numbers of threads as that isn't a case that can ever actually happen with the existing code/feature.
I was just enabling testing of the code path with fewer than 1000 files as when I made my changes, I discovered that 1) there were no test cases for the core.preloadindex feature at all and 2) there was no way to run the existing tests with core.preloadindex as they don't have the minimum number of files. This patch allows you to run the existing test suite with preload_index turned on for any test case that has 2 or more files (it passes by the way :)).
Quoted 3 lines
It seems to be only an internal test variable, such that we do not need documentation. (Is that worth mentioning in the commit message?)
Correct. That is the reason I used the magic GIT_***_TEST naming pattern as that is the only way to ensure the environment variable is preserved when running the tests.
Quoted 4 lines
The test to make use of this new variable is found in another patch I presume?
There are no new tests that take advantage of this new environment variable. Instead you can run the entire git test suite with it by running:
GIT_FORCE_PRELOAD_TEXT=1 prove -j12 --state=all ./t[0-9]*.sh
I can add that to the commit message to make it more obvious if desired.
Quoted 2 lines
Stefan
Re: [PATCH v7 02/12] preload-index: add override to enable testing preload-index
Quoted 5 lines
To execute the preload code path, we need a minimum of 2 cache entries and 2 threads so that each thread actually has work to do. Otherwise the logic below that divides the work up would need to be updated as well. The additional complexity didn't seem worth it just to enable the code path to execute with a single thread on a single cache entry.
[snip]
Quoted 3 lines
That would require a larger patch that would update the work division and thread creation logic for little to no gain.
Oh I was not aware of the additional needed refactoring. (I assumed that would *just work*, experience should have told me to have a look first).
Quoted 7 lines
There are no new tests that take advantage of this new environment variable. Instead you can run the entire git test suite with it by running:
GIT_FORCE_PRELOAD_TEXT=1 prove -j12 --state=all ./t[0-9]*.sh
I can add that to the commit message to make it more obvious if desired.
it would have helped me being less confused as there was an "obvious" easier and more correct alternative, which you just explained is neither easier nor more correct as we probably do not desire that anyway.
Thanks,
Stefan
PATCH v7 3/12 update-index: add a new --force-write-index option
In reply to Ben PeartAt times, it makes sense to avoid the cost of writing out the index when the only changes can easily be recomputed on demand. This causes problems when trying to write test cases to verify that state as they can't guarantee the state has been persisted to disk.
Add a new option (--force-write-index) to update-index that will ensure the index is written out even if the cache_changed flag is not set.
--- builtin/update-index.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
builtin/update-index.c +4 -1
@@ -915,6 +915,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)struct refresh_params refresh_args = {0 , &has_errors};int lock_error =0 ;int split_index =-1 ; +int force_write =0 ;struct lock_file *lock_file;struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;
@@ -1006,6 +1007,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("test if the filesystem supports untracked cache"), UC_TEST),OPT_SET_INT (0 ,"force-untracked-cache" , &untracked_cache,N_ ("enable untracked cache without testing the filesystem" ),UC_FORCE ), +OPT_SET_INT (0 ,"force-write-index" , &force_write, +N_ ("write out the index even if is not flagged as changed" ),1 ), OPT_END() };
@@ -1147,7 +1150,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)die ("BUG: bad untracked_cache value: %d" , untracked_cache); } -if (active_cache_changed) { +if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )exit (128 );
Signature · 1 line
2.14.1.windows.1
Re: [PATCH v7 03/12] update-index: add a new --force-write-index option
builtin/update-index.c int cmd_update_index(int argc, const char **argv, const char *prefix) · line 1011
Quoted 2 lines · Ben Peart
builtin/update-index.c +2
-0+OPT_SET_INT (0 ,"force-write-index" , &force_write, + N_("write out the index even if is not flagged as changed"), 1),
Hmph. The only time this makes difference is when the code forgets to mark active_cache_changed even when it actually made a change to the index, no? I do understand the wish to be able to observe what _would_ be written if such a bug did not exist in order to debug the other aspects of the change in this series, but at the same time I fear that we may end up sweeping the problem under the rug by running the tests with this option.
Quoted 12 lines
builtin/update-index.c +1
-1OPT_END() };@@ -1147,7 +1150,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)die ("BUG: bad untracked_cache value: %d" , untracked_cache); } -if (active_cache_changed) { +if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )exit (128 );
Re: [PATCH v7 03/12] update-index: add a new --force-write-index option
Quoted 13 lines · Junio C Hamano
builtin/update-index.c int cmd_update_index(int argc, const char **argv, const char *prefix) · line 1011
Quoted 2 lines · Ben Peart
builtin/update-index.c +2
-0+OPT_SET_INT (0 ,"force-write-index" , &force_write, + N_("write out the index even if is not flagged as changed"), 1),Hmph. The only time this makes difference is when the code forgets to mark
active_cache_changedeven when it actually made a change to the index, no? I do understand the wish to be able to observe what _would_ be written if such a bug did not exist in order to debug the other aspects of the change in this series, but at the same time I fear that we may end up sweeping the problem under the rug by running the tests with this option.
This is to enable a performance optimization I discovered while perf testing the patch series. It enables us to do a lazy index write for fsmonitor detected changes but still always generate correct results.
Lets see how my ascii art skills do at describing this:
1) Index marked dirty on every fsmonitor change:
A---x---B---y---C
2) Index *not* marked dirty on fsmonitor changes:
A---x---B---x,y---C
Assume the index is written and up-to-date at point A.
In scenario #1 above, the index is marked fsmonitor dirty every time the fsmonitor detects a file that has been modified. At point B, the fsmonitor integration script returns that file 'x' has been modified since A, the index is marked dirty and then written to disk with a last_update time of B. At point C, the script returns 'y' as the changes since point B, the index is marked dirty and written to disk again.
In scenario #2, the index is *not* marked fsmonitor dirty when changed are detected. At point B, the script returns 'x' but the index is not flagged dirty nor written to disk. At point C, the script will return 'x' and 'y' (since both have been changed since time 'A') and again the index is not marked dirty nor written to disk.
Correct results are generated in both scenarios but in scenario 2, there were 2 fewer index writes. In short, the changed files were accumulated as the cost of processing 2 files at point C (vs 1) has no measurable difference in perf but the savings of two unnecessary index writes is significant (especially when the index gets large).
There is no real concern about accumulating too many changes as 1) the processing cost for additional modified files is fairly trivial and 2) the index ends up getting written out pretty frequently anyway as files are added/removed/staged/etc which updates the fsmonitor_last_update time.
The challenge came when it was time to test that the changes to the index were correct. Since they are lazily written by default, I needed a way to force the write so that I could verify the index on disk was correct. Hence, this patch.
Quoted 12 lines
Quoted 12 lines
OPT_END() };builtin/update-index.c +1
-1@@ -1147,7 +1150,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)die ("BUG: bad untracked_cache value: %d" , untracked_cache); } -if (active_cache_changed) { +if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )exit (128 );
Re: [PATCH v7 03/12] update-index: add a new --force-write-index option
Quoted 1 line · Ben Peart
Lets see how my ascii art skills do at describing this:
Your ascii art is fine. If you said upfront that the capital letters signify points in time, lower letters are file-touching events, and time flows from left to right, it would have been perfect ;-)
Quoted 5 lines
There is no real concern about accumulating too many changes as 1) the processing cost for additional modified files is fairly trivial and 2) the index ends up getting written out pretty frequently anyway as files are added/removed/staged/etc which updates the
fsmonitor_last_updatetime.
I still see some impedance mismatch here. The optimization described is valid but the code to do the optimization would avoid writing the index file out when the in-core index is dirty only because the status reported by fsmonitor changed--if there were other changes (e.g. the code added a new index entry), even with the optimization, you would still want to write the index out, right?
With or without the need for forced flush to help debugging, would that suggest that you need two bits now, instead of just a single 'active-cache-changed' bit?
By keeping track of that new bit that tells us if we have fsmonitor-only changes that we _could_ flush, this patch can further reduce the need for forced flushing (i.e. if we know we didn't get fsmonitor induced dirtyness, force_write can still be no-op), no?
Quoted 18 lines
The challenge came when it was time to test that the changes to the index were correct. Since they are lazily written by default, I needed a way to force the write so that I could verify the index on disk was correct. Hence, this patch.
Quoted 11 lines
Quoted 11 lines
OPT_END()
};
@@ -1147,7 +1150,7 @@ int cmd_update_index(int argc, const char
**argv, const char *prefix)
die("BUG: bad untracked_cache value: %d", untracked_cache);
}
- if (active_cache_changed) {builtin/update-index.c +1
-0+if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )exit (128 );
Re: [PATCH v7 03/12] update-index: add a new --force-write-index option
Quoted 9 lines · Junio C Hamano
Quoted 1 line · Ben Peart
Lets see how my ascii art skills do at describing this:
Your ascii art is fine. If you said upfront that the capital letters signify points in time, lower letters are file-touching events, and time flows from left to right, it would have been perfect ;-)
Rats, so close and yet... ;-)
Quoted 6 lines
Quoted 5 lines
There is no real concern about accumulating too many changes as 1) the processing cost for additional modified files is fairly trivial and 2) the index ends up getting written out pretty frequently anyway as files are added/removed/staged/etc which updates the
fsmonitor_last_updatetime.
Quoted 7 lines
I still see some impedance mismatch here. The optimization described is valid but the code to do the optimization would avoid writing the index file out when the in-core index is dirty only because the status reported by fsmonitor changed--if there were other changes (e.g. the code added a new index entry), even with the optimization, you would still want to write the index out, right?
Yes, that is exactly how it works. FSMONITOR_CHANGED is only set when the fsmonitor index extension is added or removed but all the other logic to flag the index dirty (CE_ENTRY_CHANGED/REMOVED/ADDED, UNTRACKED_CHANGED, etc) still exists and will still trigger the index to be written out as it always has. It's not to say some of these other changes could not do the same optimization (I haven't looked) if recomputing is cheaper than writing out the index.
Quoted 9 lines
With or without the need for forced flush to help debugging, would that suggest that you need two bits now, instead of just a single 'active-cache-changed' bit?
By keeping track of that new bit that tells us if we have fsmonitor-only changes that we _could_ flush, this patch can further reduce the need for forced flushing (i.e. if we know we didn't get fsmonitor induced dirtyness,
force_writecan still be no-op), no?
Yes, I suppose we _could_ add a 2nd bit (and then add the logic to set that bit every time a fsmonitor change was made) but I don't see that it really buys us anything useful. The force write flag in update-index is off by default and the only scenario we have that someone would set it is for test cases where the perf of writing out the index when it is not needed just doesn't matter.
Quoted 18 lines
Quoted 18 lines
The challenge came when it was time to test that the changes to the index were correct. Since they are lazily written by default, I needed a way to force the write so that I could verify the index on disk was correct. Hence, this patch.
Quoted 11 lines
Quoted 11 lines
OPT_END() }; @@ -1147,7 +1150,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) die("BUG: bad untracked_cache value: %d", untracked_cache); } - if (active_cache_changed) {builtin/update-index.c +1
-0+if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )exit (128 );
Re: [PATCH v7 03/12] update-index: add a new --force-write-index option
Quoted 11 lines · Ben Peart
Quoted 8 lines · Junio C Hamano
Quoted 1 line · Ben Peart
Lets see how my ascii art skills do at describing this:
Your ascii art is fine. If you said upfront that the capital letters signify points in time, lower letters are file-touching events, and time flows from left to right, it would have been perfect ;-)
Rats, so close and yet... ;-)
Nah, sorry for forgetting to add "... but I could guess that was the case after reading a few paragraphs, at which point I rewound and started reading from the beginning, and it was crystal clear."
Quoted 6 lines
Yes, I suppose we _could_ add a 2nd bit (and then add the logic to set that bit every time a fsmonitor change was made) but I don't see that it really buys us anything useful. The force write flag in update-index is off by default and the only scenario we have that someone would set it is for test cases where the perf of writing out the index when it is not needed just doesn't matter.
I tend to agree now.
My reaction primarily came from that I couldn't quite tell what the IGNORE_* bit was ment to do and assumed that it meant pretty much the same thing as existing ones like "valid bit is untrustworthy, so do not pay attention to it". It turns out that this one has quite a different meaning, that is not connected to how much we should trust state maintained by the fsmonitor, which force me off-track.
Thanks.
Re: [PATCH v7 03/12] update-index: add a new --force-write-index option
Quoted 25 lines · Junio C Hamano
Quoted 11 lines · Ben Peart
Quoted 8 lines · Junio C Hamano
Quoted 1 line · Ben Peart
Lets see how my ascii art skills do at describing this:
Your ascii art is fine. If you said upfront that the capital letters signify points in time, lower letters are file-touching events, and time flows from left to right, it would have been perfect ;-)
Rats, so close and yet... ;-)
Nah, sorry for forgetting to add "... but I could guess that was the case after reading a few paragraphs, at which point I rewound and started reading from the beginning, and it was crystal clear."
Quoted 6 lines
Yes, I suppose we _could_ add a 2nd bit (and then add the logic to set that bit every time a fsmonitor change was made) but I don't see that it really buys us anything useful. The force write flag in update-index is off by default and the only scenario we have that someone would set it is for test cases where the perf of writing out the index when it is not needed just doesn't matter.
Quoted 3 lines
I tend to agree now.
My reaction primarily came from ...
oops. please ignore the last paragraph, or transplant it to the other thread X-<.
PATCH v7 4/12 fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
In reply to Ben PeartWhen the index is read from disk, the fsmonitor index extension is used to flag the last known potentially dirty index entries. The registered core.fsmonitor command is called with the time the index was last updated and returns the list of files changed since that time. This list is used to flag any additional dirty cache entries and untracked cache directories.
We can then use this valid state to speed up preload_index(), ie_match_stat(), and refresh_cache_ent() as they do not need to lstat() files to detect potential changes for those entries marked CE_FSMONITOR_VALID.
In addition, if the untracked cache is turned on valid_cached_dir() can skip checking directories for new or changed files as fsmonitor will invalidate the cache only for those directories that have been identified as having potential changes.
To keep the CE_FSMONITOR_VALID state accurate during git operations; when git updates a cache entry to match the current state on disk, it will now set the CE_FSMONITOR_VALID bit.
Inversely, anytime git changes a cache entry, the CE_FSMONITOR_VALID bit is cleared and the corresponding untracked cache directory is marked invalid.
--- Makefile | 1 + apply.c | 2 +- builtin/update-index.c | 2 + cache.h | 10 +- config.c | 14 +++ config.h | 1 + diff-lib.c | 2 + dir.c | 27 ++++-- dir.h | 2 + entry.c | 4 +- environment.c | 1 + fsmonitor.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++++ fsmonitor.h | 61 ++++++++++++ preload-index.c | 6 +- read-cache.c | 49 ++++++++-- submodule.c | 2 +- unpack-trees.c | 8 +- 17 files changed, 419 insertions(+), 26 deletions(-) create mode 100644 fsmonitor.c create mode 100644 fsmonitor.h
Makefile +1 -0
@@ -786,6 +786,7 @@ LIB_OBJS += ewah/ewah_rlw.o LIB_OBJS += exec_cmd.o LIB_OBJS += fetch-pack.o LIB_OBJS += fsck.o +LIB_OBJS += fsmonitor.o LIB_OBJS += gettext.o LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o
apply.c +1 -1
@@ -3399,7 +3399,7 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st)return -1 ;return 0 ; } -return ce_match_stat (ce, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); +return ce_match_stat (ce, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); }#define SUBMODULE_PATCH_WITHOUT_INDEX 1
builtin/update-index.c +2 -0
@@ -16,6 +16,7 @@#include "pathspec.h" #include "dir.h" #include "split-index.h" +#include "fsmonitor.h" /* * Default to not allowing changes to the list of files. The
@@ -233,6 +234,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)else active_cache[pos]->ce_flags &= ~flag; active_cache[pos]->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (&the_index, active_cache[pos]);cache_tree_invalidate_path (&the_index, path); active_cache_changed |=CE_ENTRY_CHANGED ;return 0 ;
cache.h +8 -2
@@ -203,6 +203,7 @@ struct cache_entry {#define CE_ADDED (1 << 19)#define CE_HASHED (1 << 20) +#define CE_FSMONITOR_VALID (1 << 21)#define CE_WT_REMOVE (1 << 22)/* remove in work directory */ #define CE_CONFLICTED (1 << 23)
@@ -326,6 +327,7 @@ static inline unsigned int canon_mode(unsigned int mode)#define CACHE_TREE_CHANGED (1 << 5)#define SPLIT_INDEX_ORDERED (1 << 6)#define UNTRACKED_CHANGED (1 << 7) +#define FSMONITOR_CHANGED (1 << 8)struct split_index ;struct untracked_cache ;
@@ -344,6 +346,7 @@ struct index_state {struct hashmap dir_hash;unsigned char sha1[20 ];struct untracked_cache *untracked; +uint64_t fsmonitor_last_update; };extern struct index_state the_index;
@@ -679,8 +682,10 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,#define CE_MATCH_IGNORE_MISSING 0x08/* enable stat refresh */ #define CE_MATCH_REFRESH 0x10 -extern int ie_match_stat (const struct index_state *,const struct cache_entry *,struct stat *,unsigned int ); -extern int ie_modified (const struct index_state *,const struct cache_entry *,struct stat *,unsigned int ); +/* do stat comparison even if CE_FSMONITOR_VALID is true */ +#define CE_MATCH_IGNORE_FSMONITOR 0X20 +extern int ie_match_stat (struct index_state *,const struct cache_entry *,struct stat *,unsigned int ); +extern int ie_modified (struct index_state *,const struct cache_entry *,struct stat *,unsigned int );#define HASH_WRITE_OBJECT 1#define HASH_FORMAT_CHECK 2
@@ -773,6 +778,7 @@ extern int core_apply_sparse_checkout;extern int precomposed_unicode;extern int protect_hfs;extern int protect_ntfs; +extern const char *core_fsmonitor;/* * Include broken refs in all ref iterations, which will
config.c +14 -0
@@ -2165,6 +2165,20 @@ int git_config_get_max_percent_split_change(void)return -1 ;/* default value */ } +int git_config_get_fsmonitor (void ) +{ +if (git_config_get_pathname ("core.fsmonitor" , &core_fsmonitor)) + core_fsmonitor =getenv ("GIT_FSMONITOR_TEST" ); + +if (core_fsmonitor && !*core_fsmonitor) + core_fsmonitor =NULL ; + +if (core_fsmonitor) +return 1 ; + +return 0 ; +} +NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr) {
config.h +1 -0
@@ -211,6 +211,7 @@ extern int git_config_get_pathname(const char *key, const char **dest);extern int git_config_get_untracked_cache (void );extern int git_config_get_split_index (void );extern int git_config_get_max_percent_split_change (void ); +extern int git_config_get_fsmonitor (void );/* This dies if the configured or default date is in the future */ extern int git_config_get_expiry (const char *key,const char **output);
diff-lib.c +2 -0
@@ -12,6 +12,7 @@#include "refs.h" #include "submodule.h" #include "dir.h" +#include "fsmonitor.h" /* * diff-files
@@ -228,6 +229,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)if (!changed && !dirty_submodule) {ce_mark_uptodate (ce); +mark_fsmonitor_valid (ce);if (!DIFF_OPT_TST (&revs->diffopt,FIND_COPIES_HARDER ))continue ; }
dir.c +17 -10
@@ -18,6 +18,7 @@#include "utf8.h" #include "varint.h" #include "ewah/ewok.h" +#include "fsmonitor.h" /* * Tells read_directory_recursive how a file or directory should be treated.
@@ -1688,17 +1689,23 @@ static int valid_cached_dir(struct dir_struct *dir,if (!untracked)return 0 ; -if (stat (path->len ? path->buf :"." , &st)) { -invalidate_directory (dir->untracked, untracked); -memset (&untracked->stat_data,0 ,sizeof (untracked->stat_data)); -return 0 ; - } -if (!untracked->valid || -match_stat_data_racy (istate, &untracked->stat_data, &st)) { -if (untracked->valid) +/* +* With fsmonitor, we can trust the untracked cache's valid field. +*/ +refresh_fsmonitor (istate); +if (!(dir->untracked->use_fsmonitor && untracked->valid)) { +if (stat (path->len ? path->buf :"." , &st)) {invalidate_directory (dir->untracked, untracked); -fill_stat_data (&untracked->stat_data, &st); -return 0 ; +memset (&untracked->stat_data,0 ,sizeof (untracked->stat_data)); +return 0 ; + } +if (!untracked->valid || +match_stat_data_racy (istate, &untracked->stat_data, &st)) { +if (untracked->valid) +invalidate_directory (dir->untracked, untracked); +fill_stat_data (&untracked->stat_data, &st); +return 0 ; + } }if (untracked->check_only != !!check_only) {
dir.h +2 -0
@@ -139,6 +139,8 @@ struct untracked_cache {int gitignore_invalidated;int dir_invalidated;int dir_opened; +/* fsmonitor invalidation data */ + unsigned int use_fsmonitor : 1; };struct dir_struct {
entry.c +3 -1
@@ -4,6 +4,7 @@#include "streaming.h" #include "submodule.h" #include "progress.h" +#include "fsmonitor.h" static void create_directories (const char *path,int path_len,const struct checkout *state)
@@ -357,6 +358,7 @@ static int write_entry(struct cache_entry *ce,lstat (ce->name, &st);fill_stat_cache_info (ce, &st); ce->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (state->istate, ce); state->istate->cache_changed |=CE_ENTRY_CHANGED ; }return 0 ;
@@ -402,7 +404,7 @@ int checkout_entry(struct cache_entry *ce,if (!check_path (path.buf, path.len, &st, state->base_dir_len)) {const struct submodule *sub; -unsigned changed =ce_match_stat (ce, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); +unsigned changed =ce_match_stat (ce, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR );/* * Needs to be checked before !changed returns early, * as the possibly empty directory was not changed
environment.c +1 -0
@@ -76,6 +76,7 @@ int protect_hfs = PROTECT_HFS_DEFAULT;#define PROTECT_NTFS_DEFAULT 0#endif int protect_ntfs =PROTECT_NTFS_DEFAULT ; +const char *core_fsmonitor;/* * The character that begins a commented line in user-editable file
fsmonitor.c new file +253 -0
253 lines not on this page · raw message
fsmonitor.h new file +61 -0
61 lines not on this page · raw message
preload-index.c +5 -1
@@ -4,6 +4,7 @@#include "cache.h" #include "pathspec.h" #include "dir.h" +#include "fsmonitor.h" #ifdef NO_PTHREADS static void preload_index(struct index_state *index,
@@ -55,15 +56,18 @@ static void *preload_thread(void *_data)continue ;if (ce_skip_worktree (ce))continue ; +if (ce->ce_flags &CE_FSMONITOR_VALID ) +continue ;if (!ce_path_match (ce, &p->pathspec,NULL ))continue ;if (threaded_has_symlink_leading_path (&cache, ce->name,ce_namelen (ce)))continue ;if (lstat (ce->name, &st))continue ; -if (ie_match_stat (index, ce, &st,CE_MATCH_RACY_IS_DIRTY )) +if (ie_match_stat (index, ce, &st,CE_MATCH_RACY_IS_DIRTY |CE_MATCH_IGNORE_FSMONITOR ))continue ;ce_mark_uptodate (ce); +mark_fsmonitor_valid (ce); }while (--nr >0 );cache_def_clear (&cache);return NULL ;
read-cache.c +42 -7
168 lines not on this page · raw message
submodule.c +1 -1
@@ -62,7 +62,7 @@ int is_staging_gitmodules_ok(const struct index_state *istate)if ((pos >=0 ) && (pos < istate->cache_nr)) {struct stat st;if (lstat (GITMODULES_FILE , &st) ==0 && -ce_match_stat (istate->cache[pos], &st,0 ) &DATA_CHANGED ) +ce_match_stat (istate->cache[pos], &st,CE_MATCH_IGNORE_FSMONITOR ) &DATA_CHANGED )return 0 ; }
unpack-trees.c +5 -3
@@ -14,6 +14,7 @@#include "dir.h" #include "submodule.h" #include "submodule-config.h" +#include "fsmonitor.h" /* * Error messages expected by scripts out of plumbing commands such as
@@ -408,6 +409,7 @@ static int apply_sparse_checkout(struct index_state *istate, ce->ce_flags &= ~CE_SKIP_WORKTREE ;if (was_skip_worktree !=ce_skip_worktree (ce)) { ce->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (istate, ce); istate->cache_changed |=CE_ENTRY_CHANGED ; }
@@ -1454,7 +1456,7 @@ static int verify_uptodate_1(const struct cache_entry *ce,return 0 ;if (!lstat (ce->name, &st)) { -int flags =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ; +int flags =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ;unsigned changed =ie_match_stat (o->src_index, ce, &st, flags);if (submodule_from_ce (ce)) {
@@ -1610,7 +1612,7 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int leconst struct cache_entry *src; src =index_file_exists (o->src_index, name, len,1 ); -return src && !ie_match_stat (o->src_index, src, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); +return src && !ie_match_stat (o->src_index, src, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); } static int check_ok_to_remove(const char *name, int len, int dtype,
@@ -2134,7 +2136,7 @@ int oneway_merge(const struct cache_entry * const *src,if (o->reset && o->update && !ce_uptodate (old) && !ce_skip_worktree (old)) {struct stat st;if (lstat (old->name, &st) || -ie_match_stat (o->src_index, old, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE )) +ie_match_stat (o->src_index, old, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR )) update |=CE_UPDATE ; }add_entry (o, old, update,0 );
Signature · 1 line
2.14.1.windows.1
Re: [PATCH v7 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
cache.h extern void *read_blob_data_from_index(const struct index_state *, const char *, · line 686
Quoted 2 lines · Ben Peart
cache.h +2
-0+/* do stat comparison even if CE_FSMONITOR_VALID is true */ +#define CE_MATCH_IGNORE_FSMONITOR 0X20
Hmm, when should a programmer use this flag?
config.c int git_config_get_max_percent_split_change(void) · line 2171
Quoted 4 lines
config.c +4
-0+int git_config_get_fsmonitor (void ) +{ +if (git_config_get_pathname ("core.fsmonitor" , &core_fsmonitor)) + core_fsmonitor =getenv ("GIT_FSMONITOR_TEST" );
Will the environment be part of the published API, or is it a remnant of a useful tool for debugging while developing the feature?
If it is the former (and I'd say why not, even though "git -c core.fsmontor=..." may be easy enough), we might want to rename it to replace _TEST with _PROGRAM or something and document it.
Quoted 13 lines
diff-lib.c +1
-0@@ -12,6 +12,7 @@#include "refs.h" #include "submodule.h" #include "dir.h" +#include "fsmonitor.h" /* * diff-files
diff-lib.c int run_diff_files(struct rev_info *revs, unsigned int option) · line 232
Quoted 4 lines
diff-lib.c +1
-0@@ -228,6 +229,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)if (!changed && !dirty_submodule) {ce_mark_uptodate (ce); +mark_fsmonitor_valid (ce);
I notice all calls to mark_fsmonitor_valid(ce) always follow a call to ce_mark_uptodate(ce). Should the call to the former be made as part of the latter instead? Are there cases where we want to call ce_mark_uptodate(ce) to mark the ce up-to-date, yet we do not want to call mark_fsmonitor_valid(ce)? How does a programmer tell when s/he wants to call ce_mark_uptodate(ce) if s/he also should call mark_fsmonitor_valid(ce)?
Together with "when to pass IGNORE_FSMONITOR" question, is there a summary that guides new programmers to answer these questions in the new documentation?
dir.h struct untracked_cache { · line 143
Quoted 10 lines
dir.h +2
-0@@ -139,6 +139,8 @@ struct untracked_cache {int gitignore_invalidated;int dir_invalidated;int dir_opened; +/* fsmonitor invalidation data */ + unsigned int use_fsmonitor : 1;
This makes it look like we will add a bit more fields in later patches that are about "invalidation" around fsmonitor, but it appears that such an addition never happens til the end of the series. And use_fsmonitor boolean does not seem to be what the comment says---it just tells us if fsmonitor is in use in the operation of the untracked cache, no?
Quoted 13 lines
entry.c +1
-0@@ -4,6 +4,7 @@#include "streaming.h" #include "submodule.h" #include "progress.h" +#include "fsmonitor.h" static void create_directories (const char *path,int path_len,const struct checkout *state)
entry.c static int write_entry(struct cache_entry *ce, · line 362
Quoted 5 lines
entry.c +1
-0@@ -357,6 +358,7 @@ static int write_entry(struct cache_entry *ce,lstat (ce->name, &st);fill_stat_cache_info (ce, &st); ce->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (state->istate, ce); state->istate->cache_changed |=CE_ENTRY_CHANGED ;
Similar to "how does mark_fsmonitor_valid() relate to ce_mark_uptodate()?" question earlier, mark_fsmonitor_invalid() seems to often appear in pairs with the update to cache_changed. Are there cases where we need CE_ENTRY_CHANGED bit added to the index state yet we do not want to call mark_fsmonitor_invalid()? I am wondering if there should be a single helper function that lets callers say "I changed this ce in this istate this way. Please update CE_VALID, CE_UPDATE_IN_BASE and CE_FSMONITOR_VALID accordingly".
That "changed _this way_" is deliberately vague in my question above, because it is not yet clear to me when mark-valid and mark-invalid should and should not be called from the series.
entry.c static int write_entry(struct cache_entry *ce,
Quoted 3 lines
entry.c +3
-0+/* a fsmonitor process can return '*' to indicate all entries are invalid */ +if (query_success && query_result.buf[0 ] !='/' ) { +/* Mark all entries returned by the monitor as dirty */
The comment talks about '*' and code checks if it is not '/'? The else clause of this if/else handles the "invalidate all" case, so shouldn't we be comparing with '*' instead here?
Ah, fsmonitor-watchman section of the doc tells us to write the hook in a way to return slash, so the comment above the code is stale and the comparison with '/' is what we want, I guess.
Re: [PATCH v7 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 7 lines · Junio C Hamano
Review
Quoted 2 lines · Ben Peart
(quoted diff) +2
-0+/* do stat comparison even if CE_FSMONITOR_VALID is true */ +#define CE_MATCH_IGNORE_FSMONITOR 0X20Hmm, when should a programmer use this flag?
Pretty much the same places you would also use CE_MATCH_IGNORE_VALID and CE_MATCH_IGNORE_SKIP_WORKTREE which serve the same role for those features. That is generally when you are about to overwrite data so want to be *really* sure you have what you think you have.
The other place I used it was in preload_index(). In that case, I didn't want to trigger the call to refresh_fsmonitor() as preload_index() is about trying to do a fast precompute of state for the bulk of the index entries but is not required for correctness as refresh_cache_ent() will ensure any "missed" by preload_index() are up-to-date if/when that is needed.
Quoted 8 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+int git_config_get_fsmonitor(void) +{ + if (git_config_get_pathname("core.fsmonitor", &core_fsmonitor)) + core_fsmonitor = getenv("GIT_FSMONITOR_TEST");Will the environment be part of the published API, or is it a remnant of a useful tool for debugging while developing the feature?
Quoted 4 lines
If it is the former (and I'd say why not, even though "git -c core.fsmontor=..." may be easy enough), we might want to rename it to replace
_TESTwith_PROGRAMor something and document it.
This was added this to facilitate testing. That is why it has the magic naming of "GIT_***_TEST" which is the only way I found to ensure that env variable gets passed to tests. Its use is discussed in patch 10 which contains the tests.
Quoted 14 lines
Quoted 14 lines
diff-lib.c +1
-0@@ -12,6 +12,7 @@#include "refs.h" #include "submodule.h" #include "dir.h" +#include "fsmonitor.h" /* * diff-files @@ -228,6 +229,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
Quoted 11 lines
diff-lib.c int run_diff_files(struct rev_info *revs, unsigned int option) · line 232
Quoted 3 lines
diff-lib.c +1
-0if (!changed && !dirty_submodule) {ce_mark_uptodate (ce); +mark_fsmonitor_valid (ce);I notice all calls to
mark_fsmonitor_valid(ce)always follow a call toce_mark_uptodate(ce). Should the call to the former be made as part of the latter instead? Are there cases where we want to callce_mark_uptodate(ce)to mark the ce up-to-date, yet we do not want to callmark_fsmonitor_valid(ce)? How does a programmer tell when s/he wants to callce_mark_uptodate(ce)if s/he also should callmark_fsmonitor_valid(ce)?
mark_fsmonitor_valid(ce) is the way to indicate that cache entries that were once fsmonitor dirty are now properly reflected in the index so can come off the "dirty" list. It can't really be combined with ce_mark_uptodate(ce) as that would prevent the CE_MATCH_IGNORE_FSMONITOR logic:
if (!ignore_skip_worktree && ce_skip_worktree(ce)) {
ce_mark_uptodate(ce);
return ce;
}
if (!ignore_valid && (ce->ce_flags & CE_VALID)) {
ce_mark_uptodate(ce);
return ce;
}
if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID)) {
ce_mark_uptodate(ce);
return ce;
}In addition, fsmonitor is an optional feature and so the mark_fsmonitor_valid(ce) call should only happen when the feature is turned on. I tried to keep it as simple as possible by making that test and set logic part of the function but still be performant by making the function inline.
Quoted 5 lines
Together with "when to pass
IGNORE_FSMONITOR" question, is there a summary that guides new programmers to answer these questions in the new documentation?
Only the discussion in this mail thread. I could add something to the function header in fsmonitor.h if that would help. How about something like:
fsmonitor.h +10 -5
@@ -34,9 +34,11 @@ extern void tweak_fsmonitor(struct index_state *istate);*/ extern void refresh_fsmonitor (struct index_state *istate); -/* -* Set the given cache entries CE_FSMONITOR_VALID bit. -*/ +/* +* Set the given cache entries CE_FSMONITOR_VALID bit. This should be +* called any time the cache entry has been updated to reflect the +* current state of the file on disk. +*/ static inline void mark_fsmonitor_valid (struct cache_entry *ce) {if (core_fsmonitor) {
@@ -46,8 +48,11 @@ static inline void mark_fsmonitor_valid(struct cache_entry *ce) }/* -* Clear the given cache entry's CE_FSMONITOR_VALID bit and invalidate any -* corresponding untracked cache directory structures. +* Clear the given cache entry's CE_FSMONITOR_VALID bit and invalidate +* any corresponding untracked cache directory structures. This should +* be called any time git creates or modifies a file that should +* trigger an lstat() or invalidate the untracked cache for the +* corresponding directory */ static inline void mark_fsmonitor_invalid(struct index_state *istate,
struct cache_entry *ce)
{
Quoted 9 lines
Quoted 9 lines
dir.h +1
-0@@ -139,6 +139,8 @@ struct untracked_cache {int gitignore_invalidated;int dir_invalidated;int dir_opened; +/* fsmonitor invalidation data */
Quoted 8 lines
dir.h struct untracked_cache { · line 143
Quoted 1 line
dir.h +1
-0+ unsigned int use_fsmonitor : 1;This makes it look like we will add a bit more fields in later patches that are about "invalidation" around fsmonitor, but it appears that such an addition never happens til the end of the series. And
use_fsmonitorboolean does not seem to be what the comment says---it just tells us if fsmonitor is in use in the operation of the untracked cache, no?
I don't have any more planned bit fields. I only needed a single bit so used a bit field just in case someone else comes by later and needs another bit. If you aren't worried about that, we can just make this an int.
Correct. The bit just indicates whether fsmonitor has been used to ensure the cache is current or if it needs to be checked. I have comments to this effect where the flag is used in the code but could move/duplicate them into the header if wished. For example:
/* With fsmonitor, we can trust the untracked cache's valid field. */
and
/* Now mark the untracked cache for fsmonitor usage */
Quoted 30 lines
Quoted 14 lines
entry.c +1
-0@@ -4,6 +4,7 @@#include "streaming.h" #include "submodule.h" #include "progress.h" +#include "fsmonitor.h" static void create_directories (const char *path,int path_len,const struct checkout *state)entry.c static int write_entry(struct cache_entry *ce, · line 362
Quoted 5 lines
entry.c +1
-0@@ -357,6 +358,7 @@ static int write_entry(struct cache_entry *ce,lstat (ce->name, &st);fill_stat_cache_info (ce, &st); ce->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (state->istate, ce); state->istate->cache_changed |=CE_ENTRY_CHANGED ;Similar to "how does
mark_fsmonitor_valid()relate toce_mark_uptodate()?" question earlier,mark_fsmonitor_invalid()seems to often appear in pairs with the update tocache_changed. Are there cases where we needCE_ENTRY_CHANGEDbit added to the index state yet we do not want to callmark_fsmonitor_invalid()? I am wondering if there should be a single helper function that lets callers say "I changed this ce in this istate this way. Please updateCE_VALID,CE_UPDATE_IN_BASEandCE_FSMONITOR_VALIDaccordingly".
Quoted 4 lines
That "changed
_thisway_" is deliberately vague in my question above, because it is not yet clear to me when mark-valid and mark-invalid should and should not be called from the series.
Please let me know if my comment/patch above does not address this concern sufficiently.
Quoted 8 lines
entry.c static int write_entry(struct cache_entry *ce,
Quoted 3 lines
entry.c +3
-0+/* a fsmonitor process can return '*' to indicate all entries are invalid */ +if (query_success && query_result.buf[0 ] !='/' ) { +/* Mark all entries returned by the monitor as dirty */ The comment talks about '*' and code checks if it is not '/'? The else clause of this if/else handles the "invalidate all" case, so shouldn't we be comparing with '*' instead here?
Quoted 4 lines
Ah, fsmonitor-watchman section of the doc tells us to write the hook in a way to return slash, so the comment above the code is stale and the comparison with '/' is what we want, I guess.
Correct. Sorry about missing that. Here is a patch that can be squashed in.
fsmonitor.c +0 -0
@@ -176,7 +176,7 @@ void refresh_fsmonitor(struct index_state *istate) core_fsmonitor, query_success ? "success" :
"failure");
}- /* a fsmonitor process can return '*' to indicate all entries are invalid */
+ /* a fsmonitor process can return '/' to indicate all entries are invalid */
if (query_success && query_result.buf[0] != '/') {
/* Mark all entries returned by the monitor as dirty */
buf = query_result.buf;Re: [PATCH v7 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 4 lines · Ben Peart
Pretty much the same places you would also use
CE_MATCH_IGNORE_VALIDandCE_MATCH_IGNORE_SKIP_WORKTREEwhich serve the same role for those features. That is generally when you are about to overwrite data so want to be *really* sure you have what you think you have.
Now that makes me worried gravely.
IGNORE_VALID is ignored in these places because we have been burned by end-users lying to us. IGNORE_SKIP_WORKTREE must be ignored because we know that the working tree state does not match the "reality" the index wants to have. The fact that the code treats the status reported and kept up to date by fsmonitor the same way as these two implies that it is merely advisory and cannot be trusted? Is that the reason why we tell the codepath with IGNORE_FSMONITOR to ignore the state fsmonitor reported and check the state ourselves?
Oh, wait...
Quoted 6 lines
The other place I used it was in
preload_index(). In that case, I didn't want to trigger the call torefresh_fsmonitor()aspreload_index()is about trying to do a fast precompute of state for the bulk of the index entries but is not required for correctness asrefresh_cache_ent()will ensure any "missed" bypreload_index()are up-to-date if/when that is needed.
That is a very valid design decision. So IGNORE_FSMONITOR is, unlike IGNORE_VALID and IGNORE_SKIP_WORKTREE, to tell us "do not bother asking fsmonitor to refresh the state of this entry--it is OK for us to use a slightly stale information"? That would make sense as an optimization, but that does not mesh well with the previous "we need to be really really sure" usecase. That one wants "we do not trust fsmonitor, so do not bother asking to refresh; we will do so ourselves", which would not help the "we can use slightly stale one and that is OK" usecase.
Puzzled...
Re: [PATCH v7 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 9 lines · Junio C Hamano
Quoted 4 lines · Ben Peart
Pretty much the same places you would also use
CE_MATCH_IGNORE_VALIDandCE_MATCH_IGNORE_SKIP_WORKTREEwhich serve the same role for those features. That is generally when you are about to overwrite data so want to be *really* sure you have what you think you have.Now that makes me worried gravely.
Quoted 9 lines
IGNORE_VALIDis ignored in these places because we have been burned by end-users lying to us.IGNORE_SKIP_WORKTREEmust be ignored because we know that the working tree state does not match the "reality" the index wants to have. The fact that the code treats the status reported and kept up to date by fsmonitor the same way as these two implies that it is merely advisory and cannot be trusted? Is that the reason why we tell the codepath withIGNORE_FSMONITORto ignore the state fsmonitor reported and check the state ourselves?
Sorry for causing unnecessary worry. The fsmonitor data can be trusted (as much as you can trust that Watchman or your file system monitor is not buggy). I wasn't 100% sure *why* these places passed the various IGNORE_VALID and IGNORE_SKIP_WORKTREE flags. When I looked at them, that lack of trust seemed to be the reason.
Adding IGNORE_FSMONITOR in those same places was simply an abundance of caution on my part. The only down side of passing the flag for fsmonitor is that we will end up calling lstat() on a file where we technically didn't need too. That seemed safer than potentially missing a change if I had misunderstood the code.
I'd much rather return correct results (and fall back to the old performance) than potentially be incorrect. I followed that same principal in the entire design of fsmonitor - if anything doesn't look right, fall back to the old code path just in case...
Quoted 22 lines
Oh, wait...
Quoted 6 lines
The other place I used it was in
preload_index(). In that case, I didn't want to trigger the call torefresh_fsmonitor()aspreload_index()is about trying to do a fast precompute of state for the bulk of the index entries but is not required for correctness asrefresh_cache_ent()will ensure any "missed" bypreload_index()are up-to-date if/when that is needed.That is a very valid design decision. So
IGNORE_FSMONITORis, unlikeIGNORE_VALIDandIGNORE_SKIP_WORKTREE, to tell us "do not bother asking fsmonitor to refresh the state of this entry--it is OK for us to use a slightly stale information"? That would make sense as an optimization, but that does not mesh well with the previous "we need to be really really sure" usecase. That one wants "we do not trust fsmonitor, so do not bother asking to refresh; we will do so ourselves", which would not help the "we can use slightly stale one and that is OK" usecase.Puzzled...
Re: [PATCH v7 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 22 lines · Ben Peart
Quoted 9 lines · Junio C Hamano
Quoted 4 lines · Ben Peart
Pretty much the same places you would also use
CE_MATCH_IGNORE_VALIDandCE_MATCH_IGNORE_SKIP_WORKTREEwhich serve the same role for those features. That is generally when you are about to overwrite data so want to be *really* sure you have what you think you have.Now that makes me worried gravely.
Quoted 9 lines
IGNORE_VALIDis ignored in these places because we have been burned by end-users lying to us.IGNORE_SKIP_WORKTREEmust be ignored because we know that the working tree state does not match the "reality" the index wants to have. The fact that the code treats the status reported and kept up to date by fsmonitor the same way as these two implies that it is merely advisory and cannot be trusted? Is that the reason why we tell the codepath withIGNORE_FSMONITORto ignore the state fsmonitor reported and check the state ourselves?
Quoted 6 lines
Sorry for causing unnecessary worry. The fsmonitor data can be trusted (as much as you can trust that Watchman or your file system monitor is not buggy). I wasn't 100% sure *why* these places passed the various
IGNORE_VALIDandIGNORE_SKIP_WORKTREEflags. When I looked at them, that lack of trust seemed to be the reason.Adding
IGNORE_FSMONITORin those same places was simply an abundance of caution on my part. The only down side of passing the flag for fsmonitor is that we will end up callinglstat()on a file where we technically didn't need too. That seemed safer than potentially missing a change if I had misunderstood the code.I'd much rather return correct results (and fall back to the old performance) than potentially be incorrect. I followed that same principal in the entire design of fsmonitor - if anything doesn't look right, fall back to the old code path just in case...
I spent some time with git blame/show trying to figure out the *why* for all the places CE_MATCH_IGNORE_* are passed without gaining a lot of additional understanding. Based on your description above of why these exist, I believe there are very few places we actually need to pass CE_MATCH_IGNORE_FSMONITOR and that I was being overly cautious.
Here is a patch that removes the unnecessary CE_MATCH_IGNORE_FSMONITOR instances. While the test suite passes with this change, I'm not 100% confident that we actually have test cases that would have detected all the places that we needed the CE_MATCH_IGNORE_* flags.
If this seems like a reasonable additional optimization to make, I can roll it into the next iteration of the patch series as I have some spelling, documentation changes and other tweaks as a result of all the feedback.
From 6ff7ed0467fd736dca73efe62391bb3ee9b4e771 Mon Sep 17 00:00:00 2001
From: Ben Peart <[email protected]>
Date: Thu, 21 Sep 2017 09:09:42 -0400
Subject: [PATCH] fsmonitor: remove unnecessary uses of
CE_MATCH_IGNORE_FSMONITOR
With a better understanding of *why* the CE_MATCH_IGNORE_* flags are used, it is now more clear they are not required in most cases where CE_MATCH_IGNORE_FSMONITOR was being passed out of an abundance of caution.
Since the fsmonitor data can be trusted and is kept in sync with the working directory, the only remaining valid uses are those locations where we don't want to trigger an unneeded refresh_fsmonitor() call.
One is where preload_index() is doing a fast precompute of state for the bulk of the index entries but is not required for correctness as refresh_cache_ent() will ensure any "missed" by preload_index() are up-to-date if/when they are needed.
The second is in is_staging_gitmodules_ok() where we don't want to trigger a complete refresh just to check the .gitignore file.
The net result of this change will be that there are more cases where we will be able to use the cached index state and avoid unnecessary lstat() calls.
--- apply.c | 2 +- entry.c | 2 +- read-cache.c | 4 ++-- unpack-trees.c | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-)
apply.c +1 -1
@@ -3399,7 +3399,7 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st)return -1 ;return 0 ; } -return ce_match_stat (ce, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); +return ce_match_stat (ce, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); }#define SUBMODULE_PATCH_WITHOUT_INDEX 1
entry.c +1 -1
@@ -404,7 +404,7 @@ int checkout_entry(struct cache_entry *ce,if (!check_path (path.buf, path.len, &st, state->base_dir_len)) {const struct submodule *sub; -unsigned changed =ce_match_stat (ce, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); +unsigned changed =ce_match_stat (ce, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE );/* * Needs to be checked before !changed returns early, * as the possibly empty directory was not changed
read-cache.c +2 -2
@@ -641,7 +641,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,int size, namelen, was_same;mode_t st_mode = st->st_mode;struct cache_entry *ce, *alias; -unsigned ce_option =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_RACY_IS_DIRTY |CE_MATCH_IGNORE_FSMONITOR ; +unsigned ce_option =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_RACY_IS_DIRTY ;int verbose = flags & (ADD_CACHE_VERBOSE |ADD_CACHE_PRETEND );int pretend = flags &ADD_CACHE_PRETEND ;int intent_only = flags &ADD_CACHE_INTENT ;
@@ -1356,7 +1356,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,int first =1 ;int in_porcelain = (flags &REFRESH_IN_PORCELAIN );unsigned int options = (CE_MATCH_REFRESH | - (really ?CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_FSMONITOR :0 ) | + (really ?CE_MATCH_IGNORE_VALID :0 ) | (not_new ?CE_MATCH_IGNORE_MISSING :0 ));const char *modified_fmt;const char *deleted_fmt;
unpack-trees.c +2 -2
@@ -1456,7 +1456,7 @@ static int verify_uptodate_1(const struct cache_entry *ce,return 0 ;if (!lstat (ce->name, &st)) { -int flags =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ; +int flags =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ;unsigned changed =ie_match_stat (o->src_index, ce, &st, flags);if (submodule_from_ce (ce)) {
@@ -1612,7 +1612,7 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int leconst struct cache_entry *src; src =index_file_exists (o->src_index, name, len,1 ); -return src && !ie_match_stat (o->src_index, src, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); +return src && !ie_match_stat (o->src_index, src, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); } static int check_ok_to_remove(const char *name, int len, int dtype,
@@ -2136,7 +2136,7 @@ int oneway_merge(const struct cache_entry * const *src, if (o->reset && o->update && !ce_uptodate(old) &&
!ce_skip_worktree(old)) {
struct stat st;
if (lstat(old->name, &st) ||
- ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_IGNORE_FSMONITOR)) + ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))
update |= CE_UPDATE;
}
add_entry(o, old, update, 0);
Signature · 1 line
2.14.1.548.g237ef02b2b.dirty
Quoted 22 lines
Quoted 22 lines
Oh, wait...
Quoted 6 lines
The other place I used it was in
preload_index(). In that case, I didn't want to trigger the call torefresh_fsmonitor()aspreload_index()is about trying to do a fast precompute of state for the bulk of the index entries but is not required for correctness asrefresh_cache_ent()will ensure any "missed" bypreload_index()are up-to-date if/when that is needed.That is a very valid design decision. So
IGNORE_FSMONITORis, unlikeIGNORE_VALIDandIGNORE_SKIP_WORKTREE, to tell us "do not bother asking fsmonitor to refresh the state of this entry--it is OK for us to use a slightly stale information"? That would make sense as an optimization, but that does not mesh well with the previous "we need to be really really sure" usecase. That one wants "we do not trust fsmonitor, so do not bother asking to refresh; we will do so ourselves", which would not help the "we can use slightly stale one and that is OK" usecase.Puzzled...
Re: [PATCH v7 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 3 lines · Ben Peart
Since the fsmonitor data can be trusted and is kept in sync with the working directory, the only remaining valid uses are those locations where we don't want to trigger an unneeded
refresh_fsmonitor()call.
Now that is a lot more assuring ;-) And the exceptions below also make sense to me.
Thanks for thinking this through.
Re: [PATCH v7 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
In reply to Ben Peartcache.h struct index_state { · line 349
Quoted 5 lines · Ben Peart
cache.h +1
-0@@ -344,6 +346,7 @@ struct index_state {struct hashmap dir_hash;unsigned char sha1[20 ];struct untracked_cache *untracked; +uint64_t fsmonitor_last_update;
This field being zero has more significance than just "we haven't got any update yet", right? The way I am reading the code is that setting it 0 is a way to signal that fsmon has been inactivated. It also made me wonder if add_fsmonitor() that silently returns without doing anything when this field is already non-zero is a bug (in other words, I couldn't tell what the right answer would be to a question "shouldn't the caller be avoiding duplicate calls?").
fsmonitor.c
Quoted 6 lines
fsmonitor.c new file +0
-0
This part was a pleasant read.
Thanks.
Re: [PATCH v7 04/12] fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
Quoted 16 lines · Junio C Hamano
cache.h struct index_state { · line 349
Quoted 5 lines · Ben Peart
cache.h +1
-0@@ -344,6 +346,7 @@ struct index_state {struct hashmap dir_hash;unsigned char sha1[20 ];struct untracked_cache *untracked; +uint64_t fsmonitor_last_update;This field being zero has more significance than just "we haven't got any update yet", right? The way I am reading the code is that setting it 0 is a way to signal that fsmon has been inactivated. It also made me wonder if
add_fsmonitor()that silently returns without doing anything when this field is already non-zero is a bug (in other words, I couldn't tell what the right answer would be to a question "shouldn't the caller be avoiding duplicate calls?").
Correct again. For better (and sometimes for worse) I followed the pattern set by the untracked cache. If you compare them, you will notice striking similarities. :)
Quoted 10 lines
fsmonitor.c
Quoted 6 lines
fsmonitor.c new file +0
-0This part was a pleasant read.
Quoted 1 line
Thanks.
Thank you for your careful review. I appreciate having another set of eyes taking a close look especially as I see this as a big first step towards making many git operations O(# changed files) instead of O(# size of working directory). Seeing status times drop from 1m22s to 1.45s is a huge perf win - but only if it is correct!
PATCH v7 5/12 fsmonitor: add documentation for the fsmonitor extension.
In reply to Ben PeartThis includes the core.fsmonitor setting, the query-fsmonitor hook, and the fsmonitor index extension.
--- Documentation/config.txt | 7 +++++ Documentation/git-ls-files.txt | 7 ++++- Documentation/git-update-index.txt | 45 ++++++++++++++++++++++++++++++++ Documentation/githooks.txt | 28 ++++++++++++++++++++ Documentation/technical/index-format.txt | 19 ++++++++++++++ 5 files changed, 105 insertions(+), 1 deletion(-)
Documentation/config.txt +7 -0
@@ -413,6 +413,13 @@ core.protectNTFS:: 8.3 "short" names. Defaults to `true` on Windows, and `false` elsewhere. +core.fsmonitor:: + If set, the value of this variable is used as a command which + will identify all files that may have changed since the + requested date/time. This information is used to speed up git by + avoiding unnecessary processing of files that have not changed. + See the "fsmonitor-watchman" section of linkgit:githooks[5]. + core.trustctime:: If false, the ctime differences between the index and the working tree are ignored; useful when the inode change time
Documentation/git-ls-files.txt +6 -1
@@ -9,7 +9,7 @@ git-ls-files - Show information about files in the index and the working tree SYNOPSIS -------- [verse] -'git ls-files' [-z] [-t] [-v] +'git ls-files' [-z] [-t] [-v] [-f] (--[cached|deleted|others|ignored|stage|unmerged|killed|modified])* (-[c|d|o|i|s|u|k|m])* [--eol]
@@ -133,6 +133,11 @@ a space) at the start of each line: that are marked as 'assume unchanged' (see linkgit:git-update-index[1]). +-f:: + Similar to `-t`, but use lowercase letters for files + that are marked as 'fsmonitor valid' (see + linkgit:git-update-index[1]). + --full-name:: When run from a subdirectory, the command usually outputs paths relative to the current directory. This
Documentation/git-update-index.txt +45 -0
72 lines not on this page · raw message
Documentation/githooks.txt +28 -0
@@ -455,6 +455,34 @@ the name of the file that holds the e-mail to be sent. Exiting with a non-zero status causes 'git send-email' to abort before sending any e-mails. +fsmonitor-watchman +~~~~~~~~~~~~~~~~~~ + +This hook is invoked when the configuration option core.fsmonitor is +set to .git/hooks/fsmonitor-watchman. It takes two arguments, a version +(currently 1) and the time in elapsed nanoseconds since midnight, +January 1, 1970. + +The hook should output to stdout the list of all files in the working +directory that may have changed since the requested time. The logic +should be inclusive so that it does not miss any potential changes. +The paths should be relative to the root of the working directory +and be separated by a single NUL. + +It is OK to include files which have not actually changed. All changes +including newly-created and deleted files should be included. When +files are renamed, both the old and the new name should be included. + +Git will limit what files it checks for changes as well as which +directories are checked for untracked files based on the path names +given. + +An optimized way to tell git "all files have changed" is to return +the filename '/'. + +The exit status determines whether git will use the data from the +hook to limit its search. On error, it will fall back to verifying +all files and folders. GIT ---
Documentation/technical/index-format.txt +19 -0
@@ -295,3 +295,22 @@ The remaining data of each directory block is grouped by type: in the previous ewah bitmap. - One NUL. + +== File System Monitor cache + + The file system monitor cache tracks files for which the core.fsmonitor + hook has told us about changes. The signature for this extension is + { 'F', 'S', 'M', 'N' }. + + The extension starts with + + - 32-bit version number: the current supported version is 1. + + - 64-bit time: the extension data reflects all changes through the given + time which is stored as the nanoseconds elapsed since midnight, + January 1, 1970. + + - 32-bit bitmap size: the size of the CE_FSMONITOR_VALID bitmap. + + - An ewah bitmap, the n-th bit indicates whether the n-th index entry + is not CE_FSMONITOR_VALID.
Signature · 1 line
2.14.1.windows.1
Re: [PATCH v7 05/12] fsmonitor: add documentation for the fsmonitor extension.
Quoted 17 lines · Ben Peart
Documentation/git-update-index.txt +2
-0@@ -16,9 +16,11 @@ SYNOPSIS [--chmod=(+|-)x] [--[no-]assume-unchanged] [--[no-]skip-worktree] + [--[no-]fsmonitor-valid] [--ignore-submodules] [--[no-]split-index] [--[no-|test-|force-]untracked-cache] + [--[no-]fsmonitor] [--really-refresh] [--unresolve] [--again | -g] [--info-only] [--index-info] [-z] [--stdin] [--index-version <n>]
Documentation/git-update-index.txt you will need to handle the situation manually. · line 121
Quoted 9 lines
Documentation/git-update-index.txt +6
-0@@ -111,6 +113,12 @@ you will need to handle the situation manually. set and unset the "skip-worktree" bit for the paths. See section "Skip-worktree bit" below for more information. +--[no-]fsmonitor-valid:: + When one of these flags is specified, the object name recorded + for the paths are not updated. Instead, these options + set and unset the "fsmonitor valid" bit for the paths. See + section "File System Monitor" below for more information. +
So --no-foo does not undo --foo, but there are three values: --foo, --no-foo and <nothing/default>. I find that unintuitive, but maybe it's just me. Maybe there are other such options in the codebase already. How about --fsmonitor-valid=set, --fsmonitor-valid=unset, and --no-fsmonitor-valid (which would be the default, and which would forget any earlier --fsmonitor-valid=...)?
Documentation/git-update-index.txt will remove the intended effect of the option. · line 213
Quoted 9 lines
Documentation/git-update-index.txt +2
-0-g:: --again:: Runs 'git update-index' itself on the paths whose index@@ -201,6 +209,15 @@ will remove the intended effect of the option. `--untracked-cache` used to imply `--test-untracked-cache` but this option would enable the extension unconditionally. +--fsmonitor:: +--no-fsmonitor::
Maybe "--[no-]fsmonitor" for symmetry with how you've done it above and later.
Documentation/git-update-index.txt command reads the index; while when `--[no-|force-]untracked-cache` · line 480
Quoted 3 lines
Documentation/git-update-index.txt +3
-0+When used in conjunction with the untracked cache, it can further improve +performance by avoiding the cost of scaning the entire working directory +looking for new files.
s/scaning/scanning/
Documentation/git-update-index.txt command reads the index; while when `--[no-|force-]untracked-cache` · line 488
Quoted 7 lines
Documentation/git-update-index.txt +7
-0+If you want to enable (or disable) this feature, it is easier to use +the `core.fsmonitor` configuration variable (see +linkgit:git-config[1]) than using the `--fsmonitor` option to +`git update-index` in each repository, especially if you want to do so +across all repositories you use, because you can set the configuration +variable to `true` (or `false`) in your `$HOME/.gitconfig` just once +and have it affect all repositories you touch.
This is a mouthful. Maybe you could split it a little, perhaps like so:
If you want to enable (or disable) this feature, you will probably want to use the core.fsmonitor configuration variable (see linkgit:git-config[1]). By setting it to true (or false) in your $HOME/.gitconfig, it will affect all repositories you touch. For a more fine-grained control, you can set it per repository, or use the --fsmonitor option with git update-index in each repository.
The part about $HOME/.gitconfig vs per-repo config is perhaps generic enough that it doesn't belong here. So it'd only be about config vs. option. Where to place the config item and what implications that has is arguably orthogonal to knowing that the option exists and what it does.
Martin
Re: [PATCH v7 05/12] fsmonitor: add documentation for the fsmonitor extension.
Thanks for the review. I'm not an English major so appreciate the feedback on my attempts to document the feature.
Quoted 21 lines · Martin Ågren
Quoted 20 lines · Ben Peart
Documentation/git-update-index.txt +2
-0@@ -16,9 +16,11 @@ SYNOPSIS [--chmod=(+|-)x] [--[no-]assume-unchanged] [--[no-]skip-worktree] + [--[no-]fsmonitor-valid] [--ignore-submodules] [--[no-]split-index] [--[no-|test-|force-]untracked-cache] + [--[no-]fsmonitor] [--really-refresh] [--unresolve] [--again | -g] [--info-only] [--index-info] [-z] [--stdin] [--index-version <n>]@@ -111,6 +113,12 @@ you will need to handle the situation manually. set and unset the "skip-worktree" bit for the paths. See section "Skip-worktree bit" below for more information.
Quoted 10 lines
Documentation/git-update-index.txt you will need to handle the situation manually. · line 121
Quoted 6 lines
Documentation/git-update-index.txt +6
-0+--[no-]fsmonitor-valid:: + When one of these flags is specified, the object name recorded + for the paths are not updated. Instead, these options + set and unset the "fsmonitor valid" bit for the paths. See + section "File System Monitor" below for more information. +So --no-foo does not undo --foo, but there are three values: --foo, --no-foo and <nothing/default>. I find that unintuitive, but maybe it's just me. Maybe there are other such options in the codebase already.
I understand the unintuitive comment but the other such options in the code base are just above the fsmonitor options as it is modeled on how 'assume-unchanged' and 'skip-worktree' work. Consistency is certainly helps the intuitiveness as once you have learned the model, it applies in other places.
How
Quoted 11 lines
about --fsmonitor-valid=set, --fsmonitor-valid=unset, and --no-fsmonitor-valid (which would be the default, and which would forget any earlier --fsmonitor-valid=...)?
Quoted 7 lines
Documentation/git-update-index.txt +0
-0-g:: --again:: Runs 'git update-index' itself on the paths whose index@@ -201,6 +209,15 @@ will remove the intended effect of the option. `--untracked-cache` used to imply `--test-untracked-cache` but this option would enable the extension unconditionally.
Quoted 6 lines
Documentation/git-update-index.txt will remove the intended effect of the option. · line 213
Quoted 2 lines
Documentation/git-update-index.txt +2
-0+--fsmonitor:: +--no-fsmonitor::Maybe "--[no-]fsmonitor" for symmetry with how you've done it above and later.
For better and for worse, I choose to be consistent with how the options work (especially the untracked-cache option immediately above). This is one weakness of reviewing patches via email - you don't see the patch in context with everything around it.
Quoted 6 lines
Documentation/git-update-index.txt will remove the intended effect of the option. · line 216
Quoted 3 lines
Documentation/git-update-index.txt +3
-0+When used in conjunction with the untracked cache, it can further improve +performance by avoiding the cost of scaning the entire working directory +looking for new files.s/scaning/scanning/
Thanks!
Documentation/git-update-index.txt +1 -1
@@ -476,7 +476,7 @@ inform it as to what files have been modified. This enables git to avoid having to lstat() every file to find modified files. When used in conjunction with the untracked cache, it can further improve -performance by avoiding the cost of scaning the entire working directory +performance by avoiding the cost of scanning the entire working directory looking for new files. If you want to enable (or disable) this feature, it is easier to use
Quoted 10 lines
Documentation/git-update-index.txt will remove the intended effect of the option. · line 223
Quoted 7 lines
Documentation/git-update-index.txt +7
-0+If you want to enable (or disable) this feature, it is easier to use +the `core.fsmonitor` configuration variable (see +linkgit:git-config[1]) than using the `--fsmonitor` option to +`git update-index` in each repository, especially if you want to do so +across all repositories you use, because you can set the configuration +variable to `true` (or `false`) in your `$HOME/.gitconfig` just once +and have it affect all repositories you touch.This is a mouthful. Maybe you could split it a little, perhaps like so:
Quoted 7 lines
If you want to enable (or disable) this feature, you will probably want to use the
core.fsmonitorconfiguration variable (see linkgit:git-config[1]). By setting it totrue(orfalse) in your$HOME/.gitconfig, it will affect all repositories you touch. For a more fine-grained control, you can set it per repository, or use the--fsmonitoroption withgit update-indexin each repository.
I'm going to sound like a broken record here. :) The description favored consistency with the untracked cache feature immediate above this entry.
It is literally a copy/paste/edit.
This is based on the assumption that the text had already been reviewed and found to be acceptable. This also means if you have figured it out for one option, when you read the next, you're understanding can carry forward speeding up your comprehension.
Quoted 7 lines
The part about $HOME/.gitconfig vs per-repo config is perhaps generic enough that it doesn't belong here. So it'd only be about config vs. option. Where to place the config item and what implications that has is arguably orthogonal to knowing that the option exists and what it does.
Martin
Re: [PATCH v7 05/12] fsmonitor: add documentation for the fsmonitor extension.
Quoted 19 lines · Ben Peart
Quoted 11 lines
Review
Quoted 6 lines
+--[no-]fsmonitor-valid::
(quoted diff) +5
-0+ When one of these flags is specified, the object name recorded + for the paths are not updated. Instead, these options + set and unset the "fsmonitor valid" bit for the paths. See + section "File System Monitor" below for more information. +So --no-foo does not undo --foo, but there are three values: --foo, --no-foo and <nothing/default>. I find that unintuitive, but maybe it's just me. Maybe there are other such options in the codebase already.
I understand the unintuitive comment but the other such options in the code base are just above the fsmonitor options as it is modeled on how 'assume-unchanged' and 'skip-worktree' work. Consistency is certainly helps the intuitiveness as once you have learned the model, it applies in other places.
[...]
Quoted 6 lines
For better and for worse, I choose to be consistent with how the options work (especially the untracked-cache option immediately above). This is one weakness of reviewing patches via email - you don't see the patch in context with everything around it.
[...]
Quoted 4 lines
I'm going to sound like a broken record here. :) The description favored consistency with the untracked cache feature immediate above this entry. It is literally a copy/paste/edit.
Oh. Well, that's what I get for "reviewing" by e-mail. You are indeed following the current style very well! Sorry for the noise.
Martin
PATCH v7 6/12 ls-files: Add support in ls-files to display the fsmonitor valid bit
In reply to Ben PeartAdd a new command line option (-f) to ls-files to have it use lowercase letters for 'fsmonitor valid' files
--- builtin/ls-files.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
builtin/ls-files.c +6 -2
@@ -31,6 +31,7 @@ static int show_resolve_undo;static int show_modified;static int show_killed;static int show_valid_bit; +static int show_fsmonitor_bit;static int line_terminator ='\n' ;static int debug_mode;static int show_eol;
@@ -86,7 +87,8 @@ static const char *get_tag(const struct cache_entry *ce, const char *tag) {static char alttag[4 ]; -if (tag && *tag && show_valid_bit && (ce->ce_flags &CE_VALID )) { +if (tag && *tag && ((show_valid_bit && (ce->ce_flags &CE_VALID )) || + (show_fsmonitor_bit && (ce->ce_flags &CE_FSMONITOR_VALID )))) {memcpy (alttag, tag,3 );if (isalpha (tag[0 ])) {
@@ -515,6 +517,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("identify the file status with tags")),OPT_BOOL ('v' ,NULL , &show_valid_bit,N_ ("use lowercase letters for 'assume unchanged' files" )), +OPT_BOOL ('f' ,NULL , &show_fsmonitor_bit, +N_ ("use lowercase letters for 'fsmonitor clean' files" )),OPT_BOOL ('c' ,"cached" , &show_cached,N_ ("show cached files in the output (default)" )), OPT_BOOL('d', "deleted", &show_deleted,
@@ -584,7 +588,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)for (i =0 ; i < exclude_list.nr; i++) {add_exclude (exclude_list.items[i].string,"" ,0 , el, --exclude_args); } -if (show_tag || show_valid_bit) { +if (show_tag || show_valid_bit || show_fsmonitor_bit) { tag_cached ="H " ; tag_unmerged ="M " ; tag_removed ="R " ;
Signature · 1 line
2.14.1.windows.1
RE: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bit
Quoted 10 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Tuesday, September 19, 2017 3:28 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bit
Quoted 7 lines
Add a new command line option (-f) to ls-files to have it use lowercase letters for 'fsmonitor valid' files
Signed-off-by Ben Peart <[email protected]>--- builtin/ls-files.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
This is still missing the corresponding documentation patch.
I can see from replies that at least some of my messages got through. In total, I sent messages about:
04/12 (I see replies)
05/12 (I see replies)
06/12 (no reply, issue not fixed)
10/12 (no reply, haven't checked whether same issue but I assume same issue since the new case I mentioned isn't added) 12/12 (no reply, typo fixed -- no reply required)
Re: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bit
Quoted 13 lines · David Turner
Quoted 13 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Tuesday, September 19, 2017 3:28 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bitAdd a new command line option (-f) to ls-files to have it use lowercase letters for 'fsmonitor valid' files
Quoted 6 lines
Quoted 4 lines
Signed-off-by Ben Peart <[email protected]>--- builtin/ls-files.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)This is still missing the corresponding documentation patch.
Sorry for the confusion.
The documentation is all in a patch together as they all have links to each other. You can find it here:
https://public-inbox.org/git/[email protected]/T/#u
Quoted 5 lines
I can see from replies that at least some of my messages got through. In total, I sent messages about:
04/12 (I see replies)
05/12 (I see replies)
06/12 (no reply, issue not fixed)
The documentation is all in a patch together as they all have links to each other. You can find it here:
https://public-inbox.org/git/[email protected]/T/#u
Quoted 1 line
10/12 (no reply, haven't checked whether same issue but I assume same issue since the new case I mentioned isn't added)
It wasn't a bug so I didn't "fix" it. I just sent an explanation and patch demonstrating why. You can find it here:
https://public-inbox.org/git/[email protected]/T/#u
Quoted 2 lines
12/12 (no reply, typo fixed -- no reply required)
Hope this helps.
RE: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bit
Quoted 28 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Tuesday, September 19, 2017 4:45 PM
To: David Turner <[email protected]>; 'Ben Peart' <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bitQuoted 13 lines · David Turner
Quoted 13 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Tuesday, September 19, 2017 3:28 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bitAdd a new command line option (-f) to ls-files to have it use lowercase letters for 'fsmonitor valid' files
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Signed-off-by Ben Peart <[email protected]>--- builtin/ls-files.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)This is still missing the corresponding documentation patch.
Sorry for the confusion.
Thanks for following up.
Quoted 5 lines
Quoted 2 lines
10/12 (no reply, haven't checked whether same issue but I assume same issue since the new case I mentioned isn't added)
It wasn't a bug so I didn't "fix" it. I just sent an explanation and patch demonstrating why. You can find it here:
I was concerned about the case of an untracked file inside a directory that contains no tracked files. Your patch in this mail treats dir3 just like dir1 and dir2. I think you ought to test the case of a dir with no tracked files.
After more careful checking, it looks like this case does work, but it's still worth testing.
RE: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bit
Quoted 53 lines
-----Original Message-----
From: David Turner [mailto:[email protected]]
Sent: Tuesday, September 19, 2017 5:27 PM
To: 'Ben Peart' <[email protected]>; Ben Peart <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: RE: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bitQuoted 27 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Tuesday, September 19, 2017 4:45 PM
To: David Turner <[email protected]>; 'Ben Peart' <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bitQuoted 13 lines · David Turner
Quoted 13 lines
-----Original Message-----
From: Ben Peart [mailto:[email protected]]
Sent: Tuesday, September 19, 2017 3:28 PM
To: [email protected]
Cc: David Turner <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v7 06/12] ls-files: Add support in ls-files to display the fsmonitor valid bitAdd a new command line option (-f) to ls-files to have it use lowercase letters for 'fsmonitor valid' files
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Signed-off-by Ben Peart <[email protected]>--- builtin/ls-files.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)This is still missing the corresponding documentation patch.
Sorry for the confusion.
Thanks for following up.
Quoted 3 lines
Quoted 2 lines
10/12 (no reply, haven't checked whether same issue but I assume same issue since the new case I mentioned isn't added)
Quoted 7 lines
Quoted 2 lines
It wasn't a bug so I didn't "fix" it. I just sent an explanation and patch demonstrating why. You can find it here:
I was concerned about the case of an untracked file inside a directory that contains no tracked files. Your patch in this mail treats dir3 just like dir1 and dir2. I think you ought to test the case of a dir with no tracked files.
In the case where there is an untracked file inside a directory that contains no tracked files, git will (as shown by the "failing" test) actually find the untracked file. This is the correct/expected behavior. The test failure is just indicating that the optimization of not searching that directory for untracked files was not able to occur (because there was no entry in the untracked cache for that directory).
Quoted 2 lines
After more careful checking, it looks like this case does work, but it's still worth testing.
PATCH v7 7/12 update-index: add fsmonitor support to update-index
In reply to Ben PeartAdd support in update-index to manually add/remove the fsmonitor extension via --[no-]fsmonitor flags.
Add support in update-index to manually set/clear the fsmonitor valid bit via --[no-]fsmonitor-valid flags.
--- builtin/update-index.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)
builtin/update-index.c +32 -1
@@ -33,6 +33,7 @@ static int force_remove;static int verbose;static int mark_valid_only;static int mark_skip_worktree_only; +static int mark_fsmonitor_only;#define MARK_FLAG 1#define UNMARK_FLAG 2static struct strbuf mtime_dir =STRBUF_INIT ;
@@ -229,12 +230,12 @@ static int mark_ce_flags(const char *path, int flag, int mark)int namelen =strlen (path);int pos =cache_name_pos (path, namelen);if (0 <= pos) { +mark_fsmonitor_invalid (&the_index, active_cache[pos]);if (mark) active_cache[pos]->ce_flags |= flag;else active_cache[pos]->ce_flags &= ~flag; active_cache[pos]->ce_flags |=CE_UPDATE_IN_BASE ; -mark_fsmonitor_invalid (&the_index, active_cache[pos]);cache_tree_invalidate_path (&the_index, path); active_cache_changed |=CE_ENTRY_CHANGED ;return 0 ;
@@ -460,6 +461,11 @@ static void update_one(const char *path)die ("Unable to mark file %s" , path);return ; } +if (mark_fsmonitor_only) { +if (mark_ce_flags (path,CE_FSMONITOR_VALID , mark_fsmonitor_only ==MARK_FLAG )) +die ("Unable to mark file %s" , path); +return ; + }if (force_remove) {if (remove_file_from_cache (path))
@@ -918,6 +924,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)int lock_error =0 ;int split_index =-1 ;int force_write =0 ; +int fsmonitor =-1 ;struct lock_file *lock_file;struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;
@@ -1011,6 +1018,14 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("enable untracked cache without testing the filesystem"), UC_FORCE),OPT_SET_INT (0 ,"force-write-index" , &force_write,N_ ("write out the index even if is not flagged as changed" ),1 ), +OPT_BOOL (0 ,"fsmonitor" , &fsmonitor, +N_ ("enable or disable file system monitor" )), + {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL, +N_ ("mark files as fsmonitor valid" ), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, + {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL, +N_ ("clear fsmonitor valid bit" ), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},OPT_END () };
@@ -1152,6 +1167,22 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)die ("BUG: bad untracked_cache value: %d" , untracked_cache); } +if (fsmonitor >0 ) { +if (git_config_get_fsmonitor () ==0 ) +warning (_ ("core.fsmonitor is unset; " +"set it if you really want to " +"enable fsmonitor" )); +add_fsmonitor (&the_index); +report (_ ("fsmonitor enabled" )); + }else if (!fsmonitor) { +if (git_config_get_fsmonitor () ==1 ) +warning (_ ("core.fsmonitor is set; " +"remove it if you really want to " +"disable fsmonitor" )); +remove_fsmonitor (&the_index); +report (_ ("fsmonitor disabled" )); + } +if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )
Signature · 1 line
2.14.1.windows.1
PATCH v7 8/12 fsmonitor: add a test tool to dump the index extension
In reply to Ben PeartAdd a test utility (test-dump-fsmonitor) that will dump the fsmonitor index extension.
--- Makefile | 1 + t/helper/test-dump-fsmonitor.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 t/helper/test-dump-fsmonitor.c
Makefile +1 -0
@@ -639,6 +639,7 @@ TEST_PROGRAMS_NEED_X += test-config TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta TEST_PROGRAMS_NEED_X += test-dump-cache-tree +TEST_PROGRAMS_NEED_X += test-dump-fsmonitor TEST_PROGRAMS_NEED_X += test-dump-split-index TEST_PROGRAMS_NEED_X += test-dump-untracked-cache TEST_PROGRAMS_NEED_X += test-fake-ssh
t/helper/test-dump-fsmonitor.c new file +21 -0
@@ -0,0 +1,21 @@ +#include "cache.h" + +int cmd_main (int ac,const char **av) +{ +struct index_state *istate = &the_index; +int i; + +setup_git_directory (); +if (do_read_index (istate,get_index_file (),0 ) <0 ) +die ("unable to read index file" ); +if (!istate->fsmonitor_last_update) { +printf ("no fsmonitor\n" ); +return 0 ; + } +printf ("fsmonitor last update %" PRIuMAX"\n" , (uintmax_t )istate->fsmonitor_last_update); + +for (i =0 ; i < istate->cache_nr; i++) +printf ((istate->cache[i]->ce_flags &CE_FSMONITOR_VALID ) ?"+" :"-" ); + +return 0 ; +}
Signature · 1 line
2.14.1.windows.1
PATCH v7 9/12 split-index: disable the fsmonitor extension when running the split index test
In reply to Ben PeartThe split index test t1700-split-index.sh has hard coded SHA values for the index. Currently it supports index V4 and V3 but assumes there are no index extensions loaded.
When manually forcing the fsmonitor extension to be turned on when running the test suite, the SHA values no longer match which causes the test to fail.
The potential matrix of index extensions and index versions can is quite large so instead disable the extension before attempting to run the test.
--- t/t1700-split-index.sh | 1 + 1 file changed, 1 insertion(+)
t/t1700-split-index.sh +1 -0
@@ -6,6 +6,7 @@ test_description='split index mode tests' # We need total control of index splitting here sane_unset GIT_TEST_SPLIT_INDEX +sane_unset GIT_FSMONITOR_TEST test_expect_success 'enable split index' ' git config splitIndex.maxPercentChange 100 &&
Signature · 1 line
2.14.1.windows.1
PATCH v7 10/12 fsmonitor: add test cases for fsmonitor extension
In reply to Ben PeartTest the ability to add/remove the fsmonitor index extension via update-index.
Test that dirty files returned from the integration script are properly represented in the index extension and verify that ls-files correctly reports their state.
Test that ensure status results are correct when using the new fsmonitor extension. Test untracked, modified, and new files by ensuring the results are identical to when not using the extension.
Test that if the fsmonitor extension doesn't tell git about a change, it doesn't discover it on its own. This ensures git is honoring the extension and that we get the performance benefits desired.
Three test integration scripts are provided:
fsmonitor-all - marks all files as dirty fsmonitor-none - marks no files as dirty fsmonitor-watchman - integrates with Watchman with debug logging
To run tests in the test suite while utilizing fsmonitor:
First copy t/t7519/fsmonitor-all to a location in your path and then set GIT_FORCE_PRELOAD_TEST=true and GIT_FSMONITOR_TEST=fsmonitor-all and run your tests.
Note: currently when using the test script fsmonitor-watchman on Windows, many tests fail due to a reported but not yet fixed bug in Watchman where it holds on to handles for directories and files which prevents the test directory from being cleaned up properly.
--- t/t7519-status-fsmonitor.sh | 304 ++++++++++++++++++++++++++++++++++++++++++++ t/t7519/fsmonitor-all | 24 ++++ t/t7519/fsmonitor-none | 22 ++++ t/t7519/fsmonitor-watchman | 140 ++++++++++++++++++++ 4 files changed, 490 insertions(+) create mode 100755 t/t7519-status-fsmonitor.sh create mode 100755 t/t7519/fsmonitor-all create mode 100755 t/t7519/fsmonitor-none create mode 100755 t/t7519/fsmonitor-watchman
t/t7519-status-fsmonitor.sh new file +304 -0
@@ -0,0 +1,304 @@ +#!/bin/sh + +test_description='git status with file system watcher' + +. ./test-lib.sh + +# +# To run the entire git test suite using fsmonitor: +# +# copy t/t7519/fsmonitor-all to a location in your path and then set +# GIT_FSMONITOR_TEST=fsmonitor-all and run your tests. +# + +# Note, after "git reset --hard HEAD" no extensions exist other than 'TREE' +# "git update-index --fsmonitor" can be used to get the extension written +# before testing the results. + +clean_repo () { + git reset --hard HEAD && + git clean -fd +} + +dirty_repo () { + : >untracked && + : >dir1/untracked && + : >dir2/untracked && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + echo 4 >new && + echo 5 >dir1/new && + echo 6 >dir2/new +} + +write_integration_script () { + write_script .git/hooks/fsmonitor-test<<-\EOF + if test "$#" -ne 2 + then + echo "$0: exactly 2 arguments expected" + exit 2 + fi + if test "$1" != 1 + then + echo "Unsupported core.fsmonitor hook version." >&2 + exit 1 + fi + printf "untracked\0" + printf "dir1/untracked\0" + printf "dir2/untracked\0" + printf "modified\0" + printf "dir1/modified\0" + printf "dir2/modified\0" + printf "new\0" + printf "dir1/new\0" + printf "dir2/new\0" + EOF +} + +test_lazy_prereq UNTRACKED_CACHE ' + { git update-index --test-untracked-cache; ret=$?; } && + test $ret -ne 1 +' + +test_expect_success 'setup' ' + mkdir -p .git/hooks && + : >tracked && + : >modified && + mkdir dir1 && + : >dir1/tracked && + : >dir1/modified && + mkdir dir2 && + : >dir2/tracked && + : >dir2/modified && + git -c core.fsmonitor= add . && + git -c core.fsmonitor= commit -m initial && + git config core.fsmonitor .git/hooks/fsmonitor-test && + cat >.gitignore <<-\EOF + .gitignore + expect* + actual* + marker* + EOF +' + +# test that the fsmonitor extension is off by default +test_expect_success 'fsmonitor extension is off by default' ' + test-dump-fsmonitor >actual && + grep "^no fsmonitor" actual +' + +# test that "update-index --fsmonitor" adds the fsmonitor extension +test_expect_success 'update-index --fsmonitor" adds the fsmonitor extension' ' + git update-index --fsmonitor && + test-dump-fsmonitor >actual && + grep "^fsmonitor last update" actual +' + +# test that "update-index --no-fsmonitor" removes the fsmonitor extension +test_expect_success 'update-index --no-fsmonitor" removes the fsmonitor extension' ' + git update-index --no-fsmonitor && + test-dump-fsmonitor >actual && + grep "^no fsmonitor" actual +' + +cat >expect <<EOF && +h dir1/modified +H dir1/tracked +h dir2/modified +H dir2/tracked +h modified +H tracked +EOF + +# test that "update-index --fsmonitor-valid" sets the fsmonitor valid bit +test_expect_success 'update-index --fsmonitor-valid" sets the fsmonitor valid bit' ' + git update-index --fsmonitor && + git update-index --fsmonitor-valid dir1/modified && + git update-index --fsmonitor-valid dir2/modified && + git update-index --fsmonitor-valid modified && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +H dir1/tracked +H dir2/modified +H dir2/tracked +H modified +H tracked +EOF + +# test that "update-index --no-fsmonitor-valid" clears the fsmonitor valid bit +test_expect_success 'update-index --no-fsmonitor-valid" clears the fsmonitor valid bit' ' + git update-index --no-fsmonitor-valid dir1/modified && + git update-index --no-fsmonitor-valid dir2/modified && + git update-index --no-fsmonitor-valid modified && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +H dir1/tracked +H dir2/modified +H dir2/tracked +H modified +H tracked +EOF + +# test that all files returned by the script get flagged as invalid +test_expect_success 'all files returned by integration script get flagged as invalid' ' + write_integration_script && + dirty_repo && + git update-index --fsmonitor && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +h dir1/new +H dir1/tracked +H dir2/modified +h dir2/new +H dir2/tracked +H modified +h new +H tracked +EOF + +# test that newly added files are marked valid +test_expect_success 'newly added files are marked valid' ' + git add new && + git add dir1/new && + git add dir2/new && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +h dir1/new +h dir1/tracked +H dir2/modified +h dir2/new +h dir2/tracked +H modified +h new +h tracked +EOF + +# test that all unmodified files get marked valid +test_expect_success 'all unmodified files get marked valid' ' + # modified files result in update-index returning 1 + test_must_fail git update-index --refresh --force-write-index && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +h dir1/tracked +h dir2/modified +h dir2/tracked +h modified +h tracked +EOF + +# test that *only* files returned by the integration script get flagged as invalid +test_expect_success '*only* files returned by the integration script get flagged as invalid' ' + write_script .git/hooks/fsmonitor-test<<-\EOF && + printf "dir1/modified\0" + EOF + clean_repo && + git update-index --refresh --force-write-index && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + test_must_fail git update-index --refresh --force-write-index && + git ls-files -f >actual && + test_cmp expect actual +' + +# Ensure commands that call refresh_index() to move the index back in time +# properly invalidate the fsmonitor cache +test_expect_success 'refresh_index() invalidates fsmonitor cache' ' + write_script .git/hooks/fsmonitor-test<<-\EOF && + EOF + clean_repo && + dirty_repo && + git add . && + git commit -m "to reset" && + git reset HEAD~1 && + git status >actual && + git -c core.fsmonitor= status >expect && + test_i18ncmp expect actual +' + +# test fsmonitor with and without preloadIndex +preload_values="false true" +for preload_val in $preload_values +do + test_expect_success "setup preloadIndex to $preload_val" ' + git config core.preloadIndex $preload_val && + if test $preload_val = true + then + GIT_FORCE_PRELOAD_TEST=$preload_val; export GIT_FORCE_PRELOAD_TEST + else + unset GIT_FORCE_PRELOAD_TEST + fi + ' + + # test fsmonitor with and without the untracked cache (if available) + uc_values="false" + test_have_prereq UNTRACKED_CACHE && uc_values="false true" + for uc_val in $uc_values + do + test_expect_success "setup untracked cache to $uc_val" ' + git config core.untrackedcache $uc_val + ' + + # Status is well tested elsewhere so we'll just ensure that the results are + # the same when using core.fsmonitor. + test_expect_success 'compare status with and without fsmonitor' ' + write_integration_script && + clean_repo && + dirty_repo && + git add new && + git add dir1/new && + git add dir2/new && + git status >actual && + git -c core.fsmonitor= status >expect && + test_i18ncmp expect actual + ' + + # Make sure it's actually skipping the check for modified and untracked + # (if enabled) files unless it is told about them. + test_expect_success "status doesn't detect unreported modifications" ' + write_script .git/hooks/fsmonitor-test<<-\EOF && + :>marker + EOF + clean_repo && + git status && + test_path_is_file marker && + dirty_repo && + rm -f marker && + git status >actual && + test_path_is_file marker && + test_i18ngrep ! "Changes not staged for commit:" actual && + if test $uc_val = true + then + test_i18ngrep ! "Untracked files:" actual + fi && + if test $uc_val = false + then + test_i18ngrep "Untracked files:" actual + fi && + rm -f marker + ' + done +done + +test_done
t/t7519/fsmonitor-all new file +24 -0
@@ -0,0 +1,24 @@ +#!/bin/sh +# +# An test hook script to integrate with git to test fsmonitor. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +#echo "$0 $*" >&2 + +if test "$#" -ne 2 +then + echo "$0: exactly 2 arguments expected" >&2 + exit 2 +fi + +if test "$1" != 1 +then + echo "Unsupported core.fsmonitor hook version." >&2 + exit 1 +fi + +echo "/"
t/t7519/fsmonitor-none new file +22 -0
@@ -0,0 +1,22 @@ +#!/bin/sh +# +# An test hook script to integrate with git to test fsmonitor. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +#echo "$0 $*" >&2 + +if test "$#" -ne 2 +then + echo "$0: exactly 2 arguments expected" >&2 + exit 2 +fi + +if test "$1" != 1 +then + echo "Unsupported core.fsmonitor hook version." >&2 + exit 1 +fi
t/t7519/fsmonitor-watchman new file +140 -0
140 lines not on this page · raw message
Signature · 1 line
2.14.1.windows.1
PATCH v7 11/12 fsmonitor: add a sample integration script for Watchman
In reply to Ben PeartThis script integrates the new fsmonitor capabilities of git with the cross platform Watchman file watching service. To use the script:
Download and install Watchman from https://facebook.github.io/watchman/.
Rename the sample integration hook from fsmonitor-watchman.sample to fsmonitor-watchman. Configure git to use the extension:
git config core.fsmonitor .git/hooks/fsmonitor-watchman
Optionally turn on the untracked cache for optimal performance.
--- templates/hooks--fsmonitor-watchman.sample | 122 +++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100755 templates/hooks--fsmonitor-watchman.sample
templates/hooks--fsmonitor-watchman.sample new file +122 -0
@@ -0,0 +1,122 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $time) = @ARGV; + +# Check the hook interface version + +if ($version == 1) { + # convert nanoseconds to seconds + $time = int $time / 1000000000; +} else { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +# Convert unix style paths to escaped Windows style paths when running +# in Windows command prompt + +my $system = `uname -s`; +$system =~ s/[\r\n]+//g; +my $git_work_tree; + +if ($system =~ m/^MSYS_NT/) { + $git_work_tree = `cygpath -aw "\$PWD"`; + $git_work_tree =~ s/[\r\n]+//g; + $git_work_tree =~ s,\\,/,g; +} else { + $git_work_tree = $ENV{'PWD'}; +} + +my $retry = 1; + +launch_watchman(); + +sub launch_watchman { + + # Set input record separator + local $/ = 0666; + + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $time but were not transient (ie created after + # $time but no longer exist). + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + # + # The category of transient files that we want to ignore will have a + # creation clock (cclock) newer than $time_t value and will also not + # currently exist. + + my $query = <<" END"; + ["query", "$git_work_tree", { + "since": $time, + "fields": ["name"], + "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]] + }] + END + + print CHLD_IN $query; + my $response = <CHLD_OUT>; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + my $json_pkg; + eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; + } or do { + require JSON::PP; + $json_pkg = "JSON::PP"; + }; + + my $o = $json_pkg->new->utf8->decode($response); + + if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) { + print STDERR "Adding '$git_work_tree' to watchman's watch list.\n"; + $retry--; + qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + print "/\0"; + eval { launch_watchman() }; + exit 0; + } + + die "Watchman: $o->{error}.\n" . + "Falling back to scanning...\n" if $o->{error}; + + binmode STDOUT, ":utf8"; + local $, = "\0"; + print @{$o->{files}}; +}
Signature · 1 line
2.14.1.windows.1
PATCH v7 12/12 fsmonitor: add a performance test
In reply to Ben PeartAdd a test utility (test-drop-caches) that flushes all changes to disk then drops file system cache on Windows, Linux, and OSX.
Add a perf test (p7519-fsmonitor.sh) for fsmonitor.
By default, the performance test will utilize the Watchman file system monitor if it is installed. If Watchman is not installed, it will use a dummy integration script that does not report any new or modified files. The dummy script has very little overhead which provides optimistic results.
The performance test will also use the untracked cache feature if it is available as fsmonitor uses it to speed up scanning for untracked files.
There are 3 environment variables that can be used to alter the default behavior of the performance test:
GIT_PERF_7519_UNTRACKED_CACHE: used to configure core.untrackedCacheGIT_PERF_7519_SPLIT_INDEX: used to configure core.splitIndexGIT_PERF_7519_FSMONITOR: used to configure core.fsMonitor
The big win for using fsmonitor is the elimination of the need to scan the working directory looking for changed and untracked files. If the file information is all cached in RAM, the benefits are reduced.
GIT_PERF_7519_DROP_CACHE: if set, the OS caches are dropped between tests
--- Makefile | 1 + t/helper/.gitignore | 1 + t/helper/test-drop-caches.c | 162 ++++++++++++++++++++++++++++++++++++++ t/perf/p7519-fsmonitor.sh | 184 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 348 insertions(+) create mode 100644 t/helper/test-drop-caches.c create mode 100755 t/perf/p7519-fsmonitor.sh
Makefile +1 -0
@@ -638,6 +638,7 @@ TEST_PROGRAMS_NEED_X += test-ctype TEST_PROGRAMS_NEED_X += test-config TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta +TEST_PROGRAMS_NEED_X += test-drop-caches TEST_PROGRAMS_NEED_X += test-dump-cache-tree TEST_PROGRAMS_NEED_X += test-dump-fsmonitor TEST_PROGRAMS_NEED_X += test-dump-split-index
t/helper/.gitignore +1 -0
@@ -3,6 +3,7 @@ /test-config /test-date /test-delta +/test-drop-caches /test-dump-cache-tree /test-dump-split-index /test-dump-untracked-cache
t/helper/test-drop-caches.c new file +162 -0
162 lines not on this page · raw message
t/perf/p7519-fsmonitor.sh new file +184 -0
184 lines not on this page · raw message
Signature · 1 line
2.14.1.windows.1
v8 cover Fast git status via a file system watcher
In reply to Ben PeartThanks again to everyone who provided feedback. There are several spelling, documentation, and code comment changes.
The only behavioral change from V7 is the removal of unnecessary uses of CE_MATCH_IGNORE_FSMONITOR. With a better understanding of *why* the CE_MATCH_IGNORE_* flags are used, it is now clear they are not required in most cases where CE_MATCH_IGNORE_FSMONITOR was being passed out of an abundance of caution.
Since the fsmonitor data can be trusted and is kept in sync with the working directory, the only remaining valid uses are those locations where we don't want to trigger an unneeded refresh_fsmonitor() call.
One is where preload_index() is doing a fast precompute of state for the bulk of the index entries but is not required for correctness as refresh_cache_ent() will ensure any "missed" by preload_index() are up-to-date if/when they are needed.
The second is in is_staging_gitmodules_ok() where we don't want to trigger a complete refresh just to check the .gitignore file.
The net result of this change will be that there are more cases where we will be able to use the cached index state and avoid unnecessary lstat() calls.
Interdiff between V7 and V8:
Documentation/git-update-index.txt +1 -1
@@ -476,7 +476,7 @@ inform it as to what files have been modified. This enables git to avoid having to lstat() every file to find modified files. When used in conjunction with the untracked cache, it can further improve -performance by avoiding the cost of scaning the entire working directory +performance by avoiding the cost of scanning the entire working directory looking for new files. If you want to enable (or disable) this feature, it is easier to use
apply.c +1 -1
@@ -3399,7 +3399,7 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st)return -1 ;return 0 ; } -return ce_match_stat (ce, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); +return ce_match_stat (ce, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); }#define SUBMODULE_PATCH_WITHOUT_INDEX 1
cache.h +1 -1
@@ -682,7 +682,7 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,#define CE_MATCH_IGNORE_MISSING 0x08/* enable stat refresh */ #define CE_MATCH_REFRESH 0x10 -/* do stat comparison even if CE_FSMONITOR_VALID is true */ +/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */ #define CE_MATCH_IGNORE_FSMONITOR 0X20extern int ie_match_stat (struct index_state *,const struct cache_entry *,struct stat *,unsigned int );extern int ie_modified (struct index_state *,const struct cache_entry *,struct stat *,unsigned int );
entry.c +1 -1
@@ -404,7 +404,7 @@ int checkout_entry(struct cache_entry *ce,if (!check_path (path.buf, path.len, &st, state->base_dir_len)) {const struct submodule *sub; -unsigned changed =ce_match_stat (ce, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); +unsigned changed =ce_match_stat (ce, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE );/* * Needs to be checked before !changed returns early, * as the possibly empty directory was not changed
fsmonitor.c +1 -1
@@ -176,7 +176,7 @@ void refresh_fsmonitor(struct index_state *istate) core_fsmonitor, query_success ? "success" : "failure"); } -/* a fsmonitor process can return '*' to indicate all entries are invalid */ +/* a fsmonitor process can return '/' to indicate all entries are invalid */ if (query_success && query_result.buf[0 ] !='/' ) {/* Mark all entries returned by the monitor as dirty */ buf = query_result.buf;
fsmonitor.h +8 -3
@@ -35,7 +35,9 @@ extern void tweak_fsmonitor(struct index_state *istate);extern void refresh_fsmonitor (struct index_state *istate);/* -* Set the given cache entries CE_FSMONITOR_VALID bit. +* Set the given cache entries CE_FSMONITOR_VALID bit. This should be +* called any time the cache entry has been updated to reflect the +* current state of the file on disk. */ static inline void mark_fsmonitor_valid (struct cache_entry *ce) {
@@ -46,8 +48,11 @@ static inline void mark_fsmonitor_valid(struct cache_entry *ce) }/* -* Clear the given cache entry's CE_FSMONITOR_VALID bit and invalidate any -* corresponding untracked cache directory structures. +* Clear the given cache entry's CE_FSMONITOR_VALID bit and invalidate +* any corresponding untracked cache directory structures. This should +* be called any time git creates or modifies a file that should +* trigger an lstat() or invalidate the untracked cache for the +* corresponding directory */ static inline void mark_fsmonitor_invalid (struct index_state *istate,struct cache_entry *ce) {
read-cache.c +2 -2
@@ -641,7 +641,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,int size, namelen, was_same;mode_t st_mode = st->st_mode;struct cache_entry *ce, *alias; -unsigned ce_option =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_RACY_IS_DIRTY |CE_MATCH_IGNORE_FSMONITOR ; +unsigned ce_option =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_RACY_IS_DIRTY ;int verbose = flags & (ADD_CACHE_VERBOSE |ADD_CACHE_PRETEND );int pretend = flags &ADD_CACHE_PRETEND ;int intent_only = flags &ADD_CACHE_INTENT ;
@@ -1356,7 +1356,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,int first =1 ;int in_porcelain = (flags &REFRESH_IN_PORCELAIN );unsigned int options = (CE_MATCH_REFRESH | - (really ?CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_FSMONITOR :0 ) | + (really ?CE_MATCH_IGNORE_VALID :0 ) | (not_new ?CE_MATCH_IGNORE_MISSING :0 ));const char *modified_fmt;const char *deleted_fmt;
t/helper/test-drop-caches.c +4 -2
@@ -82,6 +82,8 @@ static int cmd_dropcaches(void)HANDLE hProcess =GetCurrentProcess ();HANDLE hToken;HMODULE ntdll; +DWORD (WINAPI *NtSetSystemInformation)(INT ,PVOID ,ULONG ); +SYSTEM_MEMORY_LIST_COMMAND command;int status;if (!OpenProcessToken (hProcess,TOKEN_QUERY |TOKEN_ADJUST_PRIVILEGES , &hToken))
@@ -96,12 +98,12 @@ static int cmd_dropcaches(void)if (!ntdll)return error ("Can't load ntdll.dll, wrong Windows version?" ); -DWORD (WINAPI *NtSetSystemInformation)(INT ,PVOID ,ULONG ) = + NtSetSystemInformation = (DWORD (WINAPI *)(INT ,PVOID ,ULONG ))GetProcAddress (ntdll,"NtSetSystemInformation" );if (!NtSetSystemInformation)return error ("Can't get function addresses, wrong Windows version?" ); -SYSTEM_MEMORY_LIST_COMMAND command = MemoryPurgeStandbyList; + command = MemoryPurgeStandbyList; status = NtSetSystemInformation( SystemMemoryListInformation, &command,
unpack-trees.c +3 -3
@@ -1456,7 +1456,7 @@ static int verify_uptodate_1(const struct cache_entry *ce,return 0 ;if (!lstat (ce->name, &st)) { -int flags =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ; +int flags =CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ;unsigned changed =ie_match_stat (o->src_index, ce, &st, flags);if (submodule_from_ce (ce)) {
@@ -1612,7 +1612,7 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int leconst struct cache_entry *src; src =index_file_exists (o->src_index, name, len,1 ); -return src && !ie_match_stat (o->src_index, src, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR ); +return src && !ie_match_stat (o->src_index, src, st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE ); } static int check_ok_to_remove(const char *name, int len, int dtype,
@@ -2136,7 +2136,7 @@ int oneway_merge(const struct cache_entry * const *src,if (o->reset && o->update && !ce_uptodate (old) && !ce_skip_worktree (old)) {struct stat st;if (lstat (old->name, &st) || -ie_match_stat (o->src_index, old, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE |CE_MATCH_IGNORE_FSMONITOR )) +ie_match_stat (o->src_index, old, &st,CE_MATCH_IGNORE_VALID |CE_MATCH_IGNORE_SKIP_WORKTREE )) update |=CE_UPDATE ; }add_entry (o, old, update,0 );
Ben Peart (12):
bswap: add 64 bit endianness helper get_be64
preload-index: add override to enable testing preload-index
update-index: add a new --force-write-index option
fsmonitor: teach git to optionally utilize a file system monitor to
speed up detecting new or changed files.
fsmonitor: add documentation for the fsmonitor extension.
ls-files: Add support in ls-files to display the fsmonitor valid bit
update-index: add fsmonitor support to update-index
fsmonitor: add a test tool to dump the index extension
split-index: disable the fsmonitor extension when running the split
index test
fsmonitor: add test cases for fsmonitor extension
fsmonitor: add a sample integration script for Watchman
fsmonitor: add a performance test
Documentation/config.txt | 7 + Documentation/git-ls-files.txt | 7 +- Documentation/git-update-index.txt | 45 +++++ Documentation/githooks.txt | 28 +++ Documentation/technical/index-format.txt | 19 ++ Makefile | 3 + builtin/ls-files.c | 8 +- builtin/update-index.c | 38 +++- cache.h | 10 +- compat/bswap.h | 22 +++ config.c | 14 ++ config.h | 1 + diff-lib.c | 2 + dir.c | 27 ++- dir.h | 2 + entry.c | 2 + environment.c | 1 + fsmonitor.c | 253 ++++++++++++++++++++++++ fsmonitor.h | 66 +++++++ preload-index.c | 8 +- read-cache.c | 45 ++++- submodule.c | 2 +- t/helper/.gitignore | 1 + t/helper/test-drop-caches.c | 164 ++++++++++++++++ t/helper/test-dump-fsmonitor.c | 21 ++ t/perf/p7519-fsmonitor.sh | 184 +++++++++++++++++ t/t1700-split-index.sh | 1 + t/t7519-status-fsmonitor.sh | 304 +++++++++++++++++++++++++++++ t/t7519/fsmonitor-all | 24 +++ t/t7519/fsmonitor-none | 22 +++ t/t7519/fsmonitor-watchman | 140 +++++++++++++ templates/hooks--fsmonitor-watchman.sample | 122 ++++++++++++ unpack-trees.c | 2 + 33 files changed, 1572 insertions(+), 23 deletions(-) create mode 100644 fsmonitor.c create mode 100644 fsmonitor.h create mode 100644 t/helper/test-drop-caches.c create mode 100644 t/helper/test-dump-fsmonitor.c create mode 100755 t/perf/p7519-fsmonitor.sh create mode 100755 t/t7519-status-fsmonitor.sh create mode 100755 t/t7519/fsmonitor-all create mode 100755 t/t7519/fsmonitor-none create mode 100755 t/t7519/fsmonitor-watchman create mode 100755 templates/hooks--fsmonitor-watchman.sample
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 1/12 bswap: add 64 bit endianness helper get_be64
Add a new get_be64 macro to enable 64 bit endian conversions on memory that may or may not be aligned.
--- compat/bswap.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
compat/bswap.h +22 -0
@@ -158,7 +158,9 @@ static inline uint64_t git_bswap64(uint64_t x)#define get_be16 (p) ntohs(*(unsigned short *)(p))#define get_be32 (p) ntohl(*(unsigned int *)(p)) +#define get_be64 (p) ntohll(*(uint64_t *)(p))#define put_be32 (p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) +#define put_be64 (p, v) do { *(uint64_t *)(p) = htonll(v); } while (0)#else
@@ -178,6 +180,13 @@ static inline uint32_t get_be32(const void *ptr) (uint32_t )p[3 ] <<0 ; } +static inline uint64_t get_be64 (const void *ptr) +{ +const unsigned char *p = ptr; +return (uint64_t )get_be32 (p[0 ]) <<32 | + (uint64_t )get_be32 (p[4 ]) <<0 ; +} +static inline void put_be32 (void *ptr,uint32_t value) {unsigned char *p = ptr;
@@ -187,4 +196,17 @@ static inline void put_be32(void *ptr, uint32_t value) p[3] = value >> 0; } +static inline void put_be64 (void *ptr,uint64_t value) +{ +unsigned char *p = ptr; + p[0 ] = value >>56 ; + p[1 ] = value >>48 ; + p[2 ] = value >>40 ; + p[3 ] = value >>32 ; + p[4 ] = value >>24 ; + p[5 ] = value >>16 ; + p[6 ] = value >>8 ; + p[7 ] = value >>0 ; +} +#endif
Signature · 1 line
2.14.1.549.g6ff7ed0467
Re: [PATCH v8 01/12] bswap: add 64 bit endianness helper get_be64
Quoted 2 lines · Ben Peart
Add a new
get_be64macro to enable 64 bit endian conversions on memory that may or may not be aligned.
I needed this to compile and pass the tests with NO_UNALIGNED_LOADS.
Martin
compat/bswap.h +2 -2
@@ -183,8 +183,8 @@ static inline uint32_t get_be32(const void *ptr)static inline uint64_t get_be64 (const void *ptr) {const unsigned char *p = ptr; -return (uint64_t )get_be32 (p[0 ]) <<32 | - (uint64_t )get_be32 (p[4 ]) <<0 ; +return (uint64_t )get_be32 (p +0 ) <<32 | + (uint64_t )get_be32 (p +4 ) <<0 ; }static inline void put_be32 (void *ptr,uint32_t value)
Signature · 1 line
2.14.1.727.g9ddaf86
RE: [PATCH v8 01/12] bswap: add 64 bit endianness helper get_be64
Thanks,
Ben
Quoted 25 lines
-----Original Message-----
From: Martin Ågren [mailto:[email protected]]
Sent: Friday, September 22, 2017 7:37 PM
To: Ben Peart <[email protected]>
Cc: David Turner <[email protected]>; Ævar Arnfjörð Bjarmason <[email protected]>; Christian Couder <[email protected]>; [email protected]; Junio C Hamano <[email protected]>; Johannes Schindelin <[email protected]>; Nguyễn Thái Ngọc Duy <[email protected]>; Jeff King <[email protected]>
Subject: Re: [PATCH v8 01/12] bswap: add 64 bit endianness helperget_be64Quoted 2 lines · Ben Peart
Add a new
get_be64macro to enable 64 bit endian conversions on memory that may or may not be aligned.I needed this to compile and pass the tests with
NO_UNALIGNED_LOADS.Martin
compat/bswap.h index 6b22c4621..9dc79bdf5 renamed from compat/bswap.h +0
-0100644
compat/bswap.h static inline uint32_t get_be32(const void *ptr) static · line 186
Quoted 6 lines
compat/bswap.h +2
-2@@ -183,8 +183,8 @@ static inline uint32_t get_be32(const void *ptr) staticinline uint64_t get_be64 (const void *ptr) {const unsigned char *p = ptr; -return (uint64_t )get_be32 (p[0 ]) <<32 | - (uint64_t )get_be32 (p[4 ]) <<0 ; +return (uint64_t )get_be32 (p +0 ) <<32 | + (uint64_t )get_be32 (p +4 ) <<0 ;
This is surprising. Every other function in the file uses the p[x] syntax. Just for consistency, is there a way to stick to that syntax but still make it work correctly? Is there a typecast that can make it work?
Quoted 5 lines
compat/bswap.h +0
-1}static inline void put_be32 (void *ptr,uint32_t value) --2.14.1.727.g9ddaf86
Re: [PATCH v8 01/12] bswap: add 64 bit endianness helper get_be64
Quoted 7 lines · Ben Peart
Quoted 7 lines
compat/bswap.h index 6b22c4621..9dc79bdf5 renamed from compat/bswap.h +0
-0100644
compat/bswap.h +0
-0@@ -183,8 +183,8 @@ static inline uint32_t get_be32(const void *ptr) staticinline uint64_t get_be64 (const void *ptr) {const unsigned char *p = ptr;
Quoted 8 lines
compat/bswap.h static inline uint32_t get_be32(const void *ptr) · line 187
Quoted 4 lines
compat/bswap.h +2
-2-return (uint64_t )get_be32 (p[0 ]) <<32 | - (uint64_t )get_be32 (p[4 ]) <<0 ; +return (uint64_t )get_be32 (p +0 ) <<32 | + (uint64_t )get_be32 (p +4 ) <<0 ;This is surprising. Every other function in the file uses the p[x] syntax. Just for consistency, is there a way to stick to that syntax but still make it work correctly? Is there a typecast that can make it work?
The other ones are accessing the byte values directly, but since you are building on get_be32 here, you have to pass the pointer. So:
return (uint64_t)get_be32(&p[0]) << 32 |
(uint64_t)get_be32(&p[4]) << 0;would work. Or of course you could just spell it out like the others:
return (uint64_t)p[0] << 56 |
(uint64_t)p[1] << 48 |
(uint64_t)p[2] << 40 |
(uint64_t)p[3] << 32 |
(uint64_t)p[4] << 24 |
(uint64_t)p[5] << 16 |
(uint64_t)p[6] << 8 |
(uint64_t)p[7] << 0;I suspect compilers would end up with the same output either way (on x86, "gcc -O2" actually turns the whole thing into a bswap instruction).
-Peff
Re: [PATCH v8 01/12] bswap: add 64 bit endianness helper get_be64
In reply to Ben PeartQuoted 11 lines · Ben Peart
compat/bswap.h static inline uint32_t get_be32(const void *ptr) static · line 186
Quoted 7 lines
compat/bswap.h +2
-2@@ -183,8 +183,8 @@ static inline uint32_t get_be32(const void *ptr) staticinline uint64_t get_be64 (const void *ptr) {const unsigned char *p = ptr; -return (uint64_t )get_be32 (p[0 ]) <<32 | - (uint64_t )get_be32 (p[4 ]) <<0 ; +return (uint64_t )get_be32 (p +0 ) <<32 | + (uint64_t )get_be32 (p +4 ) <<0 ;This is surprising. Every other function in the file uses the p[x] syntax. Just for consistency, is there a way to stick to that syntax but still make it work correctly? Is there a typecast that can make it work?
I'll do "get_be32(&p[0])" etc. while queueing for now.
Thanks, both of you.
PATCH v8 2/12 preload-index: add override to enable testing preload-index
In reply to Ben PeartBy default, the preload index code path doesn't run unless there is a minimum of 1000 files. To enable running the test suite and having it execute the preload-index path, add an environment variable (GIT_FORCE_PRELOAD_TEST) which will override that minimum and set it to 2.
This enables you run existing tests and have the core.preloadindex code path execute as long as the test has at least 2 files by setting GIT_FORCE_PRELOAD_TEXT=1 before running the test.
--- preload-index.c | 2 ++ 1 file changed, 2 insertions(+)
preload-index.c +2 -0
@@ -79,6 +79,8 @@ static void preload_index(struct index_state *index,return ; threads = index->cache_nr /THREAD_COST ; +if ((index->cache_nr >1 ) && (threads <2 ) &&getenv ("GIT_FORCE_PRELOAD_TEST" )) + threads =2 ;if (threads <2 )return ;if (threads >MAX_PARALLEL )
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 3/12 update-index: add a new --force-write-index option
In reply to Ben PeartAt times, it makes sense to avoid the cost of writing out the index when the only changes can easily be recomputed on demand. This causes problems when trying to write test cases to verify that state as they can't guarantee the state has been persisted to disk.
Add a new option (--force-write-index) to update-index that will ensure the index is written out even if the cache_changed flag is not set.
--- builtin/update-index.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
builtin/update-index.c +4 -1
@@ -915,6 +915,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)struct refresh_params refresh_args = {0 , &has_errors};int lock_error =0 ;int split_index =-1 ; +int force_write =0 ;struct lock_file *lock_file;struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;
@@ -1006,6 +1007,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("test if the filesystem supports untracked cache"), UC_TEST),OPT_SET_INT (0 ,"force-untracked-cache" , &untracked_cache,N_ ("enable untracked cache without testing the filesystem" ),UC_FORCE ), +OPT_SET_INT (0 ,"force-write-index" , &force_write, +N_ ("write out the index even if is not flagged as changed" ),1 ), OPT_END() };
@@ -1147,7 +1150,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)die ("BUG: bad untracked_cache value: %d" , untracked_cache); } -if (active_cache_changed) { +if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )exit (128 );
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 4/12 fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
In reply to Ben PeartWhen the index is read from disk, the fsmonitor index extension is used to flag the last known potentially dirty index entries. The registered core.fsmonitor command is called with the time the index was last updated and returns the list of files changed since that time. This list is used to flag any additional dirty cache entries and untracked cache directories.
We can then use this valid state to speed up preload_index(), ie_match_stat(), and refresh_cache_ent() as they do not need to lstat() files to detect potential changes for those entries marked CE_FSMONITOR_VALID.
In addition, if the untracked cache is turned on valid_cached_dir() can skip checking directories for new or changed files as fsmonitor will invalidate the cache only for those directories that have been identified as having potential changes.
To keep the CE_FSMONITOR_VALID state accurate during git operations; when git updates a cache entry to match the current state on disk, it will now set the CE_FSMONITOR_VALID bit.
Inversely, anytime git changes a cache entry, the CE_FSMONITOR_VALID bit is cleared and the corresponding untracked cache directory is marked invalid.
--- Makefile | 1 + builtin/update-index.c | 2 + cache.h | 10 +- config.c | 14 +++ config.h | 1 + diff-lib.c | 2 + dir.c | 27 ++++-- dir.h | 2 + entry.c | 2 + environment.c | 1 + fsmonitor.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++++ fsmonitor.h | 66 +++++++++++++ preload-index.c | 6 +- read-cache.c | 45 ++++++++- submodule.c | 2 +- unpack-trees.c | 2 + 16 files changed, 417 insertions(+), 19 deletions(-) create mode 100644 fsmonitor.c create mode 100644 fsmonitor.h
Makefile +1 -0
@@ -786,6 +786,7 @@ LIB_OBJS += ewah/ewah_rlw.o LIB_OBJS += exec_cmd.o LIB_OBJS += fetch-pack.o LIB_OBJS += fsck.o +LIB_OBJS += fsmonitor.o LIB_OBJS += gettext.o LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o
builtin/update-index.c +2 -0
@@ -16,6 +16,7 @@#include "pathspec.h" #include "dir.h" #include "split-index.h" +#include "fsmonitor.h" /* * Default to not allowing changes to the list of files. The
@@ -233,6 +234,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)else active_cache[pos]->ce_flags &= ~flag; active_cache[pos]->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (&the_index, active_cache[pos]);cache_tree_invalidate_path (&the_index, path); active_cache_changed |=CE_ENTRY_CHANGED ;return 0 ;
cache.h +8 -2
@@ -203,6 +203,7 @@ struct cache_entry {#define CE_ADDED (1 << 19)#define CE_HASHED (1 << 20) +#define CE_FSMONITOR_VALID (1 << 21)#define CE_WT_REMOVE (1 << 22)/* remove in work directory */ #define CE_CONFLICTED (1 << 23)
@@ -326,6 +327,7 @@ static inline unsigned int canon_mode(unsigned int mode)#define CACHE_TREE_CHANGED (1 << 5)#define SPLIT_INDEX_ORDERED (1 << 6)#define UNTRACKED_CHANGED (1 << 7) +#define FSMONITOR_CHANGED (1 << 8)struct split_index ;struct untracked_cache ;
@@ -344,6 +346,7 @@ struct index_state {struct hashmap dir_hash;unsigned char sha1[20 ];struct untracked_cache *untracked; +uint64_t fsmonitor_last_update; };extern struct index_state the_index;
@@ -679,8 +682,10 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,#define CE_MATCH_IGNORE_MISSING 0x08/* enable stat refresh */ #define CE_MATCH_REFRESH 0x10 -extern int ie_match_stat (const struct index_state *,const struct cache_entry *,struct stat *,unsigned int ); -extern int ie_modified (const struct index_state *,const struct cache_entry *,struct stat *,unsigned int ); +/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */ +#define CE_MATCH_IGNORE_FSMONITOR 0X20 +extern int ie_match_stat (struct index_state *,const struct cache_entry *,struct stat *,unsigned int ); +extern int ie_modified (struct index_state *,const struct cache_entry *,struct stat *,unsigned int );#define HASH_WRITE_OBJECT 1#define HASH_FORMAT_CHECK 2
@@ -773,6 +778,7 @@ extern int core_apply_sparse_checkout;extern int precomposed_unicode;extern int protect_hfs;extern int protect_ntfs; +extern const char *core_fsmonitor;/* * Include broken refs in all ref iterations, which will
config.c +14 -0
@@ -2165,6 +2165,20 @@ int git_config_get_max_percent_split_change(void)return -1 ;/* default value */ } +int git_config_get_fsmonitor (void ) +{ +if (git_config_get_pathname ("core.fsmonitor" , &core_fsmonitor)) + core_fsmonitor =getenv ("GIT_FSMONITOR_TEST" ); + +if (core_fsmonitor && !*core_fsmonitor) + core_fsmonitor =NULL ; + +if (core_fsmonitor) +return 1 ; + +return 0 ; +} +NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr) {
config.h +1 -0
@@ -211,6 +211,7 @@ extern int git_config_get_pathname(const char *key, const char **dest);extern int git_config_get_untracked_cache (void );extern int git_config_get_split_index (void );extern int git_config_get_max_percent_split_change (void ); +extern int git_config_get_fsmonitor (void );/* This dies if the configured or default date is in the future */ extern int git_config_get_expiry (const char *key,const char **output);
diff-lib.c +2 -0
@@ -12,6 +12,7 @@#include "refs.h" #include "submodule.h" #include "dir.h" +#include "fsmonitor.h" /* * diff-files
@@ -228,6 +229,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)if (!changed && !dirty_submodule) {ce_mark_uptodate (ce); +mark_fsmonitor_valid (ce);if (!DIFF_OPT_TST (&revs->diffopt,FIND_COPIES_HARDER ))continue ; }
dir.c +17 -10
@@ -18,6 +18,7 @@#include "utf8.h" #include "varint.h" #include "ewah/ewok.h" +#include "fsmonitor.h" /* * Tells read_directory_recursive how a file or directory should be treated.
@@ -1688,17 +1689,23 @@ static int valid_cached_dir(struct dir_struct *dir,if (!untracked)return 0 ; -if (stat (path->len ? path->buf :"." , &st)) { -invalidate_directory (dir->untracked, untracked); -memset (&untracked->stat_data,0 ,sizeof (untracked->stat_data)); -return 0 ; - } -if (!untracked->valid || -match_stat_data_racy (istate, &untracked->stat_data, &st)) { -if (untracked->valid) +/* +* With fsmonitor, we can trust the untracked cache's valid field. +*/ +refresh_fsmonitor (istate); +if (!(dir->untracked->use_fsmonitor && untracked->valid)) { +if (stat (path->len ? path->buf :"." , &st)) {invalidate_directory (dir->untracked, untracked); -fill_stat_data (&untracked->stat_data, &st); -return 0 ; +memset (&untracked->stat_data,0 ,sizeof (untracked->stat_data)); +return 0 ; + } +if (!untracked->valid || +match_stat_data_racy (istate, &untracked->stat_data, &st)) { +if (untracked->valid) +invalidate_directory (dir->untracked, untracked); +fill_stat_data (&untracked->stat_data, &st); +return 0 ; + } }if (untracked->check_only != !!check_only) {
dir.h +2 -0
@@ -139,6 +139,8 @@ struct untracked_cache {int gitignore_invalidated;int dir_invalidated;int dir_opened; +/* fsmonitor invalidation data */ + unsigned int use_fsmonitor : 1; };struct dir_struct {
entry.c +2 -0
@@ -4,6 +4,7 @@#include "streaming.h" #include "submodule.h" #include "progress.h" +#include "fsmonitor.h" static void create_directories (const char *path,int path_len,const struct checkout *state)
@@ -357,6 +358,7 @@ static int write_entry(struct cache_entry *ce,lstat (ce->name, &st);fill_stat_cache_info (ce, &st); ce->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (state->istate, ce); state->istate->cache_changed |=CE_ENTRY_CHANGED ; }return 0 ;
environment.c +1 -0
@@ -76,6 +76,7 @@ int protect_hfs = PROTECT_HFS_DEFAULT;#define PROTECT_NTFS_DEFAULT 0#endif int protect_ntfs =PROTECT_NTFS_DEFAULT ; +const char *core_fsmonitor;/* * The character that begins a commented line in user-editable file
fsmonitor.c new file +253 -0
253 lines not on this page · raw message
fsmonitor.h new file +66 -0
66 lines not on this page · raw message
preload-index.c +5 -1
@@ -4,6 +4,7 @@#include "cache.h" #include "pathspec.h" #include "dir.h" +#include "fsmonitor.h" #ifdef NO_PTHREADS static void preload_index(struct index_state *index,
@@ -55,15 +56,18 @@ static void *preload_thread(void *_data)continue ;if (ce_skip_worktree (ce))continue ; +if (ce->ce_flags &CE_FSMONITOR_VALID ) +continue ;if (!ce_path_match (ce, &p->pathspec,NULL ))continue ;if (threaded_has_symlink_leading_path (&cache, ce->name,ce_namelen (ce)))continue ;if (lstat (ce->name, &st))continue ; -if (ie_match_stat (index, ce, &st,CE_MATCH_RACY_IS_DIRTY )) +if (ie_match_stat (index, ce, &st,CE_MATCH_RACY_IS_DIRTY |CE_MATCH_IGNORE_FSMONITOR ))continue ;ce_mark_uptodate (ce); +mark_fsmonitor_valid (ce); }while (--nr >0 );cache_def_clear (&cache);return NULL ;
read-cache.c +40 -5
152 lines not on this page · raw message
submodule.c +1 -1
@@ -62,7 +62,7 @@ int is_staging_gitmodules_ok(const struct index_state *istate)if ((pos >=0 ) && (pos < istate->cache_nr)) {struct stat st;if (lstat (GITMODULES_FILE , &st) ==0 && -ce_match_stat (istate->cache[pos], &st,0 ) &DATA_CHANGED ) +ce_match_stat (istate->cache[pos], &st,CE_MATCH_IGNORE_FSMONITOR ) &DATA_CHANGED )return 0 ; }
unpack-trees.c +2 -0
@@ -14,6 +14,7 @@#include "dir.h" #include "submodule.h" #include "submodule-config.h" +#include "fsmonitor.h" /* * Error messages expected by scripts out of plumbing commands such as
@@ -408,6 +409,7 @@ static int apply_sparse_checkout(struct index_state *istate, ce->ce_flags &= ~CE_SKIP_WORKTREE ;if (was_skip_worktree !=ce_skip_worktree (ce)) { ce->ce_flags |=CE_UPDATE_IN_BASE ; +mark_fsmonitor_invalid (istate, ce); istate->cache_changed |=CE_ENTRY_CHANGED ; }
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 5/12 fsmonitor: add documentation for the fsmonitor extension.
In reply to Ben PeartThis includes the core.fsmonitor setting, the fsmonitor integration hook, and the fsmonitor index extension.
Also add documentation for the new fsmonitor options to ls-files and update-index.
--- Documentation/config.txt | 7 +++++ Documentation/git-ls-files.txt | 7 ++++- Documentation/git-update-index.txt | 45 ++++++++++++++++++++++++++++++++ Documentation/githooks.txt | 28 ++++++++++++++++++++ Documentation/technical/index-format.txt | 19 ++++++++++++++ 5 files changed, 105 insertions(+), 1 deletion(-)
Documentation/config.txt +7 -0
@@ -413,6 +413,13 @@ core.protectNTFS:: 8.3 "short" names. Defaults to `true` on Windows, and `false` elsewhere. +core.fsmonitor:: + If set, the value of this variable is used as a command which + will identify all files that may have changed since the + requested date/time. This information is used to speed up git by + avoiding unnecessary processing of files that have not changed. + See the "fsmonitor-watchman" section of linkgit:githooks[5]. + core.trustctime:: If false, the ctime differences between the index and the working tree are ignored; useful when the inode change time
Documentation/git-ls-files.txt +6 -1
@@ -9,7 +9,7 @@ git-ls-files - Show information about files in the index and the working tree SYNOPSIS -------- [verse] -'git ls-files' [-z] [-t] [-v] +'git ls-files' [-z] [-t] [-v] [-f] (--[cached|deleted|others|ignored|stage|unmerged|killed|modified])* (-[c|d|o|i|s|u|k|m])* [--eol]
@@ -133,6 +133,11 @@ a space) at the start of each line: that are marked as 'assume unchanged' (see linkgit:git-update-index[1]). +-f:: + Similar to `-t`, but use lowercase letters for files + that are marked as 'fsmonitor valid' (see + linkgit:git-update-index[1]). + --full-name:: When run from a subdirectory, the command usually outputs paths relative to the current directory. This
Documentation/git-update-index.txt +45 -0
72 lines not on this page · raw message
Documentation/githooks.txt +28 -0
@@ -455,6 +455,34 @@ the name of the file that holds the e-mail to be sent. Exiting with a non-zero status causes 'git send-email' to abort before sending any e-mails. +fsmonitor-watchman +~~~~~~~~~~~~~~~~~~ + +This hook is invoked when the configuration option core.fsmonitor is +set to .git/hooks/fsmonitor-watchman. It takes two arguments, a version +(currently 1) and the time in elapsed nanoseconds since midnight, +January 1, 1970. + +The hook should output to stdout the list of all files in the working +directory that may have changed since the requested time. The logic +should be inclusive so that it does not miss any potential changes. +The paths should be relative to the root of the working directory +and be separated by a single NUL. + +It is OK to include files which have not actually changed. All changes +including newly-created and deleted files should be included. When +files are renamed, both the old and the new name should be included. + +Git will limit what files it checks for changes as well as which +directories are checked for untracked files based on the path names +given. + +An optimized way to tell git "all files have changed" is to return +the filename '/'. + +The exit status determines whether git will use the data from the +hook to limit its search. On error, it will fall back to verifying +all files and folders. GIT ---
Documentation/technical/index-format.txt +19 -0
@@ -295,3 +295,22 @@ The remaining data of each directory block is grouped by type: in the previous ewah bitmap. - One NUL. + +== File System Monitor cache + + The file system monitor cache tracks files for which the core.fsmonitor + hook has told us about changes. The signature for this extension is + { 'F', 'S', 'M', 'N' }. + + The extension starts with + + - 32-bit version number: the current supported version is 1. + + - 64-bit time: the extension data reflects all changes through the given + time which is stored as the nanoseconds elapsed since midnight, + January 1, 1970. + + - 32-bit bitmap size: the size of the CE_FSMONITOR_VALID bitmap. + + - An ewah bitmap, the n-th bit indicates whether the n-th index entry + is not CE_FSMONITOR_VALID.
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 6/12 ls-files: Add support in ls-files to display the fsmonitor valid bit
In reply to Ben PeartAdd a new command line option (-f) to ls-files to have it use lowercase letters for 'fsmonitor valid' files
--- builtin/ls-files.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
builtin/ls-files.c +6 -2
@@ -31,6 +31,7 @@ static int show_resolve_undo;static int show_modified;static int show_killed;static int show_valid_bit; +static int show_fsmonitor_bit;static int line_terminator ='\n' ;static int debug_mode;static int show_eol;
@@ -86,7 +87,8 @@ static const char *get_tag(const struct cache_entry *ce, const char *tag) {static char alttag[4 ]; -if (tag && *tag && show_valid_bit && (ce->ce_flags &CE_VALID )) { +if (tag && *tag && ((show_valid_bit && (ce->ce_flags &CE_VALID )) || + (show_fsmonitor_bit && (ce->ce_flags &CE_FSMONITOR_VALID )))) {memcpy (alttag, tag,3 );if (isalpha (tag[0 ])) {
@@ -515,6 +517,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("identify the file status with tags")),OPT_BOOL ('v' ,NULL , &show_valid_bit,N_ ("use lowercase letters for 'assume unchanged' files" )), +OPT_BOOL ('f' ,NULL , &show_fsmonitor_bit, +N_ ("use lowercase letters for 'fsmonitor clean' files" )),OPT_BOOL ('c' ,"cached" , &show_cached,N_ ("show cached files in the output (default)" )), OPT_BOOL('d', "deleted", &show_deleted,
@@ -584,7 +588,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)for (i =0 ; i < exclude_list.nr; i++) {add_exclude (exclude_list.items[i].string,"" ,0 , el, --exclude_args); } -if (show_tag || show_valid_bit) { +if (show_tag || show_valid_bit || show_fsmonitor_bit) { tag_cached ="H " ; tag_unmerged ="M " ; tag_removed ="R " ;
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 7/12 update-index: add fsmonitor support to update-index
In reply to Ben PeartAdd support in update-index to manually add/remove the fsmonitor extension via --[no-]fsmonitor flags.
Add support in update-index to manually set/clear the fsmonitor valid bit via --[no-]fsmonitor-valid flags.
--- builtin/update-index.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)
builtin/update-index.c +32 -1
@@ -33,6 +33,7 @@ static int force_remove;static int verbose;static int mark_valid_only;static int mark_skip_worktree_only; +static int mark_fsmonitor_only;#define MARK_FLAG 1#define UNMARK_FLAG 2static struct strbuf mtime_dir =STRBUF_INIT ;
@@ -229,12 +230,12 @@ static int mark_ce_flags(const char *path, int flag, int mark)int namelen =strlen (path);int pos =cache_name_pos (path, namelen);if (0 <= pos) { +mark_fsmonitor_invalid (&the_index, active_cache[pos]);if (mark) active_cache[pos]->ce_flags |= flag;else active_cache[pos]->ce_flags &= ~flag; active_cache[pos]->ce_flags |=CE_UPDATE_IN_BASE ; -mark_fsmonitor_invalid (&the_index, active_cache[pos]);cache_tree_invalidate_path (&the_index, path); active_cache_changed |=CE_ENTRY_CHANGED ;return 0 ;
@@ -460,6 +461,11 @@ static void update_one(const char *path)die ("Unable to mark file %s" , path);return ; } +if (mark_fsmonitor_only) { +if (mark_ce_flags (path,CE_FSMONITOR_VALID , mark_fsmonitor_only ==MARK_FLAG )) +die ("Unable to mark file %s" , path); +return ; + }if (force_remove) {if (remove_file_from_cache (path))
@@ -918,6 +924,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)int lock_error =0 ;int split_index =-1 ;int force_write =0 ; +int fsmonitor =-1 ;struct lock_file *lock_file;struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;
@@ -1011,6 +1018,14 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) N_("enable untracked cache without testing the filesystem"), UC_FORCE),OPT_SET_INT (0 ,"force-write-index" , &force_write,N_ ("write out the index even if is not flagged as changed" ),1 ), +OPT_BOOL (0 ,"fsmonitor" , &fsmonitor, +N_ ("enable or disable file system monitor" )), + {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL, +N_ ("mark files as fsmonitor valid" ), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, + {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL, +N_ ("clear fsmonitor valid bit" ), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},OPT_END () };
@@ -1152,6 +1167,22 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)die ("BUG: bad untracked_cache value: %d" , untracked_cache); } +if (fsmonitor >0 ) { +if (git_config_get_fsmonitor () ==0 ) +warning (_ ("core.fsmonitor is unset; " +"set it if you really want to " +"enable fsmonitor" )); +add_fsmonitor (&the_index); +report (_ ("fsmonitor enabled" )); + }else if (!fsmonitor) { +if (git_config_get_fsmonitor () ==1 ) +warning (_ ("core.fsmonitor is set; " +"remove it if you really want to " +"disable fsmonitor" )); +remove_fsmonitor (&the_index); +report (_ ("fsmonitor disabled" )); + } +if (active_cache_changed || force_write) {if (newfd <0 ) {if (refresh_args.flags &REFRESH_QUIET )
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 8/12 fsmonitor: add a test tool to dump the index extension
In reply to Ben PeartAdd a test utility (test-dump-fsmonitor) that will dump the fsmonitor index extension.
--- Makefile | 1 + t/helper/test-dump-fsmonitor.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 t/helper/test-dump-fsmonitor.c
Makefile +1 -0
@@ -639,6 +639,7 @@ TEST_PROGRAMS_NEED_X += test-config TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta TEST_PROGRAMS_NEED_X += test-dump-cache-tree +TEST_PROGRAMS_NEED_X += test-dump-fsmonitor TEST_PROGRAMS_NEED_X += test-dump-split-index TEST_PROGRAMS_NEED_X += test-dump-untracked-cache TEST_PROGRAMS_NEED_X += test-fake-ssh
t/helper/test-dump-fsmonitor.c new file +21 -0
@@ -0,0 +1,21 @@ +#include "cache.h" + +int cmd_main (int ac,const char **av) +{ +struct index_state *istate = &the_index; +int i; + +setup_git_directory (); +if (do_read_index (istate,get_index_file (),0 ) <0 ) +die ("unable to read index file" ); +if (!istate->fsmonitor_last_update) { +printf ("no fsmonitor\n" ); +return 0 ; + } +printf ("fsmonitor last update %" PRIuMAX"\n" , (uintmax_t )istate->fsmonitor_last_update); + +for (i =0 ; i < istate->cache_nr; i++) +printf ((istate->cache[i]->ce_flags &CE_FSMONITOR_VALID ) ?"+" :"-" ); + +return 0 ; +}
Signature · 1 line
2.14.1.549.g6ff7ed0467
Re: [PATCH v8 08/12] fsmonitor: add a test tool to dump the index extension
Quoted 9 lines · Ben Peart
Add a test utility (test-dump-fsmonitor) that will dump the fsmonitor index extension.
Signed-off-by Ben Peart <[email protected]>--- Makefile | 1 + t/helper/test-dump-fsmonitor.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 t/helper/test-dump-fsmonitor.c
You forget to add the new binary to .gitignore. (In patch 12/12, you introduce test-drop-caches, which you _do_ add to .gitignore.)
Martin
t/helper/.gitignore +1 -0
@@ -6,6 +6,7 @@ /test-delta /test-drop-caches /test-dump-cache-tree +/test-dump-fsmonitor /test-dump-split-index /test-dump-untracked-cache /test-fake-ssh
Signature · 1 line
2.14.1.727.g9ddaf86
RE: [PATCH v8 08/12] fsmonitor: add a test tool to dump the index extension
Quoted 24 lines
-----Original Message-----
From: Martin Ågren [mailto:[email protected]]
Sent: Friday, September 22, 2017 7:37 PM
To: Ben Peart <[email protected]>
Cc: David Turner <[email protected]>; Ævar Arnfjörð Bjarmason <[email protected]>; Christian Couder <[email protected]>; [email protected]; Junio C Hamano <[email protected]>; Johannes Schindelin <[email protected]>; Nguyễn Thái Ngọc Duy <[email protected]>; Jeff King <[email protected]>
Subject: Re: [PATCH v8 08/12] fsmonitor: add a test tool to dump the index extensionQuoted 9 lines · Ben Peart
Add a test utility (test-dump-fsmonitor) that will dump the fsmonitor index extension.
Signed-off-by Ben Peart <[email protected]>--- Makefile | 1 + t/helper/test-dump-fsmonitor.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 t/helper/test-dump-fsmonitor.c
Quoted 3 lines
You forget to add the new binary to .gitignore. (In patch 12/12, you introduce test-drop-caches, which you _do_ add to .gitignore.)
Oops. Thanks! Hopefully Junio can squash this in...
Quoted 16 lines
Martin
t/helper/.gitignore index renamed from t/helper/.gitignore +0
-0
6f07de62d..0fe2e0440100644t/helper/.gitignore +1
-1@@ -6,6 +6,7 @@ /test-delta /test-drop-caches /test-dump-cache-tree +/test-dump-fsmonitor /test-dump-split-index /test-dump-untracked-cache /test-fake-ssh --2.14.1.727.g9ddaf86
Re: [PATCH v8 08/12] fsmonitor: add a test tool to dump the index extension
Quoted 5 lines · Ben Peart
Quoted 3 lines
You forget to add the new binary to .gitignore. (In patch 12/12, you introduce test-drop-caches, which you _do_ add to .gitignore.)
Oops. Thanks! Hopefully Junio can squash this in...
OK, will do.
PATCH v8 9/12 split-index: disable the fsmonitor extension when running the split index test
In reply to Ben PeartThe split index test t1700-split-index.sh has hard coded SHA values for the index. Currently it supports index V4 and V3 but assumes there are no index extensions loaded.
When manually forcing the fsmonitor extension to be turned on when running the test suite, the SHA values no longer match which causes the test to fail.
The potential matrix of index extensions and index versions can is quite large so instead temporarily disable the extension before attempting to run the test until the underlying problem of hard coded SHA values is fixed.
--- t/t1700-split-index.sh | 1 + 1 file changed, 1 insertion(+)
t/t1700-split-index.sh +1 -0
@@ -6,6 +6,7 @@ test_description='split index mode tests' # We need total control of index splitting here sane_unset GIT_TEST_SPLIT_INDEX +sane_unset GIT_FSMONITOR_TEST test_expect_success 'enable split index' ' git config splitIndex.maxPercentChange 100 &&
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 10/12 fsmonitor: add test cases for fsmonitor extension
In reply to Ben PeartTest the ability to add/remove the fsmonitor index extension via update-index.
Test that dirty files returned from the integration script are properly represented in the index extension and verify that ls-files correctly reports their state.
Test that ensure status results are correct when using the new fsmonitor extension. Test untracked, modified, and new files by ensuring the results are identical to when not using the extension.
Test that if the fsmonitor extension doesn't tell git about a change, it doesn't discover it on its own. This ensures git is honoring the extension and that we get the performance benefits desired.
Three test integration scripts are provided:
fsmonitor-all - marks all files as dirty fsmonitor-none - marks no files as dirty fsmonitor-watchman - integrates with Watchman with debug logging
To run tests in the test suite while utilizing fsmonitor:
First copy t/t7519/fsmonitor-all to a location in your path and then set GIT_FORCE_PRELOAD_TEST=true and GIT_FSMONITOR_TEST=fsmonitor-all and run your tests.
Note: currently when using the test script fsmonitor-watchman on Windows, many tests fail due to a reported but not yet fixed bug in Watchman where it holds on to handles for directories and files which prevents the test directory from being cleaned up properly.
--- t/t7519-status-fsmonitor.sh | 304 ++++++++++++++++++++++++++++++++++++++++++++ t/t7519/fsmonitor-all | 24 ++++ t/t7519/fsmonitor-none | 22 ++++ t/t7519/fsmonitor-watchman | 140 ++++++++++++++++++++ 4 files changed, 490 insertions(+) create mode 100755 t/t7519-status-fsmonitor.sh create mode 100755 t/t7519/fsmonitor-all create mode 100755 t/t7519/fsmonitor-none create mode 100755 t/t7519/fsmonitor-watchman
t/t7519-status-fsmonitor.sh new file +304 -0
@@ -0,0 +1,304 @@ +#!/bin/sh + +test_description='git status with file system watcher' + +. ./test-lib.sh + +# +# To run the entire git test suite using fsmonitor: +# +# copy t/t7519/fsmonitor-all to a location in your path and then set +# GIT_FSMONITOR_TEST=fsmonitor-all and run your tests. +# + +# Note, after "git reset --hard HEAD" no extensions exist other than 'TREE' +# "git update-index --fsmonitor" can be used to get the extension written +# before testing the results. + +clean_repo () { + git reset --hard HEAD && + git clean -fd +} + +dirty_repo () { + : >untracked && + : >dir1/untracked && + : >dir2/untracked && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + echo 4 >new && + echo 5 >dir1/new && + echo 6 >dir2/new +} + +write_integration_script () { + write_script .git/hooks/fsmonitor-test<<-\EOF + if test "$#" -ne 2 + then + echo "$0: exactly 2 arguments expected" + exit 2 + fi + if test "$1" != 1 + then + echo "Unsupported core.fsmonitor hook version." >&2 + exit 1 + fi + printf "untracked\0" + printf "dir1/untracked\0" + printf "dir2/untracked\0" + printf "modified\0" + printf "dir1/modified\0" + printf "dir2/modified\0" + printf "new\0" + printf "dir1/new\0" + printf "dir2/new\0" + EOF +} + +test_lazy_prereq UNTRACKED_CACHE ' + { git update-index --test-untracked-cache; ret=$?; } && + test $ret -ne 1 +' + +test_expect_success 'setup' ' + mkdir -p .git/hooks && + : >tracked && + : >modified && + mkdir dir1 && + : >dir1/tracked && + : >dir1/modified && + mkdir dir2 && + : >dir2/tracked && + : >dir2/modified && + git -c core.fsmonitor= add . && + git -c core.fsmonitor= commit -m initial && + git config core.fsmonitor .git/hooks/fsmonitor-test && + cat >.gitignore <<-\EOF + .gitignore + expect* + actual* + marker* + EOF +' + +# test that the fsmonitor extension is off by default +test_expect_success 'fsmonitor extension is off by default' ' + test-dump-fsmonitor >actual && + grep "^no fsmonitor" actual +' + +# test that "update-index --fsmonitor" adds the fsmonitor extension +test_expect_success 'update-index --fsmonitor" adds the fsmonitor extension' ' + git update-index --fsmonitor && + test-dump-fsmonitor >actual && + grep "^fsmonitor last update" actual +' + +# test that "update-index --no-fsmonitor" removes the fsmonitor extension +test_expect_success 'update-index --no-fsmonitor" removes the fsmonitor extension' ' + git update-index --no-fsmonitor && + test-dump-fsmonitor >actual && + grep "^no fsmonitor" actual +' + +cat >expect <<EOF && +h dir1/modified +H dir1/tracked +h dir2/modified +H dir2/tracked +h modified +H tracked +EOF + +# test that "update-index --fsmonitor-valid" sets the fsmonitor valid bit +test_expect_success 'update-index --fsmonitor-valid" sets the fsmonitor valid bit' ' + git update-index --fsmonitor && + git update-index --fsmonitor-valid dir1/modified && + git update-index --fsmonitor-valid dir2/modified && + git update-index --fsmonitor-valid modified && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +H dir1/tracked +H dir2/modified +H dir2/tracked +H modified +H tracked +EOF + +# test that "update-index --no-fsmonitor-valid" clears the fsmonitor valid bit +test_expect_success 'update-index --no-fsmonitor-valid" clears the fsmonitor valid bit' ' + git update-index --no-fsmonitor-valid dir1/modified && + git update-index --no-fsmonitor-valid dir2/modified && + git update-index --no-fsmonitor-valid modified && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +H dir1/tracked +H dir2/modified +H dir2/tracked +H modified +H tracked +EOF + +# test that all files returned by the script get flagged as invalid +test_expect_success 'all files returned by integration script get flagged as invalid' ' + write_integration_script && + dirty_repo && + git update-index --fsmonitor && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +h dir1/new +H dir1/tracked +H dir2/modified +h dir2/new +H dir2/tracked +H modified +h new +H tracked +EOF + +# test that newly added files are marked valid +test_expect_success 'newly added files are marked valid' ' + git add new && + git add dir1/new && + git add dir2/new && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +h dir1/new +h dir1/tracked +H dir2/modified +h dir2/new +h dir2/tracked +H modified +h new +h tracked +EOF + +# test that all unmodified files get marked valid +test_expect_success 'all unmodified files get marked valid' ' + # modified files result in update-index returning 1 + test_must_fail git update-index --refresh --force-write-index && + git ls-files -f >actual && + test_cmp expect actual +' + +cat >expect <<EOF && +H dir1/modified +h dir1/tracked +h dir2/modified +h dir2/tracked +h modified +h tracked +EOF + +# test that *only* files returned by the integration script get flagged as invalid +test_expect_success '*only* files returned by the integration script get flagged as invalid' ' + write_script .git/hooks/fsmonitor-test<<-\EOF && + printf "dir1/modified\0" + EOF + clean_repo && + git update-index --refresh --force-write-index && + echo 1 >modified && + echo 2 >dir1/modified && + echo 3 >dir2/modified && + test_must_fail git update-index --refresh --force-write-index && + git ls-files -f >actual && + test_cmp expect actual +' + +# Ensure commands that call refresh_index() to move the index back in time +# properly invalidate the fsmonitor cache +test_expect_success 'refresh_index() invalidates fsmonitor cache' ' + write_script .git/hooks/fsmonitor-test<<-\EOF && + EOF + clean_repo && + dirty_repo && + git add . && + git commit -m "to reset" && + git reset HEAD~1 && + git status >actual && + git -c core.fsmonitor= status >expect && + test_i18ncmp expect actual +' + +# test fsmonitor with and without preloadIndex +preload_values="false true" +for preload_val in $preload_values +do + test_expect_success "setup preloadIndex to $preload_val" ' + git config core.preloadIndex $preload_val && + if test $preload_val = true + then + GIT_FORCE_PRELOAD_TEST=$preload_val; export GIT_FORCE_PRELOAD_TEST + else + unset GIT_FORCE_PRELOAD_TEST + fi + ' + + # test fsmonitor with and without the untracked cache (if available) + uc_values="false" + test_have_prereq UNTRACKED_CACHE && uc_values="false true" + for uc_val in $uc_values + do + test_expect_success "setup untracked cache to $uc_val" ' + git config core.untrackedcache $uc_val + ' + + # Status is well tested elsewhere so we'll just ensure that the results are + # the same when using core.fsmonitor. + test_expect_success 'compare status with and without fsmonitor' ' + write_integration_script && + clean_repo && + dirty_repo && + git add new && + git add dir1/new && + git add dir2/new && + git status >actual && + git -c core.fsmonitor= status >expect && + test_i18ncmp expect actual + ' + + # Make sure it's actually skipping the check for modified and untracked + # (if enabled) files unless it is told about them. + test_expect_success "status doesn't detect unreported modifications" ' + write_script .git/hooks/fsmonitor-test<<-\EOF && + :>marker + EOF + clean_repo && + git status && + test_path_is_file marker && + dirty_repo && + rm -f marker && + git status >actual && + test_path_is_file marker && + test_i18ngrep ! "Changes not staged for commit:" actual && + if test $uc_val = true + then + test_i18ngrep ! "Untracked files:" actual + fi && + if test $uc_val = false + then + test_i18ngrep "Untracked files:" actual + fi && + rm -f marker + ' + done +done + +test_done
t/t7519/fsmonitor-all new file +24 -0
@@ -0,0 +1,24 @@ +#!/bin/sh +# +# An test hook script to integrate with git to test fsmonitor. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +#echo "$0 $*" >&2 + +if test "$#" -ne 2 +then + echo "$0: exactly 2 arguments expected" >&2 + exit 2 +fi + +if test "$1" != 1 +then + echo "Unsupported core.fsmonitor hook version." >&2 + exit 1 +fi + +echo "/"
t/t7519/fsmonitor-none new file +22 -0
@@ -0,0 +1,22 @@ +#!/bin/sh +# +# An test hook script to integrate with git to test fsmonitor. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +#echo "$0 $*" >&2 + +if test "$#" -ne 2 +then + echo "$0: exactly 2 arguments expected" >&2 + exit 2 +fi + +if test "$1" != 1 +then + echo "Unsupported core.fsmonitor hook version." >&2 + exit 1 +fi
t/t7519/fsmonitor-watchman new file +140 -0
140 lines not on this page · raw message
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 11/12 fsmonitor: add a sample integration script for Watchman
In reply to Ben PeartThis script integrates the new fsmonitor capabilities of git with the cross platform Watchman file watching service. To use the script:
Download and install Watchman from https://facebook.github.io/watchman/.
Rename the sample integration hook from fsmonitor-watchman.sample to fsmonitor-watchman. Configure git to use the extension:
git config core.fsmonitor .git/hooks/fsmonitor-watchman
Optionally turn on the untracked cache for optimal performance.
--- templates/hooks--fsmonitor-watchman.sample | 122 +++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100755 templates/hooks--fsmonitor-watchman.sample
templates/hooks--fsmonitor-watchman.sample new file +122 -0
@@ -0,0 +1,122 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 1) and a time in nanoseconds +# formatted as a string and outputs to stdout all files that have been +# modified since the given time. Paths must be relative to the root of +# the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $time) = @ARGV; + +# Check the hook interface version + +if ($version == 1) { + # convert nanoseconds to seconds + $time = int $time / 1000000000; +} else { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +# Convert unix style paths to escaped Windows style paths when running +# in Windows command prompt + +my $system = `uname -s`; +$system =~ s/[\r\n]+//g; +my $git_work_tree; + +if ($system =~ m/^MSYS_NT/) { + $git_work_tree = `cygpath -aw "\$PWD"`; + $git_work_tree =~ s/[\r\n]+//g; + $git_work_tree =~ s,\\,/,g; +} else { + $git_work_tree = $ENV{'PWD'}; +} + +my $retry = 1; + +launch_watchman(); + +sub launch_watchman { + + # Set input record separator + local $/ = 0666; + + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $time but were not transient (ie created after + # $time but no longer exist). + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + # + # The category of transient files that we want to ignore will have a + # creation clock (cclock) newer than $time_t value and will also not + # currently exist. + + my $query = <<" END"; + ["query", "$git_work_tree", { + "since": $time, + "fields": ["name"], + "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]] + }] + END + + print CHLD_IN $query; + my $response = <CHLD_OUT>; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + my $json_pkg; + eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; + } or do { + require JSON::PP; + $json_pkg = "JSON::PP"; + }; + + my $o = $json_pkg->new->utf8->decode($response); + + if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) { + print STDERR "Adding '$git_work_tree' to watchman's watch list.\n"; + $retry--; + qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + print "/\0"; + eval { launch_watchman() }; + exit 0; + } + + die "Watchman: $o->{error}.\n" . + "Falling back to scanning...\n" if $o->{error}; + + binmode STDOUT, ":utf8"; + local $, = "\0"; + print @{$o->{files}}; +}
Signature · 1 line
2.14.1.549.g6ff7ed0467
PATCH v8 12/12 fsmonitor: add a performance test
In reply to Ben PeartAdd a test utility (test-drop-caches) that flushes all changes to disk then drops file system cache on Windows, Linux, and OSX.
Add a perf test (p7519-fsmonitor.sh) for fsmonitor.
By default, the performance test will utilize the Watchman file system monitor if it is installed. If Watchman is not installed, it will use a dummy integration script that does not report any new or modified files. The dummy script has very little overhead which provides optimistic results.
The performance test will also use the untracked cache feature if it is available as fsmonitor uses it to speed up scanning for untracked files.
There are 4 environment variables that can be used to alter the default behavior of the performance test:
GIT_PERF_7519_UNTRACKED_CACHE: used to configure core.untrackedCacheGIT_PERF_7519_SPLIT_INDEX: used to configure core.splitIndexGIT_PERF_7519_FSMONITOR: used to configure core.fsmonitorGIT_PERF_7519_DROP_CACHE: if set, the OS caches are dropped between tests
The big win for using fsmonitor is the elimination of the need to scan the working directory looking for changed and untracked files. If the file information is all cached in RAM, the benefits are reduced.
--- Makefile | 1 + t/helper/.gitignore | 1 + t/helper/test-drop-caches.c | 164 +++++++++++++++++++++++++++++++++++++++ t/perf/p7519-fsmonitor.sh | 184 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 t/helper/test-drop-caches.c create mode 100755 t/perf/p7519-fsmonitor.sh
Makefile +1 -0
@@ -638,6 +638,7 @@ TEST_PROGRAMS_NEED_X += test-ctype TEST_PROGRAMS_NEED_X += test-config TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta +TEST_PROGRAMS_NEED_X += test-drop-caches TEST_PROGRAMS_NEED_X += test-dump-cache-tree TEST_PROGRAMS_NEED_X += test-dump-fsmonitor TEST_PROGRAMS_NEED_X += test-dump-split-index
t/helper/.gitignore +1 -0
@@ -3,6 +3,7 @@ /test-config /test-date /test-delta +/test-drop-caches /test-dump-cache-tree /test-dump-split-index /test-dump-untracked-cache
t/helper/test-drop-caches.c new file +164 -0
164 lines not on this page · raw message
t/perf/p7519-fsmonitor.sh new file +184 -0
184 lines not on this page · raw message
Signature · 1 line
2.14.1.549.g6ff7ed0467
Quoted 5 lines · Ben Peart
The only behavioral change from V7 is the removal of unnecessary uses of
CE_MATCH_IGNORE_FSMONITOR. With a better understanding of *why* the CE_MATCH_IGNORE_* flags are used, it is now clear they are not required in most cases whereCE_MATCH_IGNORE_FSMONITORwas being passed out of an abundance of caution.
The reviews and updates after this round was posted were to
* 01/12 had an obvious pointer-vs-pointee thinko, which I think I have locally fixed;
* 08/12 forgot to add a new test executable to .gitignore file, which I think I have locally fixed, too.
Any other review comments and suggestions for improvements? Otherwise I am tempted to declare victory and merge this to 'next' soonish.
For reference, here is the interdiff between what was posted as v8 and what I have on 'pu'.
Thanks.
compat/bswap.h | 4 ++-- t/helper/.gitignore | 1 + 2 files changed, 3 insertions(+), 2 deletions(-)
compat/bswap.h +2 -2
@@ -183,8 +183,8 @@ static inline uint32_t get_be32(const void *ptr)static inline uint64_t get_be64 (const void *ptr) {const unsigned char *p = ptr; -return (uint64_t )get_be32 (p[0 ]) <<32 | - (uint64_t )get_be32 (p[4 ]) <<0 ; +return (uint64_t )get_be32 (&p[0 ]) <<32 | + (uint64_t )get_be32 (&p[4 ]) <<0 ; }static inline void put_be32 (void *ptr,uint32_t value)
t/helper/.gitignore +1 -0
@@ -5,6 +5,7 @@ /test-delta /test-drop-caches /test-dump-cache-tree +/test-dump-fsmonitor /test-dump-split-index /test-dump-untracked-cache /test-fake-ssh
Quoted 27 lines
-----Original Message-----
From: Junio C Hamano [mailto:[email protected]]
Sent: Thursday, September 28, 2017 10:21 PM
To: Ben Peart <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH v8 00/12] Fast git status via a file system watcherQuoted 2 lines · Ben Peart
The only behavioral change from V7 is the removal of unnecessary uses of
CE_MATCH_IGNORE_FSMONITOR. With a better understanding of*why*
Quoted 4 lines
the
CE_MATCH_IGNORE_* flags are used, it is now clear they are not required in most cases whereCE_MATCH_IGNORE_FSMONITORwas being passed out of an abundance of caution.The reviews and updates after this round was posted were to
* 01/12 had an obvious pointer-vs-pointee thinko, which I think I have locally fixed;
* 08/12 forgot to add a new test executable to .gitignore file, which I think I have locally fixed, too.
Quoted 6 lines
Any other review comments and suggestions for improvements? Otherwise I am tempted to declare victory and merge this to 'next' soonish.
For reference, here is the interdiff between what was posted as v8 and what I have on 'pu'.
I had accumulated the same set of changes with one addition of removing a duplicate "the" from a comment in the fsmonitor.h file:
fsmonitor.h +1 -1
@@ -4,7 +4,7 @@extern struct trace_key trace_fsmonitor;/* -* Read the the fsmonitor index extension and (if configured) restore the +* Read the fsmonitor index extension and (if configured) restore the * CE_FSMONITOR_VALID state. */ extern int read_fsmonitor_extension (struct index_state *istate,const void *data,unsigned long sz);
Quoted 33 lines
Thanks.
compat/bswap.h | 4 ++-- t/helper/.gitignore | 1 + 2 files changed, 3 insertions(+), 2 deletions(-)compat/bswap.h index renamed from compat/bswap.h +0
-0
6b22c46214..5078ce5ecc100644compat/bswap.h +2
-2@@ -183,8 +183,8 @@ static inline uint32_t get_be32(const void *ptr) staticinline uint64_t get_be64 (const void *ptr) {const unsigned char *p = ptr; -return (uint64_t )get_be32 (p[0 ]) <<32 | - (uint64_t )get_be32 (p[4 ]) <<0 ; +return (uint64_t )get_be32 (&p[0 ]) <<32 | + (uint64_t )get_be32 (&p[4 ]) <<0 ; } static inline void put_be32(void *ptr, uint32_t value) diff --gitb/t/helper/.gitignore a/t/helper/.gitignore index
f9328eebdd..87a648a7cf100644t/helper/.gitignore +1
-0@@ -5,6 +5,7 @@ /test-delta /test-drop-caches /test-dump-cache-tree +/test-dump-fsmonitor /test-dump-split-index /test-dump-untracked-cache /test-fake-ssh
Quoted 18 lines · Ben Peart
I had accumulated the same set of changes with one addition of removing a duplicate "the" from a comment in the
fsmonitor.hfile:fsmonitor.h +1
-1@@ -4,7 +4,7 @@extern struct trace_key trace_fsmonitor;/* -* Read the the fsmonitor index extension and (if configured) restore the +* Read the fsmonitor index extension and (if configured) restore the * CE_FSMONITOR_VALID state. */ extern int read_fsmonitor_extension (struct index_state *istate,const void *data,unsigned long sz);Quoted 2 lines
Quoted 1 line
Quoted 1 line
Thanks.
OK, now my copy has the same, so we are in sync. Unless there is no more comment that benefits from a reroll of the series, let's run with this version for now and merge it to 'next'. Further updates can be done incrementally on top.
Thanks.
Quoted 20 lines · Junio C Hamano
Quoted 18 lines · Ben Peart
I had accumulated the same set of changes with one addition of removing a duplicate "the" from a comment in the
fsmonitor.hfile:fsmonitor.h +1
-1@@ -4,7 +4,7 @@extern struct trace_key trace_fsmonitor;/* -* Read the the fsmonitor index extension and (if configured) restore the +* Read the fsmonitor index extension and (if configured) restore the * CE_FSMONITOR_VALID state. */ extern int read_fsmonitor_extension (struct index_state *istate,const void *data,unsigned long sz);Quoted 2 lines
Quoted 9 lines
Quoted 1 line
Quoted 1 line
Thanks.
OK, now my copy has the same, so we are in sync. Unless there is no more comment that benefits from a reroll of the series, let's run with this version for now and merge it to 'next'. Further updates can be done incrementally on top.
Thanks.
Well, rats. I found one more issue that applies to two of the commits. Can you squash this in as well or do you want it in some other form?
t/t7519/fsmonitor-watchman +1 -1
@@ -36,7 +36,7 @@ my $system = `uname -s`; $system =~ s/[\r\n]+//g; my $git_work_tree; -if ($system =~ m/^MSYS_NT/) { +if ($system =~ m/^MSYS_NT/ || $system =~ m/^MINGW/) { $git_work_tree = `cygpath -aw "\$PWD"`; $git_work_tree =~ s/[\r\n]+//g; $git_work_tree =~ s,\\,/,g;
templates/hooks--fsmonitor-watchman.sample +1 -1
@@ -35,7 +35,7 @@ my $system = `uname -s`; $system =~ s/[\r\n]+//g; my $git_work_tree; -if ($system =~ m/^MSYS_NT/) { +if ($system =~ m/^MSYS_NT/ || $system =~ m/^MINGW/) { $git_work_tree = `cygpath -aw "\$PWD"`; $git_work_tree =~ s/[\r\n]+//g; $git_work_tree =~ s,\\,/,g;
Quoted 3 lines · Ben Peart
Well, rats. I found one more issue that applies to two of the commits. Can you squash this in as well or do you want it in some other form?
Rats indeed. Let's go incremental as promised, perhaps like this (but please supply a better description if you have one).
-- >8 --
From: Ben Peart <[email protected]>
Subject: fsmonitor: MINGW support for watchman integration
Instead of just taking $ENV{'PWD'}, use the same logic that converts PWD to $git_work_tree on MSYS_NT in the watchman integration hook script also on MINGW.
--- t/t7519/fsmonitor-watchman | 2 +- templates/hooks--fsmonitor-watchman.sample | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
t/t7519/fsmonitor-watchman +1 -1
@@ -36,7 +36,7 @@ my $system = `uname -s`; $system =~ s/[\r\n]+//g; my $git_work_tree; -if ($system =~ m/^MSYS_NT/) { +if ($system =~ m/^MSYS_NT/ || $system =~ m/^MINGW/) { $git_work_tree = `cygpath -aw "\$PWD"`; $git_work_tree =~ s/[\r\n]+//g; $git_work_tree =~ s,\\,/,g;
templates/hooks--fsmonitor-watchman.sample +1 -1
@@ -35,7 +35,7 @@ my $system = `uname -s`; $system =~ s/[\r\n]+//g; my $git_work_tree; -if ($system =~ m/^MSYS_NT/) { +if ($system =~ m/^MSYS_NT/ || $system =~ m/^MINGW/) { $git_work_tree = `cygpath -aw "\$PWD"`; $git_work_tree =~ s/[\r\n]+//g; $git_work_tree =~ s,\\,/,g;
Quoted 2 lines · Junio C Hamano
Rats indeed. Let's go incremental as promised, perhaps like this (but please supply a better description if you have one).
I think you'll also want the following squashed into 5c8cdcfd8 and def437671:
-- >8 --
From 445d45027bb5b7823338cf111910d2884af6318b Mon Sep 17 00:00:00 2001
From: Alex Vandiver <[email protected]>
Date: Tue, 3 Oct 2017 23:27:46 -0700
Subject: [PATCH] fsmonitor: Read entirety of watchman output
In perl, setting $/ sets the string that is used as the "record separator," which sets the boundary that the <> construct reads to. Setting local $/ = 0666; evaluates the octal, getting 438, and stringifies it. Thus, the later read from <CHLD_OUT> stops as soon as it encounters the string "438" in the watchman output, yielding invalid JSON; repositories containing filenames with SHA1 hashes are able to trip this easily.
Set $/ to undefined, thus slurping all output from watchman. Also close STDIN which is provided to watchman, to better guarantee that we cannot deadlock with watchman while both attempting to read.
--- t/t7519/fsmonitor-watchman | 6 ++---- templates/hooks--fsmonitor-watchman.sample | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-)
t/t7519/fsmonitor-watchman +2 -4
@@ -50,9 +50,6 @@ launch_watchman(); sub launch_watchman { - # Set input record separator - local $/ = 0666; - my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j') or die "open2() failed: $!\n" . "Falling back to scanning...\n";
@@ -83,7 +80,8 @@ sub launch_watchman { close $fh; print CHLD_IN $query; - my $response = <CHLD_OUT>; + close CHLD_IN; + my $response = do {local $/; <CHLD_OUT>}; open ($fh, ">", ".git/watchman-response.json"); print $fh $response;
templates/hooks--fsmonitor-watchman.sample +2 -4
@@ -49,9 +49,6 @@ launch_watchman(); sub launch_watchman { - # Set input record separator - local $/ = 0666; - my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j') or die "open2() failed: $!\n" . "Falling back to scanning...\n";
@@ -78,7 +75,8 @@ sub launch_watchman { END print CHLD_IN $query; - my $response = <CHLD_OUT>; + close CHLD_IN; + my $response = do {local $/; <CHLD_OUT>}; die "Watchman: command returned no output.\n" . "Falling back to scanning...\n" if $response eq "";
Signature · 1 line
2.14.2.959.g6663358d3
Quoted 21 lines · Alex Vandiver
Quoted 2 lines · Junio C Hamano
Rats indeed. Let's go incremental as promised, perhaps like this (but please supply a better description if you have one).
I think you'll also want the following squashed into
5c8cdcfd8anddef437671:-- >8 --
From445d45027bb5b7823338cf111910d2884af6318bMon Sep 17 00:00:00 2001
From: Alex Vandiver <[email protected]>
Date: Tue, 3 Oct 2017 23:27:46 -0700
Subject: [PATCH] fsmonitor: Read entirety of watchman outputIn perl, setting $/ sets the string that is used as the "record separator," which sets the boundary that the
<>construct reads to. Settinglocal $/ = 0666;evaluates the octal, getting 438, and stringifies it. Thus, the later read from<CHLD_OUT>stops as soon as it encounters the string "438" in the watchman output, yielding invalid JSON; repositories containing filenames with SHA1 hashes are able to trip this easily.
Quoted 4 lines
Set
$/to undefined, thus slurping all output from watchman. Also close STDIN which is provided to watchman, to better guarantee that we cannot deadlock with watchman while both attempting to read.
Thank you! I'm a perl neophyte so have to rely on others when it comes to these types of perl issues. I tried out your fixes and they appear to work well.
While testing them, I discovered that your fix of local $/ = 0666; exposed an existing issue in the test version of the integration script.
The fix for that is within my perl capabilities and is fixed with the
following patch:
-- >8 --
From 3e3b983a4208a62d166c233a7de3bf045322f6c7 Mon Sep 17 00:00:00 2001
From: Ben Peart <[email protected]>
Date: Wed, 4 Oct 2017 08:33:39 -0400
Subject: [PATCH] fsmonitor: preserve utf8 filenames in fsmonitor-watchman log
Update the test fsmonitor-watchman integration script to properly preserve utf8 filenames when outputting the .git/watchman-output.out log file.
--- t/t7519/fsmonitor-watchman | 1 + 1 file changed, 1 insertion(+)
t/t7519/fsmonitor-watchman +1 -0
@@ -129,6 +129,7 @@ sub launch_watchman { "Falling back to scanning...\n" if $o->{error}; open ($fh, ">", ".git/watchman-output.out"); + binmode $fh, ":utf8"; print $fh @{$o->{files}}; close $fh;
Signature · 1 line
2.14.1.windows.1.1034.g0776750557
Quoted 8 lines · Junio C Hamano
Quoted 3 lines · Ben Peart
Well, rats. I found one more issue that applies to two of the commits. Can you squash this in as well or do you want it in some other form?
Rats indeed. Let's go incremental as promised, perhaps like this (but please supply a better description if you have one).
Thank you. Looks good.
Quoted 43 lines
-- >8 --
From: Ben Peart <[email protected]>
Subject: fsmonitor: MINGW support for watchman integrationInstead of just taking $ENV{'PWD'}, use the same logic that converts PWD to $git_work_tree on
MSYS_NTin the watchman integration hook script also on MINGW.Signed-off-by Ben Peart <[email protected]>Signed-off-by Junio C Hamano <[email protected]>--- t/t7519/fsmonitor-watchman | 2 +- templates/hooks--fsmonitor-watchman.sample | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)t/t7519/fsmonitor-watchman +1
-1@@ -36,7 +36,7 @@ my $system = `uname -s`; $system =~ s/[\r\n]+//g; my $git_work_tree; -if ($system =~ m/^MSYS_NT/) { +if ($system =~ m/^MSYS_NT/ || $system =~ m/^MINGW/) { $git_work_tree = `cygpath -aw "\$PWD"`; $git_work_tree =~ s/[\r\n]+//g; $git_work_tree =~ s,\\,/,g;templates/hooks--fsmonitor-watchman.sample +1
-1@@ -35,7 +35,7 @@ my $system = `uname -s`; $system =~ s/[\r\n]+//g; my $git_work_tree; -if ($system =~ m/^MSYS_NT/) { +if ($system =~ m/^MSYS_NT/ || $system =~ m/^MINGW/) { $git_work_tree = `cygpath -aw "\$PWD"`; $git_work_tree =~ s/[\r\n]+//g; $git_work_tree =~ s,\\,/,g;