Skip to content

Commit 6f9aed9

Browse files
committed
Merge branch 'bp/fsmonitor' into pu
We learned to talk to watchman to speed up "git status" and other operations that need to see which paths have been modified. * bp/fsmonitor: fsmonitor: add a performance test fsmonitor: add a sample integration script for Watchman fsmonitor: add test cases for fsmonitor extension split-index: disable the fsmonitor extension when running the split index test fsmonitor: add a test tool to dump the index extension update-index: add fsmonitor support to update-index ls-files: Add support in ls-files to display the fsmonitor valid bit fsmonitor: add documentation for the fsmonitor extension. fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files. update-index: add a new --force-write-index option preload-index: add override to enable testing preload-index bswap: add 64 bit endianness helper get_be64
2 parents 3e26336 + c167a5c commit 6f9aed9

34 files changed

+1572
-30
lines changed

Documentation/config.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,13 @@ core.protectNTFS::
413413
8.3 "short" names.
414414
Defaults to `true` on Windows, and `false` elsewhere.
415415

416+
core.fsmonitor::
417+
If set, the value of this variable is used as a command which
418+
will identify all files that may have changed since the
419+
requested date/time. This information is used to speed up git by
420+
avoiding unnecessary processing of files that have not changed.
421+
See the "fsmonitor-watchman" section of linkgit:githooks[5].
422+
416423
core.trustctime::
417424
If false, the ctime differences between the index and the
418425
working tree are ignored; useful when the inode change time

Documentation/git-ls-files.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ git-ls-files - Show information about files in the index and the working tree
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git ls-files' [-z] [-t] [-v]
12+
'git ls-files' [-z] [-t] [-v] [-f]
1313
(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])*
1414
(-[c|d|o|i|s|u|k|m])*
1515
[--eol]
@@ -133,6 +133,11 @@ a space) at the start of each line:
133133
that are marked as 'assume unchanged' (see
134134
linkgit:git-update-index[1]).
135135

136+
-f::
137+
Similar to `-t`, but use lowercase letters for files
138+
that are marked as 'fsmonitor valid' (see
139+
linkgit:git-update-index[1]).
140+
136141
--full-name::
137142
When run from a subdirectory, the command usually
138143
outputs paths relative to the current directory. This

Documentation/git-update-index.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ SYNOPSIS
1616
[--chmod=(+|-)x]
1717
[--[no-]assume-unchanged]
1818
[--[no-]skip-worktree]
19+
[--[no-]fsmonitor-valid]
1920
[--ignore-submodules]
2021
[--[no-]split-index]
2122
[--[no-|test-|force-]untracked-cache]
23+
[--[no-]fsmonitor]
2224
[--really-refresh] [--unresolve] [--again | -g]
2325
[--info-only] [--index-info]
2426
[-z] [--stdin] [--index-version <n>]
@@ -111,6 +113,12 @@ you will need to handle the situation manually.
111113
set and unset the "skip-worktree" bit for the paths. See
112114
section "Skip-worktree bit" below for more information.
113115

116+
--[no-]fsmonitor-valid::
117+
When one of these flags is specified, the object name recorded
118+
for the paths are not updated. Instead, these options
119+
set and unset the "fsmonitor valid" bit for the paths. See
120+
section "File System Monitor" below for more information.
121+
114122
-g::
115123
--again::
116124
Runs 'git update-index' itself on the paths whose index
@@ -201,6 +209,15 @@ will remove the intended effect of the option.
201209
`--untracked-cache` used to imply `--test-untracked-cache` but
202210
this option would enable the extension unconditionally.
203211

212+
--fsmonitor::
213+
--no-fsmonitor::
214+
Enable or disable files system monitor feature. These options
215+
take effect whatever the value of the `core.fsmonitor`
216+
configuration variable (see linkgit:git-config[1]). But a warning
217+
is emitted when the change goes against the configured value, as
218+
the configured value will take effect next time the index is
219+
read and this will remove the intended effect of the option.
220+
204221
\--::
205222
Do not interpret any more arguments as options.
206223

@@ -447,6 +464,34 @@ command reads the index; while when `--[no-|force-]untracked-cache`
447464
are used, the untracked cache is immediately added to or removed from
448465
the index.
449466

467+
File System Monitor
468+
-------------------
469+
470+
This feature is intended to speed up git operations for repos that have
471+
large working directories.
472+
473+
It enables git to work together with a file system monitor (see the
474+
"fsmonitor-watchman" section of linkgit:githooks[5]) that can
475+
inform it as to what files have been modified. This enables git to avoid
476+
having to lstat() every file to find modified files.
477+
478+
When used in conjunction with the untracked cache, it can further improve
479+
performance by avoiding the cost of scaning the entire working directory
480+
looking for new files.
481+
482+
If you want to enable (or disable) this feature, it is easier to use
483+
the `core.fsmonitor` configuration variable (see
484+
linkgit:git-config[1]) than using the `--fsmonitor` option to
485+
`git update-index` in each repository, especially if you want to do so
486+
across all repositories you use, because you can set the configuration
487+
variable to `true` (or `false`) in your `$HOME/.gitconfig` just once
488+
and have it affect all repositories you touch.
489+
490+
When the `core.fsmonitor` configuration variable is changed, the
491+
file system monitor is added to or removed from the index the next time
492+
a command reads the index. When `--[no-]fsmonitor` are used, the file
493+
system monitor is immediately added to or removed from the index.
494+
450495
Configuration
451496
-------------
452497

Documentation/githooks.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,34 @@ the name of the file that holds the e-mail to be sent. Exiting with a
455455
non-zero status causes 'git send-email' to abort before sending any
456456
e-mails.
457457

458+
fsmonitor-watchman
459+
~~~~~~~~~~~~~~~~~~
460+
461+
This hook is invoked when the configuration option core.fsmonitor is
462+
set to .git/hooks/fsmonitor-watchman. It takes two arguments, a version
463+
(currently 1) and the time in elapsed nanoseconds since midnight,
464+
January 1, 1970.
465+
466+
The hook should output to stdout the list of all files in the working
467+
directory that may have changed since the requested time. The logic
468+
should be inclusive so that it does not miss any potential changes.
469+
The paths should be relative to the root of the working directory
470+
and be separated by a single NUL.
471+
472+
It is OK to include files which have not actually changed. All changes
473+
including newly-created and deleted files should be included. When
474+
files are renamed, both the old and the new name should be included.
475+
476+
Git will limit what files it checks for changes as well as which
477+
directories are checked for untracked files based on the path names
478+
given.
479+
480+
An optimized way to tell git "all files have changed" is to return
481+
the filename '/'.
482+
483+
The exit status determines whether git will use the data from the
484+
hook to limit its search. On error, it will fall back to verifying
485+
all files and folders.
458486

459487
GIT
460488
---

Documentation/technical/index-format.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,22 @@ The remaining data of each directory block is grouped by type:
295295
in the previous ewah bitmap.
296296

297297
- One NUL.
298+
299+
== File System Monitor cache
300+
301+
The file system monitor cache tracks files for which the core.fsmonitor
302+
hook has told us about changes. The signature for this extension is
303+
{ 'F', 'S', 'M', 'N' }.
304+
305+
The extension starts with
306+
307+
- 32-bit version number: the current supported version is 1.
308+
309+
- 64-bit time: the extension data reflects all changes through the given
310+
time which is stored as the nanoseconds elapsed since midnight,
311+
January 1, 1970.
312+
313+
- 32-bit bitmap size: the size of the CE_FSMONITOR_VALID bitmap.
314+
315+
- An ewah bitmap, the n-th bit indicates whether the n-th index entry
316+
is not CE_FSMONITOR_VALID.

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,9 @@ TEST_PROGRAMS_NEED_X += test-ctype
647647
TEST_PROGRAMS_NEED_X += test-config
648648
TEST_PROGRAMS_NEED_X += test-date
649649
TEST_PROGRAMS_NEED_X += test-delta
650+
TEST_PROGRAMS_NEED_X += test-drop-caches
650651
TEST_PROGRAMS_NEED_X += test-dump-cache-tree
652+
TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
651653
TEST_PROGRAMS_NEED_X += test-dump-split-index
652654
TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
653655
TEST_PROGRAMS_NEED_X += test-fake-ssh
@@ -795,6 +797,7 @@ LIB_OBJS += ewah/ewah_rlw.o
795797
LIB_OBJS += exec_cmd.o
796798
LIB_OBJS += fetch-pack.o
797799
LIB_OBJS += fsck.o
800+
LIB_OBJS += fsmonitor.o
798801
LIB_OBJS += gettext.o
799802
LIB_OBJS += gpg-interface.o
800803
LIB_OBJS += graph.o

apply.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3399,7 +3399,7 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st)
33993399
return -1;
34003400
return 0;
34013401
}
3402-
return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
3402+
return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_IGNORE_FSMONITOR);
34033403
}
34043404

34053405
#define SUBMODULE_PATCH_WITHOUT_INDEX 1

builtin/ls-files.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ static int show_resolve_undo;
3131
static int show_modified;
3232
static int show_killed;
3333
static int show_valid_bit;
34+
static int show_fsmonitor_bit;
3435
static int line_terminator = '\n';
3536
static int debug_mode;
3637
static int show_eol;
@@ -86,7 +87,8 @@ static const char *get_tag(const struct cache_entry *ce, const char *tag)
8687
{
8788
static char alttag[4];
8889

89-
if (tag && *tag && show_valid_bit && (ce->ce_flags & CE_VALID)) {
90+
if (tag && *tag && ((show_valid_bit && (ce->ce_flags & CE_VALID)) ||
91+
(show_fsmonitor_bit && (ce->ce_flags & CE_FSMONITOR_VALID)))) {
9092
memcpy(alttag, tag, 3);
9193

9294
if (isalpha(tag[0])) {
@@ -515,6 +517,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
515517
N_("identify the file status with tags")),
516518
OPT_BOOL('v', NULL, &show_valid_bit,
517519
N_("use lowercase letters for 'assume unchanged' files")),
520+
OPT_BOOL('f', NULL, &show_fsmonitor_bit,
521+
N_("use lowercase letters for 'fsmonitor clean' files")),
518522
OPT_BOOL('c', "cached", &show_cached,
519523
N_("show cached files in the output (default)")),
520524
OPT_BOOL('d', "deleted", &show_deleted,
@@ -584,7 +588,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
584588
for (i = 0; i < exclude_list.nr; i++) {
585589
add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args);
586590
}
587-
if (show_tag || show_valid_bit) {
591+
if (show_tag || show_valid_bit || show_fsmonitor_bit) {
588592
tag_cached = "H ";
589593
tag_unmerged = "M ";
590594
tag_removed = "R ";

builtin/update-index.c

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "pathspec.h"
1717
#include "dir.h"
1818
#include "split-index.h"
19+
#include "fsmonitor.h"
1920

2021
/*
2122
* Default to not allowing changes to the list of files. The
@@ -32,6 +33,7 @@ static int force_remove;
3233
static int verbose;
3334
static int mark_valid_only;
3435
static int mark_skip_worktree_only;
36+
static int mark_fsmonitor_only;
3537
#define MARK_FLAG 1
3638
#define UNMARK_FLAG 2
3739
static struct strbuf mtime_dir = STRBUF_INIT;
@@ -228,6 +230,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)
228230
int namelen = strlen(path);
229231
int pos = cache_name_pos(path, namelen);
230232
if (0 <= pos) {
233+
mark_fsmonitor_invalid(&the_index, active_cache[pos]);
231234
if (mark)
232235
active_cache[pos]->ce_flags |= flag;
233236
else
@@ -460,6 +463,11 @@ static void update_one(const char *path)
460463
die("Unable to mark file %s", path);
461464
return;
462465
}
466+
if (mark_fsmonitor_only) {
467+
if (mark_ce_flags(path, CE_FSMONITOR_VALID, mark_fsmonitor_only == MARK_FLAG))
468+
die("Unable to mark file %s", path);
469+
return;
470+
}
463471

464472
if (force_remove) {
465473
if (remove_file_from_cache(path))
@@ -917,6 +925,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
917925
struct refresh_params refresh_args = {0, &has_errors};
918926
int lock_error = 0;
919927
int split_index = -1;
928+
int force_write = 0;
929+
int fsmonitor = -1;
920930
struct lock_file lock_file = LOCK_INIT;
921931
struct parse_opt_ctx_t ctx;
922932
strbuf_getline_fn getline_fn;
@@ -1008,6 +1018,16 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
10081018
N_("test if the filesystem supports untracked cache"), UC_TEST),
10091019
OPT_SET_INT(0, "force-untracked-cache", &untracked_cache,
10101020
N_("enable untracked cache without testing the filesystem"), UC_FORCE),
1021+
OPT_SET_INT(0, "force-write-index", &force_write,
1022+
N_("write out the index even if is not flagged as changed"), 1),
1023+
OPT_BOOL(0, "fsmonitor", &fsmonitor,
1024+
N_("enable or disable file system monitor")),
1025+
{OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL,
1026+
N_("mark files as fsmonitor valid"),
1027+
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
1028+
{OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL,
1029+
N_("clear fsmonitor valid bit"),
1030+
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
10111031
OPT_END()
10121032
};
10131033

@@ -1146,7 +1166,23 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
11461166
die("BUG: bad untracked_cache value: %d", untracked_cache);
11471167
}
11481168

1149-
if (active_cache_changed) {
1169+
if (fsmonitor > 0) {
1170+
if (git_config_get_fsmonitor() == 0)
1171+
warning(_("core.fsmonitor is unset; "
1172+
"set it if you really want to "
1173+
"enable fsmonitor"));
1174+
add_fsmonitor(&the_index);
1175+
report(_("fsmonitor enabled"));
1176+
} else if (!fsmonitor) {
1177+
if (git_config_get_fsmonitor() == 1)
1178+
warning(_("core.fsmonitor is set; "
1179+
"remove it if you really want to "
1180+
"disable fsmonitor"));
1181+
remove_fsmonitor(&the_index);
1182+
report(_("fsmonitor disabled"));
1183+
}
1184+
1185+
if (active_cache_changed || force_write) {
11501186
if (newfd < 0) {
11511187
if (refresh_args.flags & REFRESH_QUIET)
11521188
exit(128);

cache.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ struct cache_entry {
243243
#define CE_ADDED (1 << 19)
244244

245245
#define CE_HASHED (1 << 20)
246+
#define CE_FSMONITOR_VALID (1 << 21)
246247
#define CE_WT_REMOVE (1 << 22) /* remove in work directory */
247248
#define CE_CONFLICTED (1 << 23)
248249

@@ -366,6 +367,7 @@ static inline unsigned int canon_mode(unsigned int mode)
366367
#define CACHE_TREE_CHANGED (1 << 5)
367368
#define SPLIT_INDEX_ORDERED (1 << 6)
368369
#define UNTRACKED_CHANGED (1 << 7)
370+
#define FSMONITOR_CHANGED (1 << 8)
369371

370372
struct split_index;
371373
struct untracked_cache;
@@ -384,6 +386,7 @@ struct index_state {
384386
struct hashmap dir_hash;
385387
unsigned char sha1[20];
386388
struct untracked_cache *untracked;
389+
uint64_t fsmonitor_last_update;
387390
};
388391

389392
extern struct index_state the_index;
@@ -728,8 +731,10 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
728731
#define CE_MATCH_IGNORE_MISSING 0x08
729732
/* enable stat refresh */
730733
#define CE_MATCH_REFRESH 0x10
731-
extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
732-
extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
734+
/* do stat comparison even if CE_FSMONITOR_VALID is true */
735+
#define CE_MATCH_IGNORE_FSMONITOR 0X20
736+
extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
737+
extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
733738

734739
#define HASH_WRITE_OBJECT 1
735740
#define HASH_FORMAT_CHECK 2
@@ -822,6 +827,7 @@ extern int core_apply_sparse_checkout;
822827
extern int precomposed_unicode;
823828
extern int protect_hfs;
824829
extern int protect_ntfs;
830+
extern const char *core_fsmonitor;
825831

826832
/*
827833
* Include broken refs in all ref iterations, which will

0 commit comments

Comments
 (0)