diff options
| author | Carlos Martín Nieto <cmn@dwim.me> | 2015-06-24 23:34:40 +0200 |
|---|---|---|
| committer | Carlos Martín Nieto <cmn@dwim.me> | 2015-06-24 23:34:40 +0200 |
| commit | daacf96d101b9d2100a5028090b5af5249d8893d (patch) | |
| tree | 450942a1431c22f634f9c16916408bd80f165702 /src | |
| parent | e1f434f8643f26d48ee8de21d715069de76b14e1 (diff) | |
| parent | 783672fa5be4f0e9dce72bcd74690258bdbac0a9 (diff) | |
| download | libgit2-daacf96d101b9d2100a5028090b5af5249d8893d.tar.gz | |
Merge pull request #3097 from libgit2/cmn/submodule-config-state
Remove run-time configuration settings from submodules
Diffstat (limited to 'src')
| -rw-r--r-- | src/checkout.c | 10 | ||||
| -rw-r--r-- | src/config.c | 20 | ||||
| -rw-r--r-- | src/config.h | 6 | ||||
| -rw-r--r-- | src/diff_file.c | 2 | ||||
| -rw-r--r-- | src/repository.c | 1 | ||||
| -rw-r--r-- | src/repository.h | 1 | ||||
| -rw-r--r-- | src/submodule.c | 1037 | ||||
| -rw-r--r-- | src/submodule.h | 24 |
8 files changed, 421 insertions, 680 deletions
diff --git a/src/checkout.c b/src/checkout.c index 2893c63de..351e8b0ef 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -180,7 +180,7 @@ static bool checkout_is_workdir_modified( return true; } - if (git_submodule_status(&sm_status, sm) < 0 || + if (git_submodule_status(&sm_status, data->repo, wditem->path, GIT_SUBMODULE_IGNORE_FALLBACK) < 0 || GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) rval = true; else if ((sm_oid = git_submodule_wd_id(sm)) == NULL) @@ -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/config.c b/src/config.c index 1400b9513..77cf573e6 100644 --- a/src/config.c +++ b/src/config.c @@ -1194,6 +1194,26 @@ fail_parse: return -1; } +int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out, + const git_cvar_map *maps, size_t map_n, int enum_val) +{ + size_t i; + + for (i = 0; i < map_n; i++) { + const git_cvar_map *m = &maps[i]; + + if (m->map_value != enum_val) + continue; + + *type_out = m->cvar_type; + *str_out = m->str_match; + return 0; + } + + giterr_set(GITERR_CONFIG, "invalid enum value"); + return GIT_ENOTFOUND; +} + int git_config_parse_bool(int *out, const char *value) { if (git__parse_bool(out, value) == 0) diff --git a/src/config.h b/src/config.h index 691868b1d..f257cc90f 100644 --- a/src/config.h +++ b/src/config.h @@ -82,4 +82,10 @@ extern int git_config__get_int_force( extern int git_config__cvar( int *out, git_config *config, git_cvar_cached cvar); +/** + * The opposite of git_config_lookup_map_value, we take an enum value + * and map it to the string or bool value on the config. + */ +int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out, + const git_cvar_map *maps, size_t map_n, int enum_val); #endif diff --git a/src/diff_file.c b/src/diff_file.c index cef4bc169..28edcd4c1 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -186,7 +186,7 @@ static int diff_file_content_commit_to_str( return error; } - if ((error = git_submodule_status(&sm_status, sm)) < 0) { + if ((error = git_submodule_status(&sm_status, fc->repo, fc->file->path, GIT_SUBMODULE_IGNORE_FALLBACK)) < 0) { git_submodule_free(sm); return error; } diff --git a/src/repository.c b/src/repository.c index de1c0dc1f..82d998124 100644 --- a/src/repository.c +++ b/src/repository.c @@ -111,7 +111,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..45163da1b 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,22 +124,24 @@ 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; +typedef struct { + const char *path; + char *name; +} fbp_data; - assert(repo && name); - - if ((error = submodule_cache_init(repo, CACHE_OK)) < 0) - return error; +static int find_by_path(const git_config_entry *entry, void *payload) +{ + fbp_data *data = payload; - if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) - submodule_set_lookup_error(error, name); + if (!strcmp(entry->value, data->path)) { + const char *fdot, *ldot; + fdot = strchr(entry->name, '.'); + ldot = strrchr(entry->name, '.'); + data->name = git__strndup(fdot + 1, ldot - fdot - 1); + GITERR_CHECK_ALLOC(data->name); + } - return error; + return 0; } int git_submodule_lookup( @@ -208,20 +150,71 @@ int git_submodule_lookup( const char *name) /* trailing slash is allowed */ { int error; + unsigned int location; + git_submodule *sm; assert(repo && name); - if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0) + if ((error = submodule_alloc(&sm, repo, name)) < 0) + return error; + + if ((error = git_submodule_reload(sm, false)) < 0) { + git_submodule_free(sm); + return error; + } + + if ((error = git_submodule_location(&location, sm)) < 0) { + git_submodule_free(sm); return error; + } + + /* If it's not configured, we need to check for the path */ + if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) { + git_config_backend *mods; + const char *pattern = "submodule\\..*\\.path"; + fbp_data data = { name, NULL }; + + mods = open_gitmodules(repo, GITMODULES_EXISTING); + + if (mods) + error = git_config_file_foreach_match(mods, pattern, find_by_path, &data); - if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) { + git_config_file_free(mods); + + if (error < 0) { + git_submodule_free(sm); + return error; + } + + if (data.name) { + git__free(sm->name); + sm->name = data.name; + sm->path = git__strdup(name); + GITERR_CHECK_ALLOC(sm->path); - /* check if a plausible submodule exists at path */ + /* Try to load again with the right name */ + if ((error = git_submodule_reload(sm, false)) < 0) { + git_submodule_free(sm); + return error; + } + } + } + + if ((error = git_submodule_location(&location, sm)) < 0) { + git_submodule_free(sm); + return error; + } + + /* If we still haven't found it, do the WD check */ + if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) { + 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 +224,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 +240,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,17 +472,12 @@ done: git_submodule_free(sm); git_vector_free(&snapshot); - return error; -} - -void git_submodule_cache_free(git_repository *repo) -{ - git_submodule_cache *cache; - - assert(repo); + git_strmap_foreach_value(submodules, sm, { + git_submodule_free(sm); + }); + git_strmap_free(submodules); - if ((cache = git__swap(repo->_submodules, NULL)) != NULL) - submodule_cache_free(cache); + return error; } static int submodule_repo_init( @@ -394,7 +568,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 +604,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) { @@ -572,15 +737,6 @@ cleanup: return error; } -const char *git_submodule_ignore_to_str(git_submodule_ignore_t ignore) -{ - int i; - for (i = 0; i < (int)ARRAY_SIZE(_sm_ignore_map); ++i) - if (_sm_ignore_map[i].map_value == ignore) - return _sm_ignore_map[i].str_match; - return NULL; -} - const char *git_submodule_update_to_str(git_submodule_update_t update) { int i; @@ -590,89 +746,6 @@ const char *git_submodule_update_to_str(git_submodule_update_t update) return NULL; } -const char *git_submodule_recurse_to_str(git_submodule_recurse_t recurse) -{ - int i; - for (i = 0; i < (int)ARRAY_SIZE(_sm_recurse_map); ++i) - if (_sm_recurse_map[i].map_value == recurse) - return _sm_recurse_map[i].str_match; - return NULL; -} - -int git_submodule_save(git_submodule *submodule) -{ - int error = 0; - git_config_backend *mods; - git_buf key = GIT_BUF_INIT; - const char *val; - - assert(submodule); - - mods = open_gitmodules(submodule->repo->_submodules, GITMODULES_CREATE); - if (!mods) { - giterr_set(GITERR_SUBMODULE, - "Adding submodules to a bare repository is not supported"); - return -1; - } - - if ((error = git_buf_printf(&key, "submodule.%s.", submodule->name)) < 0) - goto cleanup; - - /* save values for path, url, update, ignore, fetchRecurseSubmodules */ - - if ((error = submodule_config_key_trunc_puts(&key, "path")) < 0 || - (error = git_config_file_set_string(mods, key.ptr, submodule->path)) < 0) - goto cleanup; - - if ((error = submodule_config_key_trunc_puts(&key, "url")) < 0 || - (error = git_config_file_set_string(mods, key.ptr, submodule->url)) < 0) - goto cleanup; - - if ((error = submodule_config_key_trunc_puts(&key, "branch")) < 0) - goto cleanup; - if (submodule->branch == NULL) - error = git_config_file_delete(mods, key.ptr); - else - error = git_config_file_set_string(mods, key.ptr, submodule->branch); - if (error == GIT_ENOTFOUND) { - error = 0; - giterr_clear(); - } - if (error < 0) - goto cleanup; - - if (!(error = submodule_config_key_trunc_puts(&key, "update")) && - (val = git_submodule_update_to_str(submodule->update)) != NULL) - error = git_config_file_set_string(mods, key.ptr, val); - if (error < 0) - goto cleanup; - - if (!(error = submodule_config_key_trunc_puts(&key, "ignore")) && - (val = git_submodule_ignore_to_str(submodule->ignore)) != NULL) - error = git_config_file_set_string(mods, key.ptr, val); - if (error < 0) - goto cleanup; - - if (!(error = submodule_config_key_trunc_puts(&key, "fetchRecurseSubmodules")) && - (val = git_submodule_recurse_to_str(submodule->fetch_recurse)) != NULL) - error = git_config_file_set_string(mods, key.ptr, val); - if (error < 0) - goto cleanup; - - /* update internal defaults */ - - submodule->ignore_default = submodule->ignore; - submodule->update_default = submodule->update; - submodule->fetch_recurse_default = submodule->fetch_recurse; - submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; - -cleanup: - git_config_file_free(mods); - git_buf_free(&key); - - return error; -} - git_repository *git_submodule_owner(git_submodule *submodule) { assert(submodule); @@ -718,37 +791,66 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur return error; } -const char *git_submodule_branch(git_submodule *submodule) +static int write_var(git_repository *repo, const char *name, const char *var, const char *val) { - assert(submodule); - return submodule->branch; + git_buf key = GIT_BUF_INIT; + git_config_backend *mods; + int error; + + mods = open_gitmodules(repo, GITMODULES_CREATE); + if (!mods) + return -1; + + if ((error = git_buf_printf(&key, "submodule.%s.%s", name, var)) < 0) + goto cleanup; + + if (val) + error = git_config_file_set_string(mods, key.ptr, val); + else + error = git_config_file_delete(mods, key.ptr); + + git_buf_free(&key); + +cleanup: + git_config_file_free(mods); + return error; } -int git_submodule_set_branch(git_submodule *submodule, const char *branch) +static int write_mapped_var(git_repository *repo, const char *name, git_cvar_map *maps, size_t nmaps, const char *var, int ival) { - assert(submodule); - - git__free(submodule->branch); - submodule->branch = NULL; + git_cvar_t type; + const char *val; - if (branch != NULL) { - submodule->branch = git__strdup(branch); - GITERR_CHECK_ALLOC(submodule->branch); + if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) { + giterr_set(GITERR_SUBMODULE, "invalid value for %s", var); + return -1; } - return 0; + if (type == GIT_CVAR_TRUE) + val = "true"; + + return write_var(repo, name, var, val); } -int git_submodule_set_url(git_submodule *submodule, const char *url) +const char *git_submodule_branch(git_submodule *submodule) { - assert(submodule && url); + assert(submodule); + return submodule->branch; +} - git__free(submodule->url); +int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch) +{ - submodule->url = git__strdup(url); - GITERR_CHECK_ALLOC(submodule->url); + assert(repo && name); - return 0; + return write_var(repo, name, "branch", branch); +} + +int git_submodule_set_url(git_repository *repo, const char *name, const char *url) +{ + assert(repo && name && url); + + return write_var(repo, name, "url", url); } const git_oid *git_submodule_index_id(git_submodule *submodule) @@ -799,19 +901,11 @@ git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule) GIT_SUBMODULE_IGNORE_NONE : submodule->ignore; } -git_submodule_ignore_t git_submodule_set_ignore( - git_submodule *submodule, git_submodule_ignore_t ignore) +int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore) { - git_submodule_ignore_t old; - - assert(submodule); - - if (ignore == GIT_SUBMODULE_IGNORE_RESET) - ignore = submodule->ignore_default; + assert(repo && name); - old = submodule->ignore; - submodule->ignore = ignore; - return old; + return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore); } git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) @@ -821,19 +915,11 @@ git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update; } -git_submodule_update_t git_submodule_set_update( - git_submodule *submodule, git_submodule_update_t update) +int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update) { - git_submodule_update_t old; - - assert(submodule); - - if (update == GIT_SUBMODULE_UPDATE_RESET) - update = submodule->update_default; + assert(repo && name); - old = submodule->update; - submodule->update = update; - return old; + return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update); } git_submodule_recurse_t git_submodule_fetch_recurse_submodules( @@ -843,20 +929,11 @@ git_submodule_recurse_t git_submodule_fetch_recurse_submodules( return submodule->fetch_recurse; } -git_submodule_recurse_t git_submodule_set_fetch_recurse_submodules( - git_submodule *submodule, - git_submodule_recurse_t fetch_recurse_submodules) +int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse) { - git_submodule_recurse_t old; - - assert(submodule); - - if (fetch_recurse_submodules == GIT_SUBMODULE_RECURSE_RESET) - fetch_recurse_submodules = submodule->fetch_recurse_default; + assert(repo && name); - old = submodule->fetch_recurse; - submodule->fetch_recurse = fetch_recurse_submodules; - return old; + return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse); } static int submodule_repo_create( @@ -953,7 +1030,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio memcpy(&clone_options.fetch_opts, &update_options.fetch_opts, sizeof(git_fetch_options)); /* Get the status of the submodule to determine if it is already initialized */ - if ((error = git_submodule_status(&submodule_status, sm)) < 0) + if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_FALLBACK)) < 0) goto done; /* @@ -1197,11 +1274,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 +1352,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 +1371,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 +1379,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); @@ -1352,7 +1425,7 @@ int git_submodule__status( unsigned int status; git_repository *smrepo = NULL; - if (ign < GIT_SUBMODULE_IGNORE_NONE) + if (ign == GIT_SUBMODULE_IGNORE_FALLBACK) ign = sm->ignore; /* only return location info if ignore == all */ @@ -1401,11 +1474,20 @@ int git_submodule__status( return 0; } -int git_submodule_status(unsigned int *status, git_submodule *sm) +int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore) { - assert(status && sm); + git_submodule *sm; + int error; + + assert(status && repo && name); - return git_submodule__status(status, NULL, NULL, NULL, sm, 0); + if ((error = git_submodule_lookup(&sm, repo, name)) < 0) + return error; + + error = git_submodule__status(status, NULL, NULL, NULL, sm, ignore); + git_submodule_free(sm); + + return error; } int git_submodule_location(unsigned int *location, git_submodule *sm) @@ -1422,7 +1504,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 +1527,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 +1559,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 +1611,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 +1631,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 +1665,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 +1691,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 +1698,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 +1757,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 +1778,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 +1807,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..2ef2031b3 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); @@ -163,8 +143,4 @@ extern int git_submodule_parse_ignore( extern int git_submodule_parse_update( git_submodule_update_t *out, const char *value); -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 |
