diff --git a/src/checkout.c b/src/checkout.c index 2893c63de..dab83c65a 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1860,11 +1860,6 @@ static int checkout_create_submodules( git_diff_delta *delta; size_t i; - /* initial reload of submodules if .gitmodules was changed */ - if (data->reload_submodules && - (error = git_submodule_reload_all(data->repo, 1)) < 0) - return error; - git_vector_foreach(&data->diff->deltas, i, delta) { if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) { /* this has a blocker directory that should only be removed iff @@ -1885,8 +1880,7 @@ static int checkout_create_submodules( } } - /* final reload once submodules have been updated */ - return git_submodule_reload_all(data->repo, 1); + return 0; } static int checkout_lookup_head_tree(git_tree **out, git_repository *repo) diff --git a/src/repository.c b/src/repository.c index c608fa0b8..85c86716e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -109,7 +109,6 @@ 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); diff --git a/src/repository.h b/src/repository.h index 253287368..fd679b483 100644 --- a/src/repository.h +++ b/src/repository.h @@ -121,7 +121,6 @@ struct git_repository { git_refdb *_refdb; git_config *_config; git_index *_index; - git_submodule_cache *_submodules; git_cache objects; git_attr_cache *attrcache; diff --git a/src/submodule.c b/src/submodule.c index 246502e99..9a1485436 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -87,17 +87,16 @@ __KHASH_IMPL( str, static kh_inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash) -static int submodule_cache_init(git_repository *repo, int refresh); -static void submodule_cache_free(git_submodule_cache *cache); - -static git_config_backend *open_gitmodules(git_submodule_cache *, int gitmod); +static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name); +static git_config_backend *open_gitmodules(git_repository *repo, int gitmod); static int get_url_base(git_buf *url, git_repository *repo); static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo); -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 *); static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t); +static void submodule_update_from_index_entry(git_submodule *sm, const git_index_entry *ie); +static void submodule_update_from_head_data(git_submodule *sm, mode_t mode, const git_oid *id); static int submodule_cmp(const void *a, const void *b) { @@ -111,69 +110,10 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix) return git_buf_puts(key, suffix); } -/* lookup submodule or return ENOTFOUND if it doesn't exist */ -static int submodule_lookup( - git_submodule **out, - git_submodule_cache *cache, - const char *name, - const char *alternate) -{ - khiter_t pos; - - /* lock cache */ - - pos = git_strmap_lookup_index(cache->submodules, name); - - if (!git_strmap_valid_index(cache->submodules, pos) && alternate) - pos = git_strmap_lookup_index(cache->submodules, alternate); - - 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->submodules, pos); - GIT_REFCOUNT_INC(sm); - *out = sm; - } - - /* unlock cache */ - - return 0; -} - -/* clear a set of flags on all submodules */ -static void submodule_cache_clear_flags( - git_submodule_cache *cache, uint32_t mask) -{ - git_submodule *sm; - uint32_t inverted_mask = ~mask; - - git_strmap_foreach_value(cache->submodules, sm, { - sm->flags &= inverted_mask; - }); -} - /* * PUBLIC APIS */ -bool git_submodule__is_submodule(git_repository *repo, const char *name) -{ - git_strmap *map; - - if (submodule_cache_init(repo, CACHE_OK) < 0) { - giterr_clear(); - return false; - } - - 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) @@ -184,44 +124,39 @@ static void submodule_set_lookup_error(int error, const char *name) "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, CACHE_OK)) < 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, const char *name) /* trailing slash is allowed */ { int error; + git_submodule *sm; + git_config_backend *mods; assert(repo && name); - if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0) + mods = open_gitmodules(repo, GITMODULES_EXISTING); + if (!mods) + return -1; + + if ((error = submodule_alloc(&sm, repo, name)) < 0) return error; - if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) { + if ((error = git_submodule_reload(sm, false)) < 0) { + git_submodule_free(sm); + return error; + } - /* check if a plausible submodule exists at path */ + /* If we didn't find the url, consider it missing */ + if (!sm->url) { + git_submodule_free(sm); + error = GIT_ENOTFOUND; + + /* If it's not configured, we still check if there's a repo at the path */ if (git_repository_workdir(repo)) { git_buf path = GIT_BUF_INIT; - if (git_buf_join3(&path, - '/', git_repository_workdir(repo), name, DOT_GIT) < 0) + '/', git_repository_workdir(repo), name, DOT_GIT) < 0) return -1; if (git_path_exists(path.ptr)) @@ -231,9 +166,15 @@ int git_submodule_lookup( } submodule_set_lookup_error(error, name); + return error; } - return error; + if (out) + *out = sm; + else + git_submodule_free(sm); + + return 0; } static void submodule_free_dup(void *sm) @@ -241,41 +182,221 @@ static void submodule_free_dup(void *sm) git_submodule_free(sm); } +static int submodule_get_or_create(git_submodule **out, git_repository *repo, git_strmap *map, const char *name) +{ + int error = 0; + khiter_t pos; + git_submodule *sm = NULL; + + pos = git_strmap_lookup_index(map, name); + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); + goto done; + } + + /* if the submodule doesn't exist yet in the map, create it */ + if ((error = submodule_alloc(&sm, repo, name)) < 0) + return error; + + pos = kh_put(str, map, sm->name, &error); + /* nobody can beat us to adding it */ + assert(error != 0); + if (error < 0) { + git_submodule_free(sm); + return error; + } + + git_strmap_set_value_at(map, pos, sm); + +done: + GIT_REFCOUNT_INC(sm); + *out = sm; + return 0; +} + +static int submodules_from_index(git_strmap *map, git_index *idx) +{ + int error; + git_iterator *i; + const git_index_entry *entry; + + if ((error = git_iterator_for_index(&i, idx, 0, NULL, NULL)) < 0) + return error; + + while (!(error = git_iterator_advance(&entry, i))) { + khiter_t pos = git_strmap_lookup_index(map, entry->path); + git_submodule *sm; + + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, 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_or_create(&sm, git_index_owner(idx), map, entry->path)) { + submodule_update_from_index_entry(sm, entry); + git_submodule_free(sm); + } + } + } + + if (error == GIT_ITEROVER) + error = 0; + + git_iterator_free(i); + + return error; +} + +static int submodules_from_head(git_strmap *map, git_tree *head) +{ + int error; + git_iterator *i; + const git_index_entry *entry; + + if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) + return error; + + while (!(error = git_iterator_advance(&entry, i))) { + khiter_t pos = git_strmap_lookup_index(map, entry->path); + git_submodule *sm; + + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); + + if (S_ISGITLINK(entry->mode)) + submodule_update_from_head_data(sm, entry->mode, &entry->id); + else + sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; + } else if (S_ISGITLINK(entry->mode)) { + if (!submodule_get_or_create(&sm, git_tree_owner(head), map, entry->path)) { + submodule_update_from_head_data( + sm, entry->mode, &entry->id); + git_submodule_free(sm); + } + } + } + + if (error == GIT_ITEROVER) + error = 0; + + git_iterator_free(i); + + return error; +} + +/* If have_sm is true, sm is populated, otherwise map an repo are. */ +typedef struct { + int have_sm; + git_submodule *sm; + git_strmap *map; + git_repository *repo; +} lfc_data; + +static int all_submodules(git_repository *repo, git_strmap *map) +{ + int error = 0; + git_index *idx = NULL; + git_tree *head = NULL; + const char *wd = NULL; + git_buf path = GIT_BUF_INIT; + git_submodule *sm; + git_config_backend *mods = NULL; + uint32_t mask; + + assert(repo && map); + + /* get sources that we will need to check */ + if (git_repository_index(&idx, repo) < 0) + giterr_clear(); + if (git_repository_head_tree(&head, repo) < 0) + giterr_clear(); + + wd = git_repository_workdir(repo); + if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0) + goto cleanup; + + /* clear submodule flags that are to be refreshed */ + mask = 0; + mask |= GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS__INDEX_FLAGS | + GIT_SUBMODULE_STATUS__INDEX_OID_VALID | + GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES; + + mask |= GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS__HEAD_OID_VALID; + mask |= GIT_SUBMODULE_STATUS_IN_CONFIG; + if (mask != 0) + mask |= GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS__WD_SCANNED | + GIT_SUBMODULE_STATUS__WD_FLAGS | + GIT_SUBMODULE_STATUS__WD_OID_VALID; + + /* add back submodule information from index */ + if (idx) { + if ((error = submodules_from_index(map, idx)) < 0) + goto cleanup; + } + /* add submodule information from HEAD */ + if (head) { + if ((error = submodules_from_head(map, head)) < 0) + goto cleanup; + } + /* add submodule information from .gitmodules */ + if (wd) { + lfc_data data = { 0 }; + data.map = map; + data.repo = repo; + if ((mods = open_gitmodules(repo, false)) != NULL && + (error = git_config_file_foreach( + mods, submodule_load_from_config, &data)) < 0) + goto cleanup; + } + /* shallow scan submodules in work tree as needed */ + if (wd && mask != 0) { + git_strmap_foreach_value(map, sm, { + submodule_load_from_wd_lite(sm); + }); + } + +cleanup: + git_config_file_free(mods); + /* TODO: if we got an error, mark submodule config as invalid? */ + git_index_free(idx); + git_tree_free(head); + git_buf_free(&path); + return error; +} + int git_submodule_foreach( git_repository *repo, int (*callback)(git_submodule *sm, const char *name, void *payload), void *payload) { + git_vector snapshot = GIT_VECTOR_INIT; + git_strmap *submodules; + git_submodule *sm; int error; size_t i; - git_submodule *sm; - git_submodule_cache *cache; - git_vector snapshot = GIT_VECTOR_INIT; - assert(repo && callback); - - if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0) + if ((error = git_strmap_alloc(&submodules)) < 0) return error; - cache = repo->_submodules; - - if (git_mutex_lock(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache"); - return -1; - } + if ((error = all_submodules(repo, submodules)) < 0) + goto done; if (!(error = git_vector_init( - &snapshot, kh_size(cache->submodules), submodule_cmp))) { + &snapshot, kh_size(submodules), submodule_cmp))) { - git_strmap_foreach_value(cache->submodules, sm, { + git_strmap_foreach_value(submodules, sm, { if ((error = git_vector_insert(&snapshot, sm)) < 0) break; GIT_REFCOUNT_INC(sm); }); } - git_mutex_unlock(&cache->lock); - if (error < 0) goto done; @@ -293,19 +414,14 @@ done: git_submodule_free(sm); git_vector_free(&snapshot); + git_strmap_foreach_value(submodules, sm, { + git_submodule_free(sm); + }); + git_strmap_free(submodules); + return error; } -void git_submodule_cache_free(git_repository *repo) -{ - git_submodule_cache *cache; - - assert(repo); - - if ((cache = git__swap(repo->_submodules, NULL)) != NULL) - submodule_cache_free(cache); -} - static int submodule_repo_init( git_repository **out, git_repository *parent_repo, @@ -394,7 +510,7 @@ int git_submodule_add_setup( /* update .gitmodules */ - if (!(mods = open_gitmodules(repo->_submodules, GITMODULES_CREATE))) { + if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) { giterr_set(GITERR_SUBMODULE, "Adding submodules to a bare repository is not supported"); return -1; @@ -430,19 +546,10 @@ int git_submodule_add_setup( goto cleanup; } - /* add submodule to hash and "reload" it */ - - if (git_mutex_lock(&repo->_submodules->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache"); - error = -1; + if ((error = git_submodule_lookup(&sm, repo, path)) < 0) goto cleanup; - } - if (!(error = submodule_get(&sm, repo->_submodules, path, NULL)) && - !(error = git_submodule_reload(sm, false))) - error = git_submodule_init(sm, false); - - git_mutex_unlock(&repo->_submodules->lock); + error = git_submodule_init(sm, false); cleanup: if (error && sm) { @@ -608,7 +715,7 @@ int git_submodule_save(git_submodule *submodule) assert(submodule); - mods = open_gitmodules(submodule->repo->_submodules, GITMODULES_CREATE); + mods = open_gitmodules(submodule->repo, GITMODULES_CREATE); if (!mods) { giterr_set(GITERR_SUBMODULE, "Adding submodules to a bare repository is not supported"); @@ -1197,11 +1304,6 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm) return git_submodule__open(subrepo, sm, false); } -int git_submodule_reload_all(git_repository *repo, int force) -{ - return submodule_cache_init(repo, force ? CACHE_FLUSH : CACHE_REFRESH); -} - static void submodule_update_from_index_entry( git_submodule *sm, const git_index_entry *ie) { @@ -1280,14 +1382,12 @@ int git_submodule_reload(git_submodule *sm, int force) { int error = 0; git_config_backend *mods; - git_submodule_cache *cache; + lfc_data data = { 0 }; GIT_UNUSED(force); assert(sm); - cache = sm->repo->_submodules; - /* refresh index data */ if ((error = submodule_update_index(sm)) < 0) return error; @@ -1301,7 +1401,7 @@ int git_submodule_reload(git_submodule *sm, int force) return error; /* refresh config data */ - mods = open_gitmodules(cache, GITMODULES_EXISTING); + mods = open_gitmodules(sm->repo, GITMODULES_EXISTING); if (mods != NULL) { git_buf path = GIT_BUF_INIT; @@ -1309,11 +1409,14 @@ int git_submodule_reload(git_submodule *sm, int force) git_buf_text_puts_escape_regex(&path, sm->name); git_buf_puts(&path, ".*"); - if (git_buf_oom(&path)) + if (git_buf_oom(&path)) { error = -1; - else + } else { + data.have_sm = 1; + data.sm = sm; error = git_config_file_foreach_match( - mods, path.ptr, submodule_load_from_config, cache); + mods, path.ptr, submodule_load_from_config, &data); + } git_buf_free(&path); git_config_file_free(mods); @@ -1422,7 +1525,7 @@ int git_submodule_location(unsigned int *location, git_submodule *sm) */ static int submodule_alloc( - git_submodule **out, git_submodule_cache *cache, const char *name) + git_submodule **out, git_repository *repo, const char *name) { size_t namelen; git_submodule *sm; @@ -1445,56 +1548,20 @@ static int submodule_alloc( 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 = cache->repo; + sm->repo = repo; sm->branch = NULL; *out = sm; return 0; } -static void submodule_cache_remove_item( - git_submodule_cache *cache, - git_submodule *item, - bool free_after_remove) -{ - git_strmap *map; - const char *name, *alt; - - if (!cache || !(map = cache->submodules) || !item) - return; - - name = item->name; - alt = (item->path != item->name) ? item->path : NULL; - - for (; name; name = alt, alt = NULL) { - khiter_t pos = git_strmap_lookup_index(map, name); - git_submodule *found; - - if (!git_strmap_valid_index(map, pos)) - continue; - - found = git_strmap_value_at(map, pos); - - if (found != item) - continue; - - git_strmap_set_value_at(map, pos, NULL); - git_strmap_delete_at(map, pos); - - if (free_after_remove) - git_submodule_free(found); - } -} - static void submodule_release(git_submodule *sm) { if (!sm) return; if (sm->repo) { - git_submodule_cache *cache = sm->repo->_submodules; sm->repo = NULL; - submodule_cache_remove_item(cache, sm, false); } if (sm->path != sm->name) @@ -1513,54 +1580,6 @@ void git_submodule_free(git_submodule *sm) GIT_REFCOUNT_DEC(sm, submodule_release); } -static int submodule_get( - git_submodule **out, - git_submodule_cache *cache, - const char *name, - const char *alternate) -{ - int error = 0; - khiter_t pos; - git_submodule *sm; - - pos = git_strmap_lookup_index(cache->submodules, name); - - if (!git_strmap_valid_index(cache->submodules, pos) && alternate) - pos = git_strmap_lookup_index(cache->submodules, alternate); - - 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, cache->submodules, sm->name, &error); - - if (error < 0) - goto done; - else if (error == 0) { - git_submodule_free(sm); - sm = git_strmap_value_at(cache->submodules, pos); - } else { - error = 0; - git_strmap_set_value_at(cache->submodules, pos, sm); - } - } else { - sm = git_strmap_value_at(cache->submodules, pos); - } - -done: - if (error < 0) - git_submodule_free(sm); - else if (out) { - GIT_REFCOUNT_INC(sm); - *out = sm; - } - - return error; -} - static int submodule_config_error(const char *property, const char *value) { giterr_set(GITERR_INVALID, @@ -1613,12 +1632,12 @@ 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_submodule_cache *cache = payload; const char *namestart, *property; const char *key = entry->name, *value = entry->value, *path; char *alternate = NULL, *replaced = NULL; git_buf name = GIT_BUF_INIT; - git_submodule *sm = NULL; + lfc_data *data = payload; + git_submodule *sm; int error = 0; if (git__prefixcmp(key, "submodule.") != 0) @@ -1633,10 +1652,29 @@ static int submodule_load_from_config( property++; path = !strcasecmp(property, "path") ? value : NULL; - if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 || - (error = submodule_get(&sm, cache, name.ptr, path)) < 0) + if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0) goto done; + if (data->have_sm) { + sm = data->sm; + } else { + khiter_t pos; + git_strmap *map = data->map; + pos = git_strmap_lookup_index(map, name.ptr); + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); + } else { + if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0) + goto done; + + git_strmap_insert(map, sm->name, sm, error); + assert(error != 0); + if (error < 0) + goto done; + error = 0; + } + } + sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; /* Only from config might we get differing names & paths. If so, then @@ -1648,7 +1686,7 @@ static int submodule_load_from_config( */ if (strcmp(sm->name, name.ptr) != 0) { /* name changed */ - if (!strcmp(sm->path, name.ptr)) { /* already set as path */ + if (sm->path && !strcmp(sm->path, name.ptr)) { /* already set as path */ replaced = sm->name; sm->name = sm->path; } else { @@ -1674,7 +1712,6 @@ static int submodule_load_from_config( /* Deregister under name being replaced */ if (replaced) { - git_strmap_delete(cache->submodules, replaced); git_submodule_free(sm); git__free(replaced); } @@ -1682,7 +1719,6 @@ static int submodule_load_from_config( /* Insert under alternate key */ if (alternate) { void *old_sm = NULL; - git_strmap_insert2(cache->submodules, alternate, sm, old_sm, error); if (error < 0) goto done; @@ -1742,7 +1778,6 @@ static int submodule_load_from_config( /* ignore other unknown submodule properties */ done: - git_submodule_free(sm); /* offset refcount inc from submodule_get() */ git_buf_free(&name); return error; } @@ -1764,86 +1799,11 @@ static int submodule_load_from_wd_lite(git_submodule *sm) return 0; } -static int submodule_cache_refresh_from_index( - git_submodule_cache *cache, git_index *idx) -{ - int error; - git_iterator *i; - const git_index_entry *entry; - - if ((error = git_iterator_for_index(&i, idx, 0, NULL, NULL)) < 0) - return error; - - while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path); - git_submodule *sm; - - 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, cache, entry->path, NULL)) { - submodule_update_from_index_entry(sm, entry); - git_submodule_free(sm); - } - } - } - - if (error == GIT_ITEROVER) - error = 0; - - git_iterator_free(i); - - return error; -} - -static int submodule_cache_refresh_from_head( - git_submodule_cache *cache, git_tree *head) -{ - int error; - git_iterator *i; - const git_index_entry *entry; - - if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) - return error; - - while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path); - git_submodule *sm; - - 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(sm, entry->mode, &entry->id); - else - sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; - } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get(&sm, cache, entry->path, NULL)) { - submodule_update_from_head_data( - sm, entry->mode, &entry->id); - git_submodule_free(sm); - } - } - } - - if (error == GIT_ITEROVER) - error = 0; - - git_iterator_free(i); - - return error; -} - static git_config_backend *open_gitmodules( - git_submodule_cache *cache, + git_repository *repo, int okay_to_create) { - const char *workdir = git_repository_workdir(cache->repo); + const char *workdir = git_repository_workdir(repo); git_buf path = GIT_BUF_INIT; git_config_backend *mods = NULL; @@ -1868,198 +1828,6 @@ static git_config_backend *open_gitmodules( return mods; } -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; - } - - if (git_strmap_alloc(&cache->submodules) < 0) { - 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, int refresh) -{ - int error = 0, update_index, update_head, update_gitmod; - git_index *idx = NULL; - git_tree *head = NULL; - const char *wd = NULL; - git_buf path = GIT_BUF_INIT; - git_submodule *sm; - git_config_backend *mods = NULL; - uint32_t mask; - - if (!cache || !cache->repo || !refresh) - return 0; - - if (git_mutex_lock(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache"); - return -1; - } - - /* get sources that we will need to check */ - - if (git_repository_index(&idx, cache->repo) < 0) - giterr_clear(); - if (git_repository_head_tree(&head, cache->repo) < 0) - giterr_clear(); - - wd = git_repository_workdir(cache->repo); - if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0) - goto cleanup; - - /* check for invalidation */ - - if (refresh == CACHE_FLUSH) - update_index = update_head = update_gitmod = true; - else { - update_index = - !idx || git_index__changed_relative_to(idx, &cache->index_checksum); - update_head = - !head || !git_oid_equal(&cache->head_id, git_tree_id(head)); - - update_gitmod = (wd != NULL) ? - git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr) : - (cache->gitmodules_stamp.mtime != 0); - } - - /* clear submodule flags that are to be refreshed */ - - mask = 0; - if (!idx || update_index) - mask |= GIT_SUBMODULE_STATUS_IN_INDEX | - GIT_SUBMODULE_STATUS__INDEX_FLAGS | - GIT_SUBMODULE_STATUS__INDEX_OID_VALID | - GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES; - if (!head || update_head) - mask |= GIT_SUBMODULE_STATUS_IN_HEAD | - GIT_SUBMODULE_STATUS__HEAD_OID_VALID; - if (update_gitmod) - mask |= GIT_SUBMODULE_STATUS_IN_CONFIG; - if (mask != 0) - mask |= GIT_SUBMODULE_STATUS_IN_WD | - GIT_SUBMODULE_STATUS__WD_SCANNED | - GIT_SUBMODULE_STATUS__WD_FLAGS | - GIT_SUBMODULE_STATUS__WD_OID_VALID; - else - goto cleanup; /* nothing to do */ - - submodule_cache_clear_flags(cache, mask); - - /* add back submodule information from index */ - - if (idx && update_index) { - if ((error = submodule_cache_refresh_from_index(cache, idx)) < 0) - goto cleanup; - - git_oid_cpy(&cache->index_checksum, git_index_checksum(idx)); - } - - /* add submodule information from HEAD */ - - if (head && update_head) { - if ((error = submodule_cache_refresh_from_head(cache, head)) < 0) - goto cleanup; - - git_oid_cpy(&cache->head_id, git_tree_id(head)); - } - - /* add submodule information from .gitmodules */ - - if (wd && update_gitmod > 0) { - if ((mods = open_gitmodules(cache, false)) != NULL && - (error = git_config_file_foreach( - mods, submodule_load_from_config, cache)) < 0) - goto cleanup; - } - - /* shallow scan submodules in work tree as needed */ - - if (wd && mask != 0) { - git_strmap_foreach_value(cache->submodules, sm, { - submodule_load_from_wd_lite(sm); - }); - } - - /* remove submodules that no longer exist */ - - git_strmap_foreach_value(cache->submodules, sm, { - /* purge unless in HEAD, index, or .gitmodules; no sm for wd only */ - if (sm != NULL && - !(sm->flags & - (GIT_SUBMODULE_STATUS_IN_HEAD | - GIT_SUBMODULE_STATUS_IN_INDEX | - GIT_SUBMODULE_STATUS_IN_CONFIG))) - submodule_cache_remove_item(cache, sm, true); - }); - -cleanup: - git_config_file_free(mods); - - /* TODO: if we got an error, mark submodule config as invalid? */ - - git_mutex_unlock(&cache->lock); - - git_index_free(idx); - git_tree_free(head); - git_buf_free(&path); - - return error; -} - -static int submodule_cache_init(git_repository *repo, int cache_refresh) -{ - int error = 0; - git_submodule_cache *cache = NULL; - - /* if submodules already exist, just refresh as requested */ - if (repo->_submodules) - return submodule_cache_refresh(repo->_submodules, cache_refresh); - - /* otherwise create a new cache, load it, and atomically swap it in */ - if (!(error = submodule_cache_alloc(&cache, repo)) && - !(error = submodule_cache_refresh(cache, CACHE_FLUSH))) - 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; -} - /* Lookup name of remote of the local tracking branch HEAD points to */ static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo) { diff --git a/src/submodule.h b/src/submodule.h index 7a9bf9c92..d67796193 100644 --- a/src/submodule.h +++ b/src/submodule.h @@ -99,23 +99,6 @@ 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_oid index_checksum; - git_buf gitmodules_path; - git_futils_filestamp gitmodules_stamp; - 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); @@ -137,9 +120,6 @@ enum { #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \ ((S) & ~(0xFFFFFFFFu << 20)) -/* 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); diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c index bbbb2d56e..8f24d9bb5 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -50,8 +50,6 @@ void test_submodule_modify__init(void) /* call init and see that settings are copied */ cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL)); - git_submodule_reload_all(g_repo, 1); - /* confirm submodule data in config */ cl_git_pass(git_repository_config_snapshot(&cfg, g_repo)); cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url")); diff --git a/tests/submodule/nosubs.c b/tests/submodule/nosubs.c index e343c1620..538825c1c 100644 --- a/tests/submodule/nosubs.c +++ b/tests/submodule/nosubs.c @@ -21,19 +21,11 @@ void test_submodule_nosubs__lookup(void) cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo")); - cl_git_pass(git_submodule_reload_all(repo, 0)); - cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, repo, "subdir")); cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo")); } -void test_submodule_nosubs__immediate_reload(void) -{ - git_repository *repo = cl_git_sandbox_init("status"); - cl_git_pass(git_submodule_reload_all(repo, 0)); -} - static int fake_submod_cb(git_submodule *sm, const char *n, void *p) { GIT_UNUSED(sm); GIT_UNUSED(n); GIT_UNUSED(p); @@ -57,53 +49,17 @@ void test_submodule_nosubs__add(void) git_submodule_free(sm2); cl_git_pass(git_submodule_foreach(repo, fake_submod_cb, NULL)); - cl_git_pass(git_submodule_reload_all(repo, 0)); git_submodule_free(sm); } -void test_submodule_nosubs__reload_add_reload(void) -{ - git_repository *repo = cl_git_sandbox_init("status"); - git_submodule *sm; - - cl_git_pass(git_submodule_reload_all(repo, 0)); - - /* try one add with a reload (to make sure no errors happen) */ - - cl_git_pass(git_submodule_add_setup(&sm, repo, - "https://github.com/libgit2/libgit2.git", "submodules/libgit2", 1)); - - cl_git_pass(git_submodule_reload_all(repo, 0)); - - cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm)); - git_submodule_free(sm); - - cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2")); - cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm)); - git_submodule_free(sm); - - /* try one add without a reload (to make sure cache inval works, too) */ - - cl_git_pass(git_submodule_add_setup(&sm, repo, - "https://github.com/libgit2/libgit2.git", "libgit2-again", 1)); - cl_assert_equal_s("libgit2-again", git_submodule_name(sm)); - git_submodule_free(sm); - - cl_git_pass(git_submodule_lookup(&sm, repo, "libgit2-again")); - cl_assert_equal_s("libgit2-again", git_submodule_name(sm)); - git_submodule_free(sm); -} - void test_submodule_nosubs__bad_gitmodules(void) { git_repository *repo = cl_git_sandbox_init("status"); cl_git_mkfile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=flooble\n\n"); - cl_git_fail(git_submodule_reload_all(repo, 0)); cl_git_rewritefile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=rebase\n\n"); - cl_git_pass(git_submodule_reload_all(repo, 0)); cl_git_pass(git_submodule_lookup(NULL, repo, "foobar")); cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(NULL, repo, "subdir"));