From 69b6ffc4c53d578800274993b5222fa5a7699f21 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 28 Mar 2014 14:02:21 -0700 Subject: [PATCH] Make a real submodule cache object This takes the old submodule cache which was just a git_strmap and makes a real git_submodule_cache object that can contain other things like a lock and timestamp-ish data to control refreshing of submodule info. --- src/repository.c | 2 +- src/repository.h | 9 +- src/submodule.c | 347 +++++++++++++++++++++++++++++------------------ src/submodule.h | 29 ++++ 4 files changed, 244 insertions(+), 143 deletions(-) diff --git a/src/repository.c b/src/repository.c index fccc16faa..49d1bc63e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -93,6 +93,7 @@ void git_repository__cleanup(git_repository *repo) git_cache_clear(&repo->objects); git_attr_cache_flush(repo); + git_submodule_cache_free(repo); set_config(repo, NULL); set_index(repo, NULL); @@ -108,7 +109,6 @@ void git_repository_free(git_repository *repo) git_repository__cleanup(repo); git_cache_free(&repo->objects); - git_submodule_config_free(repo); git_diff_driver_registry_free(repo->diff_drivers); repo->diff_drivers = NULL; diff --git a/src/repository.h b/src/repository.h index 4e79714f1..99923b63b 100644 --- a/src/repository.h +++ b/src/repository.h @@ -19,7 +19,7 @@ #include "buffer.h" #include "object.h" #include "attrcache.h" -#include "strmap.h" +#include "submodule.h" #include "diff_driver.h" #define DOT_GIT ".git" @@ -105,10 +105,10 @@ struct git_repository { git_refdb *_refdb; git_config *_config; git_index *_index; + git_submodule_cache *_submodules; git_cache objects; git_attr_cache attrcache; - git_strmap *submodules; git_diff_driver_registry *diff_drivers; char *path_repository; @@ -149,11 +149,6 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo); int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar); void git_repository__cvar_cache_clear(git_repository *repo); -/* - * Submodule cache - */ -extern void git_submodule_config_free(git_repository *repo); - GIT_INLINE(int) git_repository__ensure_not_bare( git_repository *repo, const char *operation_name) diff --git a/src/submodule.c b/src/submodule.c index 69791ef58..94bed8a6f 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -77,11 +77,13 @@ __KHASH_IMPL( str, static kh_inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash); -static int load_submodule_config(git_repository *repo, bool reload); -static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *); +static int submodule_cache_init(git_repository *repo, bool refresh); +static void submodule_cache_free(git_submodule_cache *cache); + +static git_config_backend *open_gitmodules(git_submodule_cache *, bool); static int lookup_head_remote_key(git_buf *key, git_repository *repo); static int lookup_head_remote(git_buf *url, git_repository *repo); -static int submodule_get(git_submodule **, git_repository *, const char *, const char *); +static int submodule_get(git_submodule **, git_submodule_cache *, const char *, const char *); static int submodule_load_from_config(const git_config_entry *, void *); static int submodule_load_from_wd_lite(git_submodule *); static void submodule_get_index_status(unsigned int *, git_submodule *); @@ -102,7 +104,7 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix) /* lookup submodule or return ENOTFOUND if it doesn't exist */ static int submodule_lookup( git_submodule **out, - git_strmap *cache, + git_submodule_cache *cache, const char *name, const char *alternate) { @@ -110,18 +112,18 @@ static int submodule_lookup( /* lock cache */ - pos = git_strmap_lookup_index(cache, name); + pos = git_strmap_lookup_index(cache->submodules, name); - if (!git_strmap_valid_index(cache, pos) && alternate) - pos = git_strmap_lookup_index(cache, alternate); + if (!git_strmap_valid_index(cache->submodules, pos) && alternate) + pos = git_strmap_lookup_index(cache->submodules, alternate); - if (!git_strmap_valid_index(cache, pos)) { + if (!git_strmap_valid_index(cache->submodules, pos)) { /* unlock cache */ return GIT_ENOTFOUND; /* don't set error - caller will cope */ } if (out != NULL) { - git_submodule *sm = git_strmap_value_at(cache, pos); + git_submodule *sm = git_strmap_value_at(cache->submodules, pos); GIT_REFCOUNT_INC(sm); *out = sm; } @@ -139,17 +141,45 @@ bool git_submodule__is_submodule(git_repository *repo, const char *name) { git_strmap *map; - if (load_submodule_config(repo, false) < 0) { + if (submodule_cache_init(repo, false) < 0) { giterr_clear(); return false; } - if (!(map = repo->submodules)) + if (!repo->_submodules || !(map = repo->_submodules->submodules)) return false; return git_strmap_valid_index(map, git_strmap_lookup_index(map, name)); } +static void submodule_set_lookup_error(int error, const char *name) +{ + if (!error) + return; + + giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? + "No submodule named '%s'" : + "Submodule '%s' has not been added yet", name); +} + +int git_submodule__lookup( + git_submodule **out, /* NULL if user only wants to test existence */ + git_repository *repo, + const char *name) /* trailing slash is allowed */ +{ + int error; + + assert(repo && name); + + if ((error = submodule_cache_init(repo, false)) < 0) + return error; + + if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) + submodule_set_lookup_error(error, name); + + return error; +} + int git_submodule_lookup( git_submodule **out, /* NULL if user only wants to test existence */ git_repository *repo, @@ -159,10 +189,10 @@ int git_submodule_lookup( assert(repo && name); - if ((error = load_submodule_config(repo, false)) < 0) + if ((error = submodule_cache_init(repo, true)) < 0) return error; - if ((error = submodule_lookup(out, repo->submodules, name, NULL)) < 0) { + if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) { /* check if a plausible submodule exists at path */ if (git_repository_workdir(repo)) { @@ -178,9 +208,7 @@ int git_submodule_lookup( git_buf_free(&path); } - giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? - "No submodule named '%s'" : - "Submodule '%s' has not been added yet", name); + submodule_set_lookup_error(error, name); } return error; @@ -198,10 +226,10 @@ int git_submodule_foreach( assert(repo && callback); - if ((error = load_submodule_config(repo, true)) < 0) + if ((error = submodule_cache_init(repo, true)) < 0) return error; - git_strmap_foreach_value(repo->submodules, sm, { + git_strmap_foreach_value(repo->_submodules->submodules, sm, { /* Usually the following will not come into play - it just prevents * us from issuing a callback twice for a submodule where the name * and path are not the same. @@ -224,24 +252,14 @@ int git_submodule_foreach( return error; } -void git_submodule_config_free(git_repository *repo) +void git_submodule_cache_free(git_repository *repo) { - git_strmap *smcfg; - git_submodule *sm; + git_submodule_cache *cache; assert(repo); - smcfg = repo->submodules; - repo->submodules = NULL; - - if (smcfg == NULL) - return; - - git_strmap_foreach_value(smcfg, sm, { - sm->repo = NULL; /* disconnect from repo */; - git_submodule_free(sm); - }); - git_strmap_free(smcfg); + if ((cache = git__swap(repo->_submodules, NULL)) != NULL) + submodule_cache_free(cache); } int git_submodule_add_setup( @@ -262,12 +280,11 @@ int git_submodule_add_setup( /* see if there is already an entry for this submodule */ - if (git_submodule_lookup(&sm, repo, path) < 0) + if (git_submodule_lookup(NULL, repo, path) < 0) giterr_clear(); else { - git_submodule_free(sm); giterr_set(GITERR_SUBMODULE, - "Attempt to add a submodule that already exists"); + "Attempt to add submodule '%s' that already exists", path); return GIT_EEXISTS; } @@ -288,7 +305,7 @@ int git_submodule_add_setup( /* update .gitmodules */ - if ((mods = open_gitmodules(repo, true, NULL)) == NULL) { + if ((mods = open_gitmodules(repo->_submodules, true)) == NULL) { giterr_set(GITERR_SUBMODULE, "Adding submodules to a bare repository is not supported (for now)"); return -1; @@ -348,7 +365,7 @@ int git_submodule_add_setup( /* add submodule to hash and "reload" it */ - if (!(error = submodule_get(&sm, repo, path, NULL)) && + if (!(error = submodule_get(&sm, repo->_submodules, path, NULL)) && !(error = git_submodule_reload(sm, false))) error = git_submodule_init(sm, false); @@ -489,7 +506,7 @@ int git_submodule_save(git_submodule *submodule) assert(submodule); - mods = open_gitmodules(submodule->repo, true, NULL); + mods = open_gitmodules(submodule->repo->_submodules, true); if (!mods) { giterr_set(GITERR_SUBMODULE, "Adding submodules to a bare repository is not supported (for now)"); @@ -862,7 +879,7 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm) } static void submodule_cache_remove_item( - git_strmap *cache, + git_strmap *map, const char *name, git_submodule *expected, bool free_after_remove) @@ -870,21 +887,21 @@ static void submodule_cache_remove_item( khiter_t pos; git_submodule *found; - if (!cache) + if (!map) return; - pos = git_strmap_lookup_index(cache, name); + pos = git_strmap_lookup_index(map, name); - if (!git_strmap_valid_index(cache, pos)) + if (!git_strmap_valid_index(map, pos)) return; - found = git_strmap_value_at(cache, pos); + found = git_strmap_value_at(map, pos); if (expected && found != expected) return; - git_strmap_set_value_at(cache, pos, NULL); - git_strmap_delete_at(cache, pos); + git_strmap_set_value_at(map, pos, NULL); + git_strmap_delete_at(map, pos); if (free_after_remove) git_submodule_free(found); @@ -894,27 +911,31 @@ int git_submodule_reload_all(git_repository *repo, int force) { int error = 0; git_submodule *sm; + git_strmap *map; GIT_UNUSED(force); assert(repo); - if (repo->submodules) - git_strmap_foreach_value(repo->submodules, sm, { sm->flags = 0; }); + if (repo->_submodules) { + map = repo->_submodules->submodules; + git_strmap_foreach_value(map, sm, { sm->flags = 0; }); + } - if ((error = load_submodule_config(repo, true)) < 0) + if ((error = submodule_cache_init(repo, true)) < 0) return error; - git_strmap_foreach_value(repo->submodules, sm, { - git_strmap *cache = repo->submodules; + if (!repo->_submodules || !(map = repo->_submodules->submodules)) + return error; + git_strmap_foreach_value(map, sm, { if (sm && (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS) == 0) { /* we must check path != name before first remove, in case * that call frees the submodule */ bool free_as_path = (sm->path != sm->name); - submodule_cache_remove_item(cache, sm->name, sm, true); + submodule_cache_remove_item(map, sm->name, sm, true); if (free_as_path) - submodule_cache_remove_item(cache, sm->path, sm, true); + submodule_cache_remove_item(map, sm->path, sm, true); } }); @@ -995,41 +1016,44 @@ static int submodule_update_head(git_submodule *submodule) } -int git_submodule_reload(git_submodule *submodule, int force) +int git_submodule_reload(git_submodule *sm, int force) { int error = 0; git_config_backend *mods; + git_submodule_cache *cache; GIT_UNUSED(force); - assert(submodule); + assert(sm); + + cache = sm->repo->_submodules; /* refresh index data */ - if ((error = submodule_update_index(submodule)) < 0) + if ((error = submodule_update_index(sm)) < 0) return error; /* refresh HEAD tree data */ - if ((error = submodule_update_head(submodule)) < 0) + if ((error = submodule_update_head(sm)) < 0) return error; /* done if bare */ - if (git_repository_is_bare(submodule->repo)) + if (git_repository_is_bare(sm->repo)) return error; /* refresh config data */ - mods = open_gitmodules(submodule->repo, false, NULL); + mods = open_gitmodules(cache, false); if (mods != NULL) { git_buf path = GIT_BUF_INIT; git_buf_sets(&path, "submodule\\."); - git_buf_text_puts_escape_regex(&path, submodule->name); + git_buf_text_puts_escape_regex(&path, sm->name); git_buf_puts(&path, ".*"); if (git_buf_oom(&path)) error = -1; else error = git_config_file_foreach_match( - mods, path.ptr, submodule_load_from_config, submodule->repo); + mods, path.ptr, submodule_load_from_config, cache); git_buf_free(&path); git_config_file_free(mods); @@ -1039,9 +1063,9 @@ int git_submodule_reload(git_submodule *submodule, int force) } /* refresh wd data */ - submodule->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; + sm->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; - return submodule_load_from_wd_lite(submodule); + return submodule_load_from_wd_lite(sm); } static void submodule_copy_oid_maybe( @@ -1135,34 +1159,35 @@ int git_submodule_location(unsigned int *location, git_submodule *sm) * INTERNAL FUNCTIONS */ -static git_submodule *submodule_alloc(git_repository *repo, const char *name) +static int submodule_alloc( + git_submodule **out, git_submodule_cache *cache, const char *name) { size_t namelen; git_submodule *sm; if (!name || !(namelen = strlen(name))) { giterr_set(GITERR_SUBMODULE, "Invalid submodule name"); - return NULL; + return -1; } sm = git__calloc(1, sizeof(git_submodule)); - if (sm == NULL) - return NULL; + GITERR_CHECK_ALLOC(sm); sm->name = sm->path = git__strdup(name); if (!sm->name) { git__free(sm); - return NULL; + return -1; } GIT_REFCOUNT_INC(sm); sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE; sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT; sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO; - sm->repo = repo; + sm->repo = cache->repo; sm->branch = NULL; - return sm; + *out = sm; + return 0; } static void submodule_release(git_submodule *sm) @@ -1170,11 +1195,15 @@ static void submodule_release(git_submodule *sm) if (!sm) return; - if (sm->repo) { - git_strmap *cache = sm->repo->submodules; - submodule_cache_remove_item(cache, sm->name, sm, false); - if (sm->path != sm->name) - submodule_cache_remove_item(cache, sm->path, sm, false); + if (sm->repo && sm->repo->_submodules) { + git_strmap *map = sm->repo->_submodules->submodules; + bool free_as_path = (sm->path != sm->name); + + sm->repo = NULL; + + submodule_cache_remove_item(map, sm->name, sm, false); + if (free_as_path) + submodule_cache_remove_item(map, sm->path, sm, false); } if (sm->path != sm->name) @@ -1195,40 +1224,39 @@ void git_submodule_free(git_submodule *sm) static int submodule_get( git_submodule **out, - git_repository *repo, + git_submodule_cache *cache, const char *name, const char *alternate) { int error = 0; - git_strmap *smcfg = repo->submodules; khiter_t pos; git_submodule *sm; - pos = git_strmap_lookup_index(smcfg, name); + pos = git_strmap_lookup_index(cache->submodules, name); - if (!git_strmap_valid_index(smcfg, pos) && alternate) - pos = git_strmap_lookup_index(smcfg, alternate); + if (!git_strmap_valid_index(cache->submodules, pos) && alternate) + pos = git_strmap_lookup_index(cache->submodules, alternate); - if (!git_strmap_valid_index(smcfg, pos)) { - sm = submodule_alloc(repo, name); - GITERR_CHECK_ALLOC(sm); + if (!git_strmap_valid_index(cache->submodules, pos)) { + if ((error = submodule_alloc(&sm, cache, name)) < 0) + return error; /* insert value at name - if another thread beats us to it, then use * their record and release our own. */ - pos = kh_put(str, smcfg, sm->name, &error); + pos = kh_put(str, cache->submodules, sm->name, &error); if (error < 0) goto done; else if (error == 0) { git_submodule_free(sm); - sm = git_strmap_value_at(smcfg, pos); + sm = git_strmap_value_at(cache->submodules, pos); } else { error = 0; - git_strmap_set_value_at(smcfg, pos, sm); + git_strmap_set_value_at(cache->submodules, pos, sm); } } else { - sm = git_strmap_value_at(smcfg, pos); + sm = git_strmap_value_at(cache->submodules, pos); } done: @@ -1294,8 +1322,7 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value) static int submodule_load_from_config( const git_config_entry *entry, void *payload) { - git_repository *repo = payload; - git_strmap *smcfg = repo->submodules; + git_submodule_cache *cache = payload; const char *namestart, *property, *alternate = NULL; const char *key = entry->name, *value = entry->value, *path; git_buf name = GIT_BUF_INIT; @@ -1315,7 +1342,7 @@ static int submodule_load_from_config( path = !strcasecmp(property, "path") ? value : NULL; if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 || - (error = submodule_get(&sm, repo, name.ptr, path)) < 0) + (error = submodule_get(&sm, cache, name.ptr, path)) < 0) goto done; sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; @@ -1341,7 +1368,7 @@ static int submodule_load_from_config( /* Found a alternate key for the submodule */ if (alternate) { void *old_sm = NULL; - git_strmap_insert2(smcfg, alternate, sm, old_sm, error); + git_strmap_insert2(cache->submodules, alternate, sm, old_sm, error); if (error < 0) goto done; @@ -1423,36 +1450,35 @@ static int submodule_load_from_wd_lite(git_submodule *sm) return 0; } -static int load_submodule_config_from_index( - git_repository *repo, git_oid *gitmodules_oid) +static int submodule_cache_refresh_from_index(git_submodule_cache *cache) { int error; git_index *index; git_iterator *i; const git_index_entry *entry; - if ((error = git_repository_index__weakptr(&index, repo)) < 0 || + if ((error = git_repository_index__weakptr(&index, cache->repo)) < 0 || (error = git_iterator_for_index(&i, index, 0, NULL, NULL)) < 0) return error; while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path); + khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path); git_submodule *sm; - if (git_strmap_valid_index(repo->submodules, pos)) { - sm = git_strmap_value_at(repo->submodules, pos); + if (git_strmap_valid_index(cache->submodules, pos)) { + sm = git_strmap_value_at(cache->submodules, pos); if (S_ISGITLINK(entry->mode)) submodule_update_from_index_entry(sm, entry); else sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get(&sm, repo, entry->path, NULL)) { + if (!submodule_get(&sm, cache, entry->path, NULL)) { submodule_update_from_index_entry(sm, entry); git_submodule_free(sm); } } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0) - git_oid_cpy(gitmodules_oid, &entry->id); + git_oid_cpy(&cache->gitmodules_id, &entry->id); } if (error == GIT_ITEROVER) @@ -1463,8 +1489,7 @@ static int load_submodule_config_from_index( return error; } -static int load_submodule_config_from_head( - git_repository *repo, git_oid *gitmodules_oid) +static int submodule_cache_refresh_from_head(git_submodule_cache *cache) { int error; git_tree *head; @@ -1472,7 +1497,7 @@ static int load_submodule_config_from_head( const git_index_entry *entry; /* if we can't look up current head, then there's no submodule in it */ - if (git_repository_head_tree(&head, repo) < 0) { + if (git_repository_head_tree(&head, cache->repo) < 0) { giterr_clear(); return 0; } @@ -1483,11 +1508,11 @@ static int load_submodule_config_from_head( } while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path); + khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path); git_submodule *sm; - if (git_strmap_valid_index(repo->submodules, pos)) { - sm = git_strmap_value_at(repo->submodules, pos); + if (git_strmap_valid_index(cache->submodules, pos)) { + sm = git_strmap_value_at(cache->submodules, pos); if (S_ISGITLINK(entry->mode)) submodule_update_from_head_data( @@ -1495,14 +1520,14 @@ static int load_submodule_config_from_head( else sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get(&sm, repo, entry->path, NULL)) { + if (!submodule_get(&sm, cache, entry->path, NULL)) { submodule_update_from_head_data( sm, entry->mode, &entry->id); git_submodule_free(sm); } } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0 && - git_oid_iszero(gitmodules_oid)) { - git_oid_cpy(gitmodules_oid, &entry->id); + git_oid_iszero(&cache->gitmodules_id)) { + git_oid_cpy(&cache->gitmodules_id, &entry->id); } } @@ -1516,11 +1541,10 @@ static int load_submodule_config_from_head( } static git_config_backend *open_gitmodules( - git_repository *repo, - bool okay_to_create, - const git_oid *gitmodules_oid) + git_submodule_cache *cache, + bool okay_to_create) { - const char *workdir = git_repository_workdir(repo); + const char *workdir = git_repository_workdir(cache->repo); git_buf path = GIT_BUF_INIT; git_config_backend *mods = NULL; @@ -1540,7 +1564,7 @@ static git_config_backend *open_gitmodules( } } - if (!mods && gitmodules_oid && !git_oid_iszero(gitmodules_oid)) { + if (!mods && !git_oid_iszero(&cache->gitmodules_id)) { /* TODO: Retrieve .gitmodules content from ODB */ /* Should we actually do this? Core git does not, but it means you @@ -1553,53 +1577,86 @@ static git_config_backend *open_gitmodules( return mods; } -static int load_submodule_config(git_repository *repo, bool reload) +static void submodule_cache_free(git_submodule_cache *cache) +{ + git_submodule *sm; + + if (!cache) + return; + + git_strmap_foreach_value(cache->submodules, sm, { + sm->repo = NULL; /* disconnect from repo */ + git_submodule_free(sm); + }); + git_strmap_free(cache->submodules); + + git_buf_free(&cache->gitmodules_path); + git_mutex_free(&cache->lock); + git__free(cache); +} + +static int submodule_cache_alloc( + git_submodule_cache **out, git_repository *repo) +{ + git_submodule_cache *cache = git__calloc(1, sizeof(git_submodule_cache)); + GITERR_CHECK_ALLOC(cache); + + if (git_mutex_init(&cache->lock) < 0) { + giterr_set(GITERR_OS, "Unable to initialize submodule cache lock"); + git__free(cache); + return -1; + } + + cache->submodules = git_strmap_alloc(); + if (!cache->submodules) { + submodule_cache_free(cache); + return -1; + } + + cache->repo = repo; + git_buf_init(&cache->gitmodules_path, 0); + + *out = cache; + return 0; +} + +static int submodule_cache_refresh(git_submodule_cache *cache, bool force) { int error; - git_oid gitmodules_oid; git_config_backend *mods = NULL; - if (!reload && repo->submodules) - return 0; - - memset(&gitmodules_oid, 0, sizeof(gitmodules_oid)); - - /* Submodule data is kept in a hashtable keyed by both name and path. - * These are usually the same, but that is not guaranteed. - */ - if (!repo->submodules) { - repo->submodules = git_strmap_alloc(); - GITERR_CHECK_ALLOC(repo->submodules); - } + GIT_UNUSED(force); /* TODO: only do the following if the sources appear modified */ + memset(&cache->gitmodules_id, 0, sizeof(cache->gitmodules_id)); + /* add submodule information from index */ - if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0) + if ((error = submodule_cache_refresh_from_index(cache)) < 0) goto cleanup; /* add submodule information from HEAD */ - if ((error = load_submodule_config_from_head(repo, &gitmodules_oid)) < 0) + if ((error = submodule_cache_refresh_from_head(cache)) < 0) goto cleanup; /* add submodule information from .gitmodules */ - if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL && + if ((mods = open_gitmodules(cache, false)) != NULL && (error = git_config_file_foreach( - mods, submodule_load_from_config, repo)) < 0) + mods, submodule_load_from_config, cache)) < 0) goto cleanup; /* shallow scan submodules in work tree */ - if (!git_repository_is_bare(repo)) { + if (!git_repository_is_bare(cache->repo)) { git_submodule *sm; - git_strmap_foreach_value(repo->submodules, sm, { + git_strmap_foreach_value(cache->submodules, sm, { sm->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; }); - git_strmap_foreach_value(repo->submodules, sm, { + git_strmap_foreach_value(cache->submodules, sm, { submodule_load_from_wd_lite(sm); }); } @@ -1608,8 +1665,28 @@ cleanup: if (mods != NULL) git_config_file_free(mods); - if (error) - git_submodule_config_free(repo); + /* TODO: if we got an error, mark submodule config as invalid? */ + + return error; +} + +static int submodule_cache_init(git_repository *repo, bool refresh) +{ + int error = 0; + git_submodule_cache *cache = NULL; + + /* if submodules already exist, just refresh as requested */ + if (repo->_submodules) + return refresh ? submodule_cache_refresh(repo->_submodules, false) : 0; + + /* otherwise create a new cache, load it, and atomically swap it in */ + if (!(error = submodule_cache_alloc(&cache, repo)) && + !(error = submodule_cache_refresh(cache, true))) + cache = git__compare_and_swap(&repo->_submodules, NULL, cache); + + /* might have raced with another thread to set cache, so free if needed */ + if (cache) + submodule_cache_free(cache); return error; } diff --git a/src/submodule.h b/src/submodule.h index 1c41897e3..4b9828a9c 100644 --- a/src/submodule.h +++ b/src/submodule.h @@ -99,6 +99,30 @@ struct git_submodule { git_oid wd_oid; }; +/** + * The git_submodule_cache stores known submodules along with timestamps, + * etc. about when they were loaded + */ +typedef struct { + git_repository *repo; + git_strmap *submodules; + git_mutex lock; + + /* cache invalidation data */ + git_oid head_id; + git_futils_filestamp index_stamp; + git_buf gitmodules_path; + git_futils_filestamp gitmodules_stamp; + git_oid gitmodules_id; + git_futils_filestamp config_stamp; +} git_submodule_cache; + +/* Force revalidation of submodule data cache (alloc as needed) */ +extern int git_submodule_cache_refresh(git_repository *repo); + +/* Release all submodules */ +extern void git_submodule_cache_free(git_repository *repo); + /* Additional flags on top of public GIT_SUBMODULE_STATUS values */ enum { GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20), @@ -122,6 +146,10 @@ enum { /* Internal submodule check does not attempt to refresh cached data */ extern bool git_submodule__is_submodule(git_repository *repo, const char *name); +/* Internal lookup does not attempt to refresh cached data */ +extern int git_submodule__lookup( + git_submodule **out, git_repository *repo, const char *path); + /* Internal status fn returns status and optionally the various OIDs */ extern int git_submodule__status( unsigned int *out_status, @@ -143,5 +171,6 @@ extern int git_submodule_parse_update( extern const char *git_submodule_ignore_to_str(git_submodule_ignore_t); extern const char *git_submodule_update_to_str(git_submodule_update_t); +extern const char *git_submodule_recurse_to_str(git_submodule_recurse_t); #endif