Skip to content

Commit a8f07ec

Browse files
committed
Merge branch 'bp/fsmonitor' into pu
We learned to talk to watchman to speed up "git status". * bp/fsmonitor: perf: add a performance test for core.fsmonitor fsmonitor: add a sample query-fsmonitor hook script for Watchman fsmonitor: add documentation for the fsmonitor extension fsmonitor: add test cases for fsmonitor extension fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files. dir: make lookup_untracked() available outside of dir.c bswap: add 64 bit endianness helper get_be64
2 parents a0d3f67 + 774fc5b commit a8f07ec

19 files changed

+652
-3
lines changed

Documentation/config.txt

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

413+
core.fsmonitor::
414+
If set to true, call the query-fsmonitor hook proc which will
415+
identify all files that may have had changes since the last
416+
request. This information is used to speed up operations like
417+
'git commit' and 'git status' by limiting what git must scan to
418+
detect changes.
419+
413420
core.trustctime::
414421
If false, the ctime differences between the index and the
415422
working tree are ignored; useful when the inode change time

Documentation/githooks.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,29 @@ non-zero status causes 'git send-email' to abort before sending any
456456
e-mails.
457457

458458

459+
[[query-fsmonitor]]
460+
query-fsmonitor
461+
~~~~~~~~~~~~~~~
462+
463+
This hook is invoked when the configuration option core.fsmonitor is
464+
set and git needs to identify changed or untracked files. It takes
465+
two arguments, a version (currently 1) and the time in elapsed
466+
nanoseconds since midnight, January 1, 1970.
467+
468+
The hook should output to stdout the list of all files in the working
469+
directory that may have changed since the requested time. The logic
470+
should be inclusive so that it does not miss any potential changes.
471+
The paths should be relative to the root of the working directory
472+
and be separated by a single NUL.
473+
474+
Git will limit what files it checks for changes as well as which
475+
directories are checked for untracked files based on the path names
476+
given.
477+
478+
The exit status determines whether git will use the data from the
479+
hook to limit its search. On error, it will fall back to verifying
480+
all files and folders.
481+
459482
GIT
460483
---
461484
Part of the linkgit:git[1] suite

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 query-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_DIRTY bitmap.
314+
315+
- An ewah bitmap, the n-th bit indicates whether the n-th index entry
316+
is CE_FSMONITOR_DIRTY.

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,7 @@ LIB_OBJS += ewah/ewah_rlw.o
784784
LIB_OBJS += exec_cmd.o
785785
LIB_OBJS += fetch-pack.o
786786
LIB_OBJS += fsck.o
787+
LIB_OBJS += fsmonitor.o
787788
LIB_OBJS += gettext.o
788789
LIB_OBJS += gpg-interface.o
789790
LIB_OBJS += graph.o

builtin/update-index.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)
232232
else
233233
active_cache[pos]->ce_flags &= ~flag;
234234
active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
235+
active_cache[pos]->ce_flags |= CE_FSMONITOR_DIRTY;
235236
cache_tree_invalidate_path(&the_index, path);
236237
active_cache_changed |= CE_ENTRY_CHANGED;
237238
return 0;

cache.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ struct cache_entry {
201201
#define CE_ADDED (1 << 19)
202202

203203
#define CE_HASHED (1 << 20)
204+
#define CE_FSMONITOR_DIRTY (1 << 21)
204205
#define CE_WT_REMOVE (1 << 22) /* remove in work directory */
205206
#define CE_CONFLICTED (1 << 23)
206207

@@ -324,6 +325,7 @@ static inline unsigned int canon_mode(unsigned int mode)
324325
#define CACHE_TREE_CHANGED (1 << 5)
325326
#define SPLIT_INDEX_ORDERED (1 << 6)
326327
#define UNTRACKED_CHANGED (1 << 7)
328+
#define FSMONITOR_CHANGED (1 << 8)
327329

328330
struct split_index;
329331
struct untracked_cache;
@@ -342,6 +344,8 @@ struct index_state {
342344
struct hashmap dir_hash;
343345
unsigned char sha1[20];
344346
struct untracked_cache *untracked;
347+
uint64_t fsmonitor_last_update;
348+
struct ewah_bitmap *fsmonitor_dirty;
345349
};
346350

347351
extern struct index_state the_index;
@@ -767,6 +771,7 @@ extern int precomposed_unicode;
767771
extern int protect_hfs;
768772
extern int protect_ntfs;
769773
extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
774+
extern int core_fsmonitor;
770775

771776
/*
772777
* Include broken refs in all ref iterations, which will

compat/bswap.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ 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)
162163

163164
#else
@@ -170,6 +171,9 @@ static inline uint64_t git_bswap64(uint64_t x)
170171
(*((unsigned char *)(p) + 1) << 16) | \
171172
(*((unsigned char *)(p) + 2) << 8) | \
172173
(*((unsigned char *)(p) + 3) << 0) )
174+
#define get_be64(p) ( \
175+
((uint64_t)get_be32((unsigned char *)(p) + 0) << 32) | \
176+
((uint64_t)get_be32((unsigned char *)(p) + 4) << 0)
173177
#define put_be32(p, v) do { \
174178
unsigned int __v = (v); \
175179
*((unsigned char *)(p) + 0) = __v >> 24; \

config.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,11 @@ static int git_default_core_config(const char *var, const char *value)
12481248
return 0;
12491249
}
12501250

1251+
if (!strcmp(var, "core.fsmonitor")) {
1252+
core_fsmonitor = git_config_bool(var, value);
1253+
return 0;
1254+
}
1255+
12511256
/* Add other config variables here and to Documentation/config.txt. */
12521257
return 0;
12531258
}

dir.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "utf8.h"
1818
#include "varint.h"
1919
#include "ewah/ewok.h"
20+
#include "fsmonitor.h"
2021

2122
/*
2223
* Tells read_directory_recursive how a file or directory should be treated.
@@ -666,7 +667,7 @@ static void trim_trailing_spaces(char *buf)
666667
*
667668
* If "name" has the trailing slash, it'll be excluded in the search.
668669
*/
669-
static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
670+
struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
670671
struct untracked_cache_dir *dir,
671672
const char *name, int len)
672673
{
@@ -1680,6 +1681,18 @@ static int valid_cached_dir(struct dir_struct *dir,
16801681
if (!untracked)
16811682
return 0;
16821683

1684+
refresh_by_fsmonitor(&the_index);
1685+
if (dir->untracked->use_fsmonitor) {
1686+
/*
1687+
* With fsmonitor, we can trust the untracked cache's
1688+
* valid field.
1689+
*/
1690+
if (untracked->valid)
1691+
goto skip_stat;
1692+
else
1693+
invalidate_directory(dir->untracked, untracked);
1694+
}
1695+
16831696
if (stat(path->len ? path->buf : ".", &st)) {
16841697
invalidate_directory(dir->untracked, untracked);
16851698
memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
@@ -1693,6 +1706,7 @@ static int valid_cached_dir(struct dir_struct *dir,
16931706
return 0;
16941707
}
16951708

1709+
skip_stat:
16961710
if (untracked->check_only != !!check_only) {
16971711
invalidate_directory(dir->untracked, untracked);
16981712
return 0;

dir.h

Lines changed: 5 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 {
@@ -354,4 +356,7 @@ extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git
354356
extern void relocate_gitdir(const char *path,
355357
const char *old_git_dir,
356358
const char *new_git_dir);
359+
struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
360+
struct untracked_cache_dir *dir,
361+
const char *name, int len);
357362
#endif

entry.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ static int write_entry(struct cache_entry *ce,
322322
lstat(ce->name, &st);
323323
fill_stat_cache_info(ce, &st);
324324
ce->ce_flags |= CE_UPDATE_IN_BASE;
325+
ce->ce_flags |= CE_FSMONITOR_DIRTY;
325326
state->istate->cache_changed |= CE_ENTRY_CHANGED;
326327
}
327328
return 0;

environment.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
6464
unsigned long pack_size_limit_cfg;
6565
enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
6666
enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
67+
int core_fsmonitor;
6768

6869
#ifndef PROTECT_HFS_DEFAULT
6970
#define PROTECT_HFS_DEFAULT 0

0 commit comments

Comments
 (0)