Skip to content

Commit e05336b

Browse files
committed
Merge branch 'bp/fsmonitor'
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: preserve utf8 filenames in fsmonitor-watchman log fsmonitor: read entirety of watchman output fsmonitor: MINGW support for watchman integration 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 f5da077 + 614a718 commit e05336b

33 files changed

+1570
-23
lines changed

Documentation/config.txt

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

419+
core.fsmonitor::
420+
If set, the value of this variable is used as a command which
421+
will identify all files that may have changed since the
422+
requested date/time. This information is used to speed up git by
423+
avoiding unnecessary processing of files that have not changed.
424+
See the "fsmonitor-watchman" section of linkgit:githooks[5].
425+
419426
core.trustctime::
420427
If false, the ctime differences between the index and the
421428
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 scanning 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
@@ -454,6 +454,34 @@ the name of the file that holds the e-mail to be sent. Exiting with a
454454
non-zero status causes 'git send-email' to abort before sending any
455455
e-mails.
456456

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

458486
GIT
459487
---

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
@@ -646,7 +646,9 @@ TEST_PROGRAMS_NEED_X += test-ctype
646646
TEST_PROGRAMS_NEED_X += test-config
647647
TEST_PROGRAMS_NEED_X += test-date
648648
TEST_PROGRAMS_NEED_X += test-delta
649+
TEST_PROGRAMS_NEED_X += test-drop-caches
649650
TEST_PROGRAMS_NEED_X += test-dump-cache-tree
651+
TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
650652
TEST_PROGRAMS_NEED_X += test-dump-split-index
651653
TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
652654
TEST_PROGRAMS_NEED_X += test-fake-ssh
@@ -794,6 +796,7 @@ LIB_OBJS += ewah/ewah_rlw.o
794796
LIB_OBJS += exec_cmd.o
795797
LIB_OBJS += fetch-pack.o
796798
LIB_OBJS += fsck.o
799+
LIB_OBJS += fsmonitor.o
797800
LIB_OBJS += gettext.o
798801
LIB_OBJS += gpg-interface.o
799802
LIB_OBJS += graph.o

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
@@ -204,6 +204,7 @@ struct cache_entry {
204204
#define CE_ADDED (1 << 19)
205205

206206
#define CE_HASHED (1 << 20)
207+
#define CE_FSMONITOR_VALID (1 << 21)
207208
#define CE_WT_REMOVE (1 << 22) /* remove in work directory */
208209
#define CE_CONFLICTED (1 << 23)
209210

@@ -327,6 +328,7 @@ static inline unsigned int canon_mode(unsigned int mode)
327328
#define CACHE_TREE_CHANGED (1 << 5)
328329
#define SPLIT_INDEX_ORDERED (1 << 6)
329330
#define UNTRACKED_CHANGED (1 << 7)
331+
#define FSMONITOR_CHANGED (1 << 8)
330332

331333
struct split_index;
332334
struct untracked_cache;
@@ -345,6 +347,7 @@ struct index_state {
345347
struct hashmap dir_hash;
346348
unsigned char sha1[20];
347349
struct untracked_cache *untracked;
350+
uint64_t fsmonitor_last_update;
348351
};
349352

350353
extern struct index_state the_index;
@@ -700,8 +703,10 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
700703
#define CE_MATCH_IGNORE_MISSING 0x08
701704
/* enable stat refresh */
702705
#define CE_MATCH_REFRESH 0x10
703-
extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
704-
extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
706+
/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
707+
#define CE_MATCH_IGNORE_FSMONITOR 0X20
708+
extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
709+
extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
705710

706711
#define HASH_WRITE_OBJECT 1
707712
#define HASH_FORMAT_CHECK 2
@@ -799,6 +804,7 @@ extern int core_apply_sparse_checkout;
799804
extern int precomposed_unicode;
800805
extern int protect_hfs;
801806
extern int protect_ntfs;
807+
extern const char *core_fsmonitor;
802808

803809
/*
804810
* Include broken refs in all ref iterations, which will

compat/bswap.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ static inline uint64_t git_bswap64(uint64_t x)
158158

159159
#define get_be16(p) ntohs(*(unsigned short *)(p))
160160
#define get_be32(p) ntohl(*(unsigned int *)(p))
161+
#define get_be64(p) ntohll(*(uint64_t *)(p))
161162
#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
163+
#define put_be64(p, v) do { *(uint64_t *)(p) = htonll(v); } while (0)
162164

163165
#else
164166

@@ -178,6 +180,13 @@ static inline uint32_t get_be32(const void *ptr)
178180
(uint32_t)p[3] << 0;
179181
}
180182

183+
static inline uint64_t get_be64(const void *ptr)
184+
{
185+
const unsigned char *p = ptr;
186+
return (uint64_t)get_be32(&p[0]) << 32 |
187+
(uint64_t)get_be32(&p[4]) << 0;
188+
}
189+
181190
static inline void put_be32(void *ptr, uint32_t value)
182191
{
183192
unsigned char *p = ptr;
@@ -187,4 +196,17 @@ static inline void put_be32(void *ptr, uint32_t value)
187196
p[3] = value >> 0;
188197
}
189198

199+
static inline void put_be64(void *ptr, uint64_t value)
200+
{
201+
unsigned char *p = ptr;
202+
p[0] = value >> 56;
203+
p[1] = value >> 48;
204+
p[2] = value >> 40;
205+
p[3] = value >> 32;
206+
p[4] = value >> 24;
207+
p[5] = value >> 16;
208+
p[6] = value >> 8;
209+
p[7] = value >> 0;
210+
}
211+
190212
#endif

0 commit comments

Comments
 (0)