#define USE_THE_REPOSITORY_VARIABLE #include "git-compat-util.h" #include "config.h" #include "dir.h" #include "environment.h" #include "ewah/ewok.h" #include "fsmonitor.h" #include "fsmonitor-ipc.h" #include "name-hash.h" #include "repository.h" #include "run-command.h" #include "strbuf.h" #include "trace2.h" #define INDEX_EXTENSION_VERSION1 … #define INDEX_EXTENSION_VERSION2 … #define HOOK_INTERFACE_VERSION1 … #define HOOK_INTERFACE_VERSION2 … struct trace_key trace_fsmonitor = …; static void assert_index_minimum(struct index_state *istate, size_t pos) { … } static void fsmonitor_ewah_callback(size_t pos, void *is) { … } static int fsmonitor_hook_version(void) { … } int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz) { … } void fill_fsmonitor_bitmap(struct index_state *istate) { … } void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate) { … } /* * Call the query-fsmonitor hook passing the last update token of the saved results. */ static int query_fsmonitor_hook(struct repository *r, int version, const char *last_update, struct strbuf *query_result) { … } /* * Invalidate the FSM bit on this CE. This is like mark_fsmonitor_invalid() * but we've already handled the untracked-cache, so let's not repeat that * work. This also lets us have a different trace message so that we can * see everything that was done as part of the refresh-callback. */ static void invalidate_ce_fsm(struct cache_entry *ce) { … } static size_t handle_path_with_trailing_slash( struct index_state *istate, const char *name, int pos); /* * Use the name-hash to do a case-insensitive cache-entry lookup with * the pathname and invalidate the cache-entry. * * Returns the number of cache-entries that we invalidated. */ static size_t handle_using_name_hash_icase( struct index_state *istate, const char *name) { … } /* * Use the dir-name-hash to find the correct-case spelling of the * directory. Use the canonical spelling to invalidate all of the * cache-entries within the matching cone. * * Returns the number of cache-entries that we invalidated. */ static size_t handle_using_dir_name_hash_icase( struct index_state *istate, const char *name) { … } /* * The daemon sent an observed pathname without a trailing slash. * (This is the normal case.) We do not know if it is a tracked or * untracked file, a sparse-directory, or a populated directory (on a * platform such as Windows where FSEvents are not qualified). * * The pathname contains the observed case reported by the FS. We * do not know it is case-correct or -incorrect. * * Assume it is case-correct and try an exact match. * * Return the number of cache-entries that we invalidated. */ static size_t handle_path_without_trailing_slash( struct index_state *istate, const char *name, int pos) { … } /* * The daemon can decorate directory events, such as a move or rename, * by adding a trailing slash to the observed name. Use this to * explicitly invalidate the entire cone under that directory. * * The daemon can only reliably do that if the OS FSEvent contains * sufficient information in the event. * * macOS FSEvents have enough information. * * Other platforms may or may not be able to do it (and it might * depend on the type of event (for example, a daemon could lstat() an * observed pathname after a rename, but not after a delete)). * * If we find an exact match in the index for a path with a trailing * slash, it means that we matched a sparse-index directory in a * cone-mode sparse-checkout (since that's the only time we have * directories in the index). We should never see this in practice * (because sparse directories should not be present and therefore * not generating FS events). Either way, we can treat them in the * same way and just invalidate the cache-entry and the untracked * cache (and in this case, the forward cache-entry scan won't find * anything and it doesn't hurt to let it run). * * Return the number of cache-entries that we invalidated. We will * use this later to determine if we need to attempt a second * case-insensitive search on case-insensitive file systems. That is, * if the search using the observed-case in the FSEvent yields any * results, we assume the prefix is case-correct. If there are no * matches, we still don't know if the observed path is simply * untracked or case-incorrect. */ static size_t handle_path_with_trailing_slash( struct index_state *istate, const char *name, int pos) { … } static void fsmonitor_refresh_callback(struct index_state *istate, char *name) { … } /* * The number of pathnames that we need to receive from FSMonitor * before we force the index to be updated. * * Note that any pathname within the set of received paths MAY cause * cache-entry or istate flag bits to be updated and thus cause the * index to be updated on disk. * * However, the response may contain many paths (such as ignored * paths) that will not update any flag bits. And thus not force the * index to be updated. (This is fine and normal.) It also means * that the token will not be updated in the FSMonitor index * extension. So the next Git command will find the same token in the * index, make the same token-relative request, and receive the same * response (plus any newly changed paths). If this response is large * (and continues to grow), performance could be impacted. * * For example, if the user runs a build and it writes 100K object * files but doesn't modify any source files, the index would not need * to be updated. The FSMonitor response (after the build and * relative to a pre-build token) might be 5MB. Each subsequent Git * command will receive that same 100K/5MB response until something * causes the index to be updated. And `refresh_fsmonitor()` will * have to iterate over those 100K paths each time. * * Performance could be improved if we optionally force update the * index after a very large response and get an updated token into * the FSMonitor index extension. This should allow subsequent * commands to get smaller and more current responses. * * The value chosen here does not need to be precise. The index * will be updated automatically the first time the user touches * a tracked file and causes a command like `git status` to * update an mtime to be updated and/or set a flag bit. */ static int fsmonitor_force_update_threshold = …; void refresh_fsmonitor(struct index_state *istate) { … } /* * The caller wants to turn on FSMonitor. And when the caller writes * the index to disk, a FSMonitor extension should be included. This * requires that `istate->fsmonitor_last_update` not be NULL. But we * have not actually talked to a FSMonitor process yet, so we don't * have an initial value for this field. * * For a protocol V1 FSMonitor process, this field is a formatted * "nanoseconds since epoch" field. However, for a protocol V2 * FSMonitor process, this field is an opaque token. * * Historically, `add_fsmonitor()` has initialized this field to the * current time for protocol V1 processes. There are lots of race * conditions here, but that code has shipped... * * The only true solution is to use a V2 FSMonitor and get a current * or default token value (that it understands), but we cannot do that * until we have actually talked to an instance of the FSMonitor process * (but the protocol requires that we send a token first...). * * For simplicity, just initialize like we have a V1 process and require * that V2 processes adapt. */ static void initialize_fsmonitor_last_update(struct index_state *istate) { … } void add_fsmonitor(struct index_state *istate) { … } void remove_fsmonitor(struct index_state *istate) { … } void tweak_fsmonitor(struct index_state *istate) { … }