Skip to content

Commit 883e248

Browse files
benpeartgitster
authored andcommitted
fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.
When 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. Signed-off-by: Ben Peart <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7c545be commit 883e248

16 files changed

+417
-19
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,7 @@ LIB_OBJS += ewah/ewah_rlw.o
786786
LIB_OBJS += exec_cmd.o
787787
LIB_OBJS += fetch-pack.o
788788
LIB_OBJS += fsck.o
789+
LIB_OBJS += fsmonitor.o
789790
LIB_OBJS += gettext.o
790791
LIB_OBJS += gpg-interface.o
791792
LIB_OBJS += graph.o

builtin/update-index.c

Lines changed: 2 additions & 0 deletions
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
@@ -233,6 +234,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)
233234
else
234235
active_cache[pos]->ce_flags &= ~flag;
235236
active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
237+
mark_fsmonitor_invalid(&the_index, active_cache[pos]);
236238
cache_tree_invalidate_path(&the_index, path);
237239
active_cache_changed |= CE_ENTRY_CHANGED;
238240
return 0;

cache.h

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

205205
#define CE_HASHED (1 << 20)
206+
#define CE_FSMONITOR_VALID (1 << 21)
206207
#define CE_WT_REMOVE (1 << 22) /* remove in work directory */
207208
#define CE_CONFLICTED (1 << 23)
208209

@@ -326,6 +327,7 @@ static inline unsigned int canon_mode(unsigned int mode)
326327
#define CACHE_TREE_CHANGED (1 << 5)
327328
#define SPLIT_INDEX_ORDERED (1 << 6)
328329
#define UNTRACKED_CHANGED (1 << 7)
330+
#define FSMONITOR_CHANGED (1 << 8)
329331

330332
struct split_index;
331333
struct untracked_cache;
@@ -344,6 +346,7 @@ struct index_state {
344346
struct hashmap dir_hash;
345347
unsigned char sha1[20];
346348
struct untracked_cache *untracked;
349+
uint64_t fsmonitor_last_update;
347350
};
348351

349352
extern struct index_state the_index;
@@ -679,8 +682,10 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
679682
#define CE_MATCH_IGNORE_MISSING 0x08
680683
/* enable stat refresh */
681684
#define CE_MATCH_REFRESH 0x10
682-
extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
683-
extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
685+
/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
686+
#define CE_MATCH_IGNORE_FSMONITOR 0X20
687+
extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
688+
extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
684689

685690
#define HASH_WRITE_OBJECT 1
686691
#define HASH_FORMAT_CHECK 2
@@ -773,6 +778,7 @@ extern int core_apply_sparse_checkout;
773778
extern int precomposed_unicode;
774779
extern int protect_hfs;
775780
extern int protect_ntfs;
781+
extern const char *core_fsmonitor;
776782

777783
/*
778784
* Include broken refs in all ref iterations, which will

config.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2165,6 +2165,20 @@ int git_config_get_max_percent_split_change(void)
21652165
return -1; /* default value */
21662166
}
21672167

2168+
int git_config_get_fsmonitor(void)
2169+
{
2170+
if (git_config_get_pathname("core.fsmonitor", &core_fsmonitor))
2171+
core_fsmonitor = getenv("GIT_FSMONITOR_TEST");
2172+
2173+
if (core_fsmonitor && !*core_fsmonitor)
2174+
core_fsmonitor = NULL;
2175+
2176+
if (core_fsmonitor)
2177+
return 1;
2178+
2179+
return 0;
2180+
}
2181+
21682182
NORETURN
21692183
void git_die_config_linenr(const char *key, const char *filename, int linenr)
21702184
{

config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ extern int git_config_get_pathname(const char *key, const char **dest);
211211
extern int git_config_get_untracked_cache(void);
212212
extern int git_config_get_split_index(void);
213213
extern int git_config_get_max_percent_split_change(void);
214+
extern int git_config_get_fsmonitor(void);
214215

215216
/* This dies if the configured or default date is in the future */
216217
extern int git_config_get_expiry(const char *key, const char **output);

diff-lib.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "refs.h"
1313
#include "submodule.h"
1414
#include "dir.h"
15+
#include "fsmonitor.h"
1516

1617
/*
1718
* diff-files
@@ -228,6 +229,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
228229

229230
if (!changed && !dirty_submodule) {
230231
ce_mark_uptodate(ce);
232+
mark_fsmonitor_valid(ce);
231233
if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
232234
continue;
233235
}

dir.c

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "utf8.h"
1919
#include "varint.h"
2020
#include "ewah/ewok.h"
21+
#include "fsmonitor.h"
2122

2223
/*
2324
* 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,
16881689
if (!untracked)
16891690
return 0;
16901691

1691-
if (stat(path->len ? path->buf : ".", &st)) {
1692-
invalidate_directory(dir->untracked, untracked);
1693-
memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
1694-
return 0;
1695-
}
1696-
if (!untracked->valid ||
1697-
match_stat_data_racy(istate, &untracked->stat_data, &st)) {
1698-
if (untracked->valid)
1692+
/*
1693+
* With fsmonitor, we can trust the untracked cache's valid field.
1694+
*/
1695+
refresh_fsmonitor(istate);
1696+
if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
1697+
if (stat(path->len ? path->buf : ".", &st)) {
16991698
invalidate_directory(dir->untracked, untracked);
1700-
fill_stat_data(&untracked->stat_data, &st);
1701-
return 0;
1699+
memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
1700+
return 0;
1701+
}
1702+
if (!untracked->valid ||
1703+
match_stat_data_racy(istate, &untracked->stat_data, &st)) {
1704+
if (untracked->valid)
1705+
invalidate_directory(dir->untracked, untracked);
1706+
fill_stat_data(&untracked->stat_data, &st);
1707+
return 0;
1708+
}
17021709
}
17031710

17041711
if (untracked->check_only != !!check_only) {

dir.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ struct untracked_cache {
139139
int gitignore_invalidated;
140140
int dir_invalidated;
141141
int dir_opened;
142+
/* fsmonitor invalidation data */
143+
unsigned int use_fsmonitor : 1;
142144
};
143145

144146
struct dir_struct {

entry.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "streaming.h"
55
#include "submodule.h"
66
#include "progress.h"
7+
#include "fsmonitor.h"
78

89
static void create_directories(const char *path, int path_len,
910
const struct checkout *state)
@@ -357,6 +358,7 @@ static int write_entry(struct cache_entry *ce,
357358
lstat(ce->name, &st);
358359
fill_stat_cache_info(ce, &st);
359360
ce->ce_flags |= CE_UPDATE_IN_BASE;
361+
mark_fsmonitor_invalid(state->istate, ce);
360362
state->istate->cache_changed |= CE_ENTRY_CHANGED;
361363
}
362364
return 0;

environment.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ int protect_hfs = PROTECT_HFS_DEFAULT;
7676
#define PROTECT_NTFS_DEFAULT 0
7777
#endif
7878
int protect_ntfs = PROTECT_NTFS_DEFAULT;
79+
const char *core_fsmonitor;
7980

8081
/*
8182
* The character that begins a commented line in user-editable file

0 commit comments

Comments
 (0)