From dfda2f68ea9602a6b0d08e36bd48e6a4899b12ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 27 Apr 2015 19:27:29 +0200 Subject: [PATCH 01/18] submodule: remove the per-repo cache Having this cache and giving them out goes against our multithreading guarantees and it makes it impossible to use submodules in a multi-threaded environment, as any thread can ask for a refresh which may reallocate some string in the submodule struct which we've accessed in a different one via a getter. This makes the submodules behave more like remotes, where each object is created upon request and not shared except explicitly by the user. This means that some tests won't pass yet, as they assume they can affect the submodule objects in the cache and that will affect later operations. --- src/checkout.c | 8 +- src/repository.c | 1 - src/repository.h | 1 - src/submodule.c | 772 ++++++++++++++------------------------- src/submodule.h | 20 - tests/submodule/modify.c | 2 - tests/submodule/nosubs.c | 44 --- 7 files changed, 271 insertions(+), 577 deletions(-) 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")); From 5a9fc6c83c2fd12ce312d1042ec2c1e6abad4033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 4 May 2015 16:22:56 +0200 Subject: [PATCH 02/18] submodule: make set_ignore() affect the configuration Instead of affecting a particular instance, make it change the configuration. --- include/git2/submodule.h | 24 ++++++++---------------- src/submodule.c | 37 ++++++++++++++++++++++--------------- tests/diff/submodules.c | 16 ++++++++-------- tests/submodule/modify.c | 27 +++++++++++---------------- tests/submodule/status.c | 2 +- 5 files changed, 50 insertions(+), 56 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 48faf8a49..e41dc49d2 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -452,9 +452,6 @@ GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule); * The working directory will be consider clean so long as there is a * checked out version present. * - * plus the special **GIT_SUBMODULE_IGNORE_RESET** which can be used with - * `git_submodule_set_ignore()` to revert to the on-disk setting. - * * @param submodule The submodule to check * @return The current git_submodule_ignore_t valyue what will be used for * this submodule. @@ -463,23 +460,18 @@ GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore( git_submodule *submodule); /** - * Set the ignore rule for the submodule. + * Set the ignore rule for the submodule in the configuration * - * This sets the in-memory ignore rule for the submodule which will - * control the behavior of `git_submodule_status()`. + * This does not affect any currently-loaded instances. * - * To make changes persistent, call `git_submodule_save()` to write the - * value to disk (in the ".gitmodules" and ".git/config" files). - * - * Call with `GIT_SUBMODULE_IGNORE_RESET` or call `git_submodule_reload()` - * to revert the in-memory rule to the value that is on disk. - * - * @param submodule The submodule to update + * @param repo the repository to affect + * @param name the name of the submdule * @param ignore The new value for the ignore rule - * @return old value for ignore + * @return 0 or an error code */ -GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore( - git_submodule *submodule, +GIT_EXTERN(int) git_submodule_set_ignore( + git_repository *repo, + const char *name, git_submodule_ignore_t ignore); /** diff --git a/src/submodule.c b/src/submodule.c index 9a1485436..78bf5198c 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -754,12 +754,6 @@ int git_submodule_save(git_submodule *submodule) 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); @@ -906,19 +900,32 @@ 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; + git_buf key = GIT_BUF_INIT; + git_config_backend *mods; + const char *val; + int error; - assert(submodule); + val = git_submodule_ignore_to_str(ignore); + if (!val) { + giterr_set(GITERR_SUBMODULE, "invalid ignore value"); + return -1; + } - if (ignore == GIT_SUBMODULE_IGNORE_RESET) - ignore = submodule->ignore_default; + mods = open_gitmodules(repo, GITMODULES_CREATE); + if (!mods) + return -1; - old = submodule->ignore; - submodule->ignore = ignore; - return old; + if ((error = git_buf_printf(&key, "submodule.%s.ignore", name)) < 0) + goto cleanup; + + error = git_config_file_set_string(mods, key.ptr, val); + git_buf_free(&key); + +cleanup: + git_config_file_free(mods); + return error; } git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) diff --git a/tests/diff/submodules.c b/tests/diff/submodules.c index e2169583b..08682cd4b 100644 --- a/tests/diff/submodules.c +++ b/tests/diff/submodules.c @@ -273,13 +273,13 @@ void test_diff_submodules__invalid_cache(void) /* create untracked file in submodule working directory */ cl_git_mkfile("submod2/sm_changed_head/new_around_here", "hello"); - git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE); + git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_NONE); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_dirty); git_diff_free(diff); - git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED); + git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_UNTRACKED); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_unchanged); @@ -301,7 +301,7 @@ void test_diff_submodules__invalid_cache(void) check_diff_patches(diff, expected_dirty); git_diff_free(diff); - git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY); + git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_unchanged); @@ -312,13 +312,13 @@ void test_diff_submodules__invalid_cache(void) cl_git_pass(git_repository_index(&smindex, smrepo)); cl_git_pass(git_index_add_bypath(smindex, "file_to_modify")); - git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED); + git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_UNTRACKED); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_dirty); git_diff_free(diff); - git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY); + git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_unchanged); @@ -327,19 +327,19 @@ void test_diff_submodules__invalid_cache(void) /* commit changed index of submodule */ cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Move it"); - git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY); + git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_moved); git_diff_free(diff); - git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_ALL); + git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_ALL); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_unchanged); git_diff_free(diff); - git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE); + git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_NONE); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_moved_dirty); diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c index 8f24d9bb5..65f656ea2 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -128,11 +128,21 @@ void test_submodule_modify__sync(void) git_submodule_free(sm3); } +void test_submodule_modify__set_ignore(void) +{ + git_submodule *sm; + + cl_git_pass(git_submodule_set_ignore(g_repo, "sm_changed_head", GIT_SUBMODULE_IGNORE_UNTRACKED)); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); + cl_assert_equal_i(GIT_SUBMODULE_IGNORE_UNTRACKED, git_submodule_ignore(sm)); + git_submodule_free(sm); +} + void test_submodule_modify__edit_and_save(void) { git_submodule *sm1, *sm2; char *old_url, *old_branch; - git_submodule_ignore_t old_ignore; git_submodule_update_t old_update; git_repository *r2; git_submodule_recurse_t old_fetchrecurse; @@ -145,15 +155,12 @@ void test_submodule_modify__edit_and_save(void) /* modify properties of submodule */ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH)); - old_ignore = git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED); old_update = git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE); old_fetchrecurse = git_submodule_set_fetch_recurse_submodules( sm1, GIT_SUBMODULE_RECURSE_YES); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1)); - cl_assert_equal_i( - GIT_SUBMODULE_IGNORE_UNTRACKED, git_submodule_ignore(sm1)); cl_assert_equal_i( GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm1)); cl_assert_equal_i( @@ -162,9 +169,6 @@ void test_submodule_modify__edit_and_save(void) /* revert without saving (and confirm setters return old value) */ cl_git_pass(git_submodule_set_url(sm1, old_url)); cl_git_pass(git_submodule_set_branch(sm1, old_branch)); - cl_assert_equal_i( - GIT_SUBMODULE_IGNORE_UNTRACKED, - git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET)); cl_assert_equal_i( GIT_SUBMODULE_UPDATE_REBASE, git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET)); @@ -175,7 +179,6 @@ void test_submodule_modify__edit_and_save(void) /* check that revert was successful */ cl_assert_equal_s(old_url, git_submodule_url(sm1)); cl_assert_equal_s(old_branch, git_submodule_branch(sm1)); - cl_assert_equal_i((int)old_ignore, (int)git_submodule_ignore(sm1)); cl_assert_equal_i((int)old_update, (int)git_submodule_update_strategy(sm1)); cl_assert_equal_i( old_fetchrecurse, git_submodule_fetch_recurse_submodules(sm1)); @@ -183,7 +186,6 @@ void test_submodule_modify__edit_and_save(void) /* modify properties of submodule (again) */ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH)); - git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED); git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE); git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_YES); @@ -191,14 +193,11 @@ void test_submodule_modify__edit_and_save(void) cl_git_pass(git_submodule_save(sm1)); /* attempt to "revert" values */ - git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET); git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET); /* but ignore and update should NOT revert because the RESET * should now be the newly saved value... */ - cl_assert_equal_i( - (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1)); cl_assert_equal_i( (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update_strategy(sm1)); cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); @@ -208,8 +207,6 @@ void test_submodule_modify__edit_and_save(void) cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1)); - cl_assert_equal_i( - (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1)); cl_assert_equal_i( (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update_strategy(sm1)); cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); @@ -225,8 +222,6 @@ void test_submodule_modify__edit_and_save(void) cl_git_pass(git_submodule_lookup(&sm2, r2, "sm_changed_head")); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm2)); - cl_assert_equal_i( - GIT_SUBMODULE_IGNORE_UNTRACKED, git_submodule_ignore(sm2)); cl_assert_equal_i( GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm2)); cl_assert_equal_i( diff --git a/tests/submodule/status.c b/tests/submodule/status.c index 6efae35c6..4d51d8dc0 100644 --- a/tests/submodule/status.c +++ b/tests/submodule/status.c @@ -111,7 +111,7 @@ static int set_sm_ignore(git_submodule *sm, const char *name, void *payload) { git_submodule_ignore_t ignore = *(git_submodule_ignore_t *)payload; GIT_UNUSED(name); - git_submodule_set_ignore(sm, ignore); + git_submodule_set_ignore(g_repo, git_submodule_name(sm), ignore); return 0; } From 64bbd47a32e6aaed539bafd109eef624f24fbae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 4 May 2015 17:09:21 +0200 Subject: [PATCH 03/18] submodule: don't let status change an existing instance As submodules are becomes more like values, we should not let a status check to update its properties. Instead of taking a submodule, have status take a repo and submodule name. --- include/git2/submodule.h | 9 +++++---- src/checkout.c | 2 +- src/diff_file.c | 2 +- src/submodule.c | 17 ++++++++++++---- tests/submodule/submodule_helpers.c | 8 +++----- tests/submodule/update.c | 30 ++++++++++++++++++++--------- 6 files changed, 44 insertions(+), 24 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index e41dc49d2..737570844 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -626,16 +626,17 @@ GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo, int force); * This looks at a submodule and tries to determine the status. It * will return a combination of the `GIT_SUBMODULE_STATUS` values above. * How deeply it examines the working directory to do this will depend - * on the `git_submodule_ignore_t` value for the submodule - which can be - * set either temporarily or permanently with `git_submodule_set_ignore()`. + * on the `git_submodule_ignore_t` value for the submodule. * * @param status Combination of `GIT_SUBMODULE_STATUS` flags - * @param submodule Submodule for which to get status + * @param repo the repository in which to look + * @param name name of the submodule * @return 0 on success, <0 on error */ GIT_EXTERN(int) git_submodule_status( unsigned int *status, - git_submodule *submodule); + git_repository *repo, + const char *name); /** * Get the locations of submodule information. diff --git a/src/checkout.c b/src/checkout.c index dab83c65a..d95244a69 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) < 0 || GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) rval = true; else if ((sm_oid = git_submodule_wd_id(sm)) == NULL) diff --git a/src/diff_file.c b/src/diff_file.c index cef4bc169..4d9ecc8d8 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)) < 0) { git_submodule_free(sm); return error; } diff --git a/src/submodule.c b/src/submodule.c index 78bf5198c..2faaa734d 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1067,7 +1067,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)) < 0) goto done; /* @@ -1511,11 +1511,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) { - assert(status && sm); + git_submodule *sm; + int error; - return git_submodule__status(status, NULL, NULL, NULL, sm, 0); + assert(status && repo && name); + + if ((error = git_submodule_lookup(&sm, repo, name)) < 0) + return error; + + error = git_submodule__status(status, NULL, NULL, NULL, sm, 0); + git_submodule_free(sm); + + return error; } int git_submodule_location(unsigned int *location, git_submodule *sm) diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c index 19bb04f75..36838228f 100644 --- a/tests/submodule/submodule_helpers.c +++ b/tests/submodule/submodule_helpers.c @@ -164,13 +164,11 @@ void refute__submodule_exists( unsigned int get_submodule_status(git_repository *repo, const char *name) { - git_submodule *sm = NULL; unsigned int status = 0; - cl_git_pass(git_submodule_lookup(&sm, repo, name)); - cl_assert(sm); - cl_git_pass(git_submodule_status(&status, sm)); - git_submodule_free(sm); + assert(repo && name); + + cl_git_pass(git_submodule_status(&status, repo, name)); return status; } diff --git a/tests/submodule/update.c b/tests/submodule/update.c index e7f1b76b8..85870434e 100644 --- a/tests/submodule/update.c +++ b/tests/submodule/update.c @@ -103,7 +103,7 @@ void test_submodule_update__update_submodule(void) cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); /* verify the initial state of the submodule */ - cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -114,7 +114,7 @@ void test_submodule_update__update_submodule(void) cl_git_pass(git_submodule_update(sm, 0, &update_options)); /* verify state */ - cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -142,7 +142,7 @@ void test_submodule_update__update_and_init_submodule(void) /* get the submodule */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); - cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -177,7 +177,7 @@ void test_submodule_update__update_already_checked_out_submodule(void) /* Initialize and update the sub repository */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); - cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -203,7 +203,11 @@ void test_submodule_update__update_already_checked_out_submodule(void) * HEAD commit and index should be updated, but not the workdir. */ - cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + + git_submodule_free(sm); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -251,7 +255,7 @@ void test_submodule_update__update_blocks_on_dirty_wd(void) /* Initialize and update the sub repository */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); - cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -277,7 +281,11 @@ void test_submodule_update__update_blocks_on_dirty_wd(void) * HEAD commit and index should be updated, but not the workdir. */ - cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + + git_submodule_free(sm); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -324,7 +332,7 @@ void test_submodule_update__can_force_update(void) /* Initialize and update the sub repository */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); - cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -349,7 +357,11 @@ void test_submodule_update__can_force_update(void) * Verify state after checkout of parent repository. The submodule ID in the * HEAD commit and index should be updated, but not the workdir. */ - cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + + git_submodule_free(sm); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | From c6f489c964bc4df29bdacb1ee4afdcdb294f3815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 4 May 2015 17:29:12 +0200 Subject: [PATCH 04/18] submodule: add an ignore option to status This lets us specify in the status call which ignore rules we want to use (optionally falling back to whatever the submodule has in its configuration). This removes one of the reasons for having `_set_ignore()` set the value in-memory. We re-use the `IGNORE_RESET` value for this as it is no longer relevant but has a similar purpose to `IGNORE_FALLBACK`. Similarly, we remove `IGNORE_DEFAULT` which does not have use outside of initializers and move that to fall back to the configuration as well. --- include/git2/diff.h | 2 +- include/git2/submodule.h | 4 +- include/git2/types.h | 6 +-- src/checkout.c | 2 +- src/diff_file.c | 2 +- src/submodule.c | 8 ++-- tests/diff/tree.c | 2 +- tests/submodule/status.c | 65 ++++++++++++----------------- tests/submodule/submodule_helpers.c | 2 +- tests/submodule/update.c | 18 ++++---- 10 files changed, 50 insertions(+), 61 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 0ecdc1bed..90e2e1b22 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -399,7 +399,7 @@ typedef struct { * `git_diff_options_init` programmatic initialization. */ #define GIT_DIFF_OPTIONS_INIT \ - {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_DEFAULT, {NULL,0}, NULL, NULL, 3} + {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_FALLBACK, {NULL,0}, NULL, NULL, 3} /** * Initializes a `git_diff_options` with default values. Equivalent to diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 737570844..31218cc94 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -631,12 +631,14 @@ GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo, int force); * @param status Combination of `GIT_SUBMODULE_STATUS` flags * @param repo the repository in which to look * @param name name of the submodule + * @param ignore the ignore rules to follow * @return 0 on success, <0 on error */ GIT_EXTERN(int) git_submodule_status( unsigned int *status, git_repository *repo, - const char *name); + const char *name, + git_submodule_ignore_t ignore); /** * Get the locations of submodule information. diff --git a/include/git2/types.h b/include/git2/types.h index d1e7cd92c..aa0f31a9a 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -374,7 +374,7 @@ typedef enum { * * The values are: * - * - GIT_SUBMODULE_IGNORE_RESET: reset to the on-disk value. + * - GIT_SUBMODULE_IGNORE_FALLBACK: use the submodule's configuration * - GIT_SUBMODULE_IGNORE_NONE: don't ignore any change - i.e. even an * untracked file, will mark the submodule as dirty. Ignored files are * still ignored, of course. @@ -388,14 +388,12 @@ typedef enum { * when we don't want any particular ignore rule to be specified. */ typedef enum { - GIT_SUBMODULE_IGNORE_RESET = -1, /**< reset to on-disk value */ + GIT_SUBMODULE_IGNORE_FALLBACK = -1, /**< use the submodule's configuration */ GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */ GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */ GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */ GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */ - - GIT_SUBMODULE_IGNORE_DEFAULT = 0 } git_submodule_ignore_t; /** diff --git a/src/checkout.c b/src/checkout.c index d95244a69..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, data->repo, wditem->path) < 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) diff --git a/src/diff_file.c b/src/diff_file.c index 4d9ecc8d8..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, fc->repo, fc->file->path)) < 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/submodule.c b/src/submodule.c index 2faaa734d..c6effdef8 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1067,7 +1067,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->repo, sm->name)) < 0) + if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_FALLBACK)) < 0) goto done; /* @@ -1462,7 +1462,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 */ @@ -1511,7 +1511,7 @@ int git_submodule__status( return 0; } -int git_submodule_status(unsigned int *status, git_repository *repo, const char *name) +int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore) { git_submodule *sm; int error; @@ -1521,7 +1521,7 @@ int git_submodule_status(unsigned int *status, git_repository *repo, const char if ((error = git_submodule_lookup(&sm, repo, name)) < 0) return error; - error = git_submodule__status(status, NULL, NULL, NULL, sm, 0); + error = git_submodule__status(status, NULL, NULL, NULL, sm, ignore); git_submodule_free(sm); return error; diff --git a/tests/diff/tree.c b/tests/diff/tree.c index 6dd17203d..977e21f5b 100644 --- a/tests/diff/tree.c +++ b/tests/diff/tree.c @@ -89,7 +89,7 @@ void test_diff_tree__0(void) } #define DIFF_OPTS(FLAGS, CTXT) \ - {GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_DEFAULT, \ + {GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_FALLBACK, \ {NULL,0}, NULL, NULL, (CTXT), 1} void test_diff_tree__options(void) diff --git a/tests/submodule/status.c b/tests/submodule/status.c index 4d51d8dc0..6721ee92a 100644 --- a/tests/submodule/status.c +++ b/tests/submodule/status.c @@ -107,56 +107,47 @@ void test_submodule_status__ignore_none(void) cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0); } -static int set_sm_ignore(git_submodule *sm, const char *name, void *payload) -{ - git_submodule_ignore_t ignore = *(git_submodule_ignore_t *)payload; - GIT_UNUSED(name); - git_submodule_set_ignore(g_repo, git_submodule_name(sm), ignore); - return 0; -} - void test_submodule_status__ignore_untracked(void) { unsigned int status; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED; rm_submodule("sm_unchanged"); - cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); refute_submodule_exists(g_repo, "not", GIT_EEXISTS); - status = get_submodule_status(g_repo, "sm_changed_index"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0); - status = get_submodule_status(g_repo, "sm_changed_head"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - status = get_submodule_status(g_repo, "sm_changed_file"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0); - status = get_submodule_status(g_repo, "sm_changed_untracked_file"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - status = get_submodule_status(g_repo, "sm_missing_commits"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - status = get_submodule_status(g_repo, "sm_added_and_uncommited"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0); /* removed sm_unchanged for deleted workdir */ - status = get_submodule_status(g_repo, "sm_unchanged"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0); /* now mkdir sm_unchanged to test uninitialized */ cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); - status = get_submodule_status(g_repo, "sm_unchanged"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0); /* update sm_changed_head in index */ add_submodule_to_index("sm_changed_head"); - status = get_submodule_status(g_repo, "sm_changed_head"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0); } @@ -166,42 +157,41 @@ void test_submodule_status__ignore_dirty(void) git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY; rm_submodule("sm_unchanged"); - cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); refute_submodule_exists(g_repo, "not", GIT_EEXISTS); - status = get_submodule_status(g_repo, "sm_changed_index"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - status = get_submodule_status(g_repo, "sm_changed_head"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - status = get_submodule_status(g_repo, "sm_changed_file"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - status = get_submodule_status(g_repo, "sm_changed_untracked_file"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - status = get_submodule_status(g_repo, "sm_missing_commits"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - status = get_submodule_status(g_repo, "sm_added_and_uncommited"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0); /* removed sm_unchanged for deleted workdir */ - status = get_submodule_status(g_repo, "sm_unchanged"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0); /* now mkdir sm_unchanged to test uninitialized */ cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); - status = get_submodule_status(g_repo, "sm_unchanged"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0); /* update sm_changed_head in index */ add_submodule_to_index("sm_changed_head"); - status = get_submodule_status(g_repo, "sm_changed_head"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign)); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0); } @@ -211,42 +201,41 @@ void test_submodule_status__ignore_all(void) git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL; rm_submodule("sm_unchanged"); - cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); refute_submodule_exists(g_repo, "not", GIT_EEXISTS); - status = get_submodule_status(g_repo, "sm_changed_index"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - status = get_submodule_status(g_repo, "sm_changed_head"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - status = get_submodule_status(g_repo, "sm_changed_file"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - status = get_submodule_status(g_repo, "sm_changed_untracked_file"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - status = get_submodule_status(g_repo, "sm_missing_commits"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - status = get_submodule_status(g_repo, "sm_added_and_uncommited"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* removed sm_unchanged for deleted workdir */ - status = get_submodule_status(g_repo, "sm_unchanged"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* now mkdir sm_unchanged to test uninitialized */ cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); - status = get_submodule_status(g_repo, "sm_unchanged"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* update sm_changed_head in index */ add_submodule_to_index("sm_changed_head"); - status = get_submodule_status(g_repo, "sm_changed_head"); + cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); } diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c index 36838228f..d5e02360a 100644 --- a/tests/submodule/submodule_helpers.c +++ b/tests/submodule/submodule_helpers.c @@ -168,7 +168,7 @@ unsigned int get_submodule_status(git_repository *repo, const char *name) assert(repo && name); - cl_git_pass(git_submodule_status(&status, repo, name)); + cl_git_pass(git_submodule_status(&status, repo, name, GIT_SUBMODULE_IGNORE_FALLBACK)); return status; } diff --git a/tests/submodule/update.c b/tests/submodule/update.c index 85870434e..fed6d38b3 100644 --- a/tests/submodule/update.c +++ b/tests/submodule/update.c @@ -103,7 +103,7 @@ void test_submodule_update__update_submodule(void) cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); /* verify the initial state of the submodule */ - cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK)); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -114,7 +114,7 @@ void test_submodule_update__update_submodule(void) cl_git_pass(git_submodule_update(sm, 0, &update_options)); /* verify state */ - cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK)); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -142,7 +142,7 @@ void test_submodule_update__update_and_init_submodule(void) /* get the submodule */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); - cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK)); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -177,7 +177,7 @@ void test_submodule_update__update_already_checked_out_submodule(void) /* Initialize and update the sub repository */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); - cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK)); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -203,7 +203,7 @@ void test_submodule_update__update_already_checked_out_submodule(void) * HEAD commit and index should be updated, but not the workdir. */ - cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK)); git_submodule_free(sm); cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); @@ -255,7 +255,7 @@ void test_submodule_update__update_blocks_on_dirty_wd(void) /* Initialize and update the sub repository */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); - cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK)); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -281,7 +281,7 @@ void test_submodule_update__update_blocks_on_dirty_wd(void) * HEAD commit and index should be updated, but not the workdir. */ - cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK)); git_submodule_free(sm); cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); @@ -332,7 +332,7 @@ void test_submodule_update__can_force_update(void) /* Initialize and update the sub repository */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); - cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK)); cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | @@ -357,7 +357,7 @@ void test_submodule_update__can_force_update(void) * Verify state after checkout of parent repository. The submodule ID in the * HEAD commit and index should be updated, but not the workdir. */ - cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo")); + cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK)); git_submodule_free(sm); cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); From d769a3fdda81be4936a3760bd29f39935b32376a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 May 2015 06:03:21 +0200 Subject: [PATCH 05/18] submodule: bring back finding by path During the removal of the cache, we also removed the ability to use `_lookup()` to search by path rather than name. Bring this logic back. --- src/submodule.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/submodule.c b/src/submodule.c index c6effdef8..37d420525 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -124,6 +124,26 @@ static void submodule_set_lookup_error(int error, const char *name) "Submodule '%s' has not been added yet", name); } +typedef struct { + const char *path; + char *name; +} fbp_data; + +static int find_by_path(const git_config_entry *entry, void *payload) +{ + fbp_data *data = payload; + + 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 0; +} + int git_submodule_lookup( git_submodule **out, /* NULL if user only wants to test existence */ git_repository *repo, @@ -147,6 +167,28 @@ int git_submodule_lookup( return error; } + /* Didn't find it via the name, maybe it's the path */ + if (!sm->url) { + const char *pattern = "submodule\\..*\\.path"; + fbp_data data = { name, NULL }; + + if ((error = git_config_file_foreach_match(mods, pattern, find_by_path, &data)) < 0) + return error; + + if (data.name) { + git__free(sm->name); + sm->name = data.name; + sm->path = git__strdup(name); + GITERR_CHECK_ALLOC(sm->path); + + /* Try to load again with the right name */ + if ((error = git_submodule_reload(sm, false)) < 0) { + git_submodule_free(sm); + return error; + } + } + } + /* If we didn't find the url, consider it missing */ if (!sm->url) { git_submodule_free(sm); From 2278637c4a77df29231b8b7841ba21630ae6c4a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 May 2015 06:14:40 +0200 Subject: [PATCH 06/18] submodule: correct detection of existing submodules During the cache deletion, the check for whether we consider a submodule to exist got changed regarding submodules which are in the worktree but not configured. Instead of checking for the url field to be populated, check the location where we've found it. --- src/submodule.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/submodule.c b/src/submodule.c index 37d420525..f77708516 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -150,15 +150,11 @@ int git_submodule_lookup( const char *name) /* trailing slash is allowed */ { int error; + unsigned int location; git_submodule *sm; - git_config_backend *mods; assert(repo && name); - mods = open_gitmodules(repo, GITMODULES_EXISTING); - if (!mods) - return -1; - if ((error = submodule_alloc(&sm, repo, name)) < 0) return error; @@ -167,13 +163,28 @@ int git_submodule_lookup( return error; } - /* Didn't find it via the name, maybe it's the path */ - if (!sm->url) { + 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 }; - if ((error = git_config_file_foreach_match(mods, pattern, find_by_path, &data)) < 0) + mods = open_gitmodules(repo, GITMODULES_EXISTING); + + if (mods) + error = git_config_file_foreach_match(mods, pattern, find_by_path, &data); + + git_config_file_free(mods); + + if (error < 0) { + git_submodule_free(sm); return error; + } if (data.name) { git__free(sm->name); @@ -189,8 +200,13 @@ int git_submodule_lookup( } } - /* If we didn't find the url, consider it missing */ - if (!sm->url) { + 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; From e8a39f8ed1a95b96415c719c826f0e5715432393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 May 2015 08:35:29 +0200 Subject: [PATCH 07/18] submodule: make `_set_update()` affect the configuration Moving on with the removal of runtime-changing variables, the update setting for a remote is whatever it was when it was looked up. --- include/git2/submodule.h | 21 +++++++--------- src/submodule.c | 52 +++++++++++++++++++++------------------- tests/submodule/modify.c | 32 +++++++++---------------- 3 files changed, 47 insertions(+), 58 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 31218cc94..d36884a3c 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -490,23 +490,18 @@ GIT_EXTERN(git_submodule_update_t) git_submodule_update_strategy( git_submodule *submodule); /** - * Set the update rule for the submodule. + * Set the update rule for the submodule in the configuration * - * The initial value comes from the ".git/config" setting of - * `submodule.$name.update` for this submodule (which is initialized from - * the ".gitmodules" file). Using this function sets the update rule in - * memory for the submodule. Call `git_submodule_save()` to write out the - * new update rule. + * This setting won't affect any existing instances. * - * Calling this again with GIT_SUBMODULE_UPDATE_RESET or calling - * `git_submodule_reload()` will revert the rule to the on disk value. - * - * @param submodule The submodule to update + * @param repo the repository to affect + * @param name the name of the submodule to configure * @param update The new value to use - * @return old value for update + * @return 0 or an error code */ -GIT_EXTERN(git_submodule_update_t) git_submodule_set_update( - git_submodule *submodule, +GIT_EXTERN(int) git_submodule_set_update( + git_repository *repo, + const char *name, git_submodule_update_t update); /** diff --git a/src/submodule.c b/src/submodule.c index f77708516..6d2d095f7 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -806,12 +806,6 @@ int git_submodule_save(git_submodule *submodule) 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, "fetchRecurseSubmodules")) && (val = git_submodule_recurse_to_str(submodule->fetch_recurse)) != NULL) error = git_config_file_set_string(mods, key.ptr, val); @@ -958,24 +952,17 @@ git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule) GIT_SUBMODULE_IGNORE_NONE : submodule->ignore; } -int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore) +static int write_var(git_repository *repo, const char *name, const char *var, const char *val) { git_buf key = GIT_BUF_INIT; git_config_backend *mods; - const char *val; int error; - val = git_submodule_ignore_to_str(ignore); - if (!val) { - giterr_set(GITERR_SUBMODULE, "invalid ignore value"); - return -1; - } - mods = open_gitmodules(repo, GITMODULES_CREATE); if (!mods) return -1; - if ((error = git_buf_printf(&key, "submodule.%s.ignore", name)) < 0) + if ((error = git_buf_printf(&key, "submodule.%s.%s", name, var)) < 0) goto cleanup; error = git_config_file_set_string(mods, key.ptr, val); @@ -986,6 +973,22 @@ cleanup: return error; } +int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore) +{ + const char *val; + int error; + + val = git_submodule_ignore_to_str(ignore); + if (!val) { + giterr_set(GITERR_SUBMODULE, "invalid ignore value"); + return -1; + } + + error = write_var(repo, name, "ignore", val); + + return error; +} + git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) { assert(submodule); @@ -993,19 +996,20 @@ 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; + const char *val; + int error; - assert(submodule); + val = git_submodule_update_to_str(update); + if (!val) { + giterr_set(GITERR_SUBMODULE, "invalid update value"); + return -1; + } - if (update == GIT_SUBMODULE_UPDATE_RESET) - update = submodule->update_default; + error = write_var(repo, name, "update", val); - old = submodule->update; - submodule->update = update; - return old; + return error; } git_submodule_recurse_t git_submodule_fetch_recurse_submodules( diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c index 65f656ea2..a44e79116 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -139,11 +139,21 @@ void test_submodule_modify__set_ignore(void) git_submodule_free(sm); } +void test_submodule_modify__set_update(void) +{ + git_submodule *sm; + + cl_git_pass(git_submodule_set_update(g_repo, "sm_changed_head", GIT_SUBMODULE_UPDATE_REBASE)); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); + cl_assert_equal_i(GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm)); + git_submodule_free(sm); +} + void test_submodule_modify__edit_and_save(void) { git_submodule *sm1, *sm2; char *old_url, *old_branch; - git_submodule_update_t old_update; git_repository *r2; git_submodule_recurse_t old_fetchrecurse; @@ -155,23 +165,17 @@ void test_submodule_modify__edit_and_save(void) /* modify properties of submodule */ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH)); - old_update = git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE); old_fetchrecurse = git_submodule_set_fetch_recurse_submodules( sm1, GIT_SUBMODULE_RECURSE_YES); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1)); - cl_assert_equal_i( - GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm1)); cl_assert_equal_i( GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); /* revert without saving (and confirm setters return old value) */ cl_git_pass(git_submodule_set_url(sm1, old_url)); cl_git_pass(git_submodule_set_branch(sm1, old_branch)); - cl_assert_equal_i( - GIT_SUBMODULE_UPDATE_REBASE, - git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET)); cl_assert_equal_i( GIT_SUBMODULE_RECURSE_YES, git_submodule_set_fetch_recurse_submodules( sm1, GIT_SUBMODULE_RECURSE_RESET)); @@ -179,27 +183,17 @@ void test_submodule_modify__edit_and_save(void) /* check that revert was successful */ cl_assert_equal_s(old_url, git_submodule_url(sm1)); cl_assert_equal_s(old_branch, git_submodule_branch(sm1)); - cl_assert_equal_i((int)old_update, (int)git_submodule_update_strategy(sm1)); cl_assert_equal_i( old_fetchrecurse, git_submodule_fetch_recurse_submodules(sm1)); /* modify properties of submodule (again) */ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH)); - git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE); git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_YES); /* call save */ cl_git_pass(git_submodule_save(sm1)); - /* attempt to "revert" values */ - git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET); - - /* but ignore and update should NOT revert because the RESET - * should now be the newly saved value... - */ - cl_assert_equal_i( - (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update_strategy(sm1)); cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); /* call reload and check that the new values are loaded */ @@ -207,8 +201,6 @@ void test_submodule_modify__edit_and_save(void) cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1)); - cl_assert_equal_i( - (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update_strategy(sm1)); cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); /* unset branch again and verify that the property is deleted in config */ @@ -222,8 +214,6 @@ void test_submodule_modify__edit_and_save(void) cl_git_pass(git_submodule_lookup(&sm2, r2, "sm_changed_head")); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm2)); - cl_assert_equal_i( - GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm2)); cl_assert_equal_i( GIT_SUBMODULE_RECURSE_NO, git_submodule_fetch_recurse_submodules(sm2)); From 4e63642321bbea96b6a06ac28ac3c3e447f15df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 May 2015 09:01:20 +0200 Subject: [PATCH 08/18] submodule: make `_set_update_fetch_recurse_submodules()` affect the config Similarly to the other ones. In this test we copy over testing `RECURSE_YES` which shows an error in our handling of the `YES` variant which we may have to port to the rest. --- include/git2/submodule.h | 14 ++++++------- src/submodule.c | 44 ++++++++++++++++++++++++---------------- tests/submodule/modify.c | 44 +++++++++++++++------------------------- 3 files changed, 49 insertions(+), 53 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index d36884a3c..63572150d 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -519,18 +519,18 @@ GIT_EXTERN(git_submodule_recurse_t) git_submodule_fetch_recurse_submodules( git_submodule *submodule); /** - * Set the fetchRecurseSubmodules rule for a submodule. + * Set the fetchRecurseSubmodules rule for a submodule in the configuration * - * This sets the submodule..fetchRecurseSubmodules value for - * the submodule. You should call `git_submodule_save()` if you want - * to persist the new value. + * This setting won't affect any existing instances. * - * @param submodule The submodule to modify + * @param repo the repository to affect + * @param name the submodule to configure * @param fetch_recurse_submodules Boolean value * @return old value for fetchRecurseSubmodules */ -GIT_EXTERN(git_submodule_recurse_t) git_submodule_set_fetch_recurse_submodules( - git_submodule *submodule, +GIT_EXTERN(int) git_submodule_set_fetch_recurse_submodules( + git_repository *repo, + const char *name, git_submodule_recurse_t fetch_recurse_submodules); /** diff --git a/src/submodule.c b/src/submodule.c index 6d2d095f7..7576ead33 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -769,7 +769,6 @@ 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); @@ -806,12 +805,6 @@ int git_submodule_save(git_submodule *submodule) 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; @@ -965,7 +958,11 @@ static int write_var(git_repository *repo, const char *name, const char *var, co if ((error = git_buf_printf(&key, "submodule.%s.%s", name, var)) < 0) goto cleanup; - error = git_config_file_set_string(mods, key.ptr, val); + 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: @@ -1019,20 +1016,31 @@ 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; + const char *val; + int error; - assert(submodule); + assert(repo && name); - if (fetch_recurse_submodules == GIT_SUBMODULE_RECURSE_RESET) - fetch_recurse_submodules = submodule->fetch_recurse_default; + val = git_submodule_recurse_to_str(recurse); + if (!val) { + switch (recurse) { + case GIT_SUBMODULE_RECURSE_YES: + val = "true"; + break; + case GIT_SUBMODULE_RECURSE_NO: + val = NULL; + break; + default: + giterr_set(GITERR_SUBMODULE, "invalid recurse value"); + return -1; + } + } - old = submodule->fetch_recurse; - submodule->fetch_recurse = fetch_recurse_submodules; - return old; + error = write_var(repo, name, "fetchRecurseSubmodules", val); + + return error; } static int submodule_repo_create( diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c index a44e79116..e26114783 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -150,12 +150,27 @@ void test_submodule_modify__set_update(void) git_submodule_free(sm); } +void test_submodule_modify__set_fetch_recurse_submodules(void) +{ + git_submodule *sm; + + cl_git_pass(git_submodule_set_fetch_recurse_submodules(g_repo, "sm_changed_head", GIT_SUBMODULE_RECURSE_YES)); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); + cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm)); + git_submodule_free(sm); + + git_submodule_set_fetch_recurse_submodules(g_repo, "sm_changed_head", GIT_SUBMODULE_RECURSE_ONDEMAND); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); + cl_assert_equal_i(GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm)); + git_submodule_free(sm); +} + void test_submodule_modify__edit_and_save(void) { git_submodule *sm1, *sm2; char *old_url, *old_branch; git_repository *r2; - git_submodule_recurse_t old_fetchrecurse; cl_git_pass(git_submodule_lookup(&sm1, g_repo, "sm_changed_head")); @@ -165,43 +180,29 @@ void test_submodule_modify__edit_and_save(void) /* modify properties of submodule */ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH)); - old_fetchrecurse = git_submodule_set_fetch_recurse_submodules( - sm1, GIT_SUBMODULE_RECURSE_YES); - cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1)); - cl_assert_equal_i( - GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); /* revert without saving (and confirm setters return old value) */ cl_git_pass(git_submodule_set_url(sm1, old_url)); cl_git_pass(git_submodule_set_branch(sm1, old_branch)); - cl_assert_equal_i( - GIT_SUBMODULE_RECURSE_YES, git_submodule_set_fetch_recurse_submodules( - sm1, GIT_SUBMODULE_RECURSE_RESET)); /* check that revert was successful */ cl_assert_equal_s(old_url, git_submodule_url(sm1)); cl_assert_equal_s(old_branch, git_submodule_branch(sm1)); - cl_assert_equal_i( - old_fetchrecurse, git_submodule_fetch_recurse_submodules(sm1)); /* modify properties of submodule (again) */ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH)); - git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_YES); /* call save */ cl_git_pass(git_submodule_save(sm1)); - cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); - /* call reload and check that the new values are loaded */ cl_git_pass(git_submodule_reload(sm1, 0)); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1)); - cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); /* unset branch again and verify that the property is deleted in config */ cl_git_pass(git_submodule_set_branch(sm1, NULL)); @@ -214,19 +215,6 @@ void test_submodule_modify__edit_and_save(void) cl_git_pass(git_submodule_lookup(&sm2, r2, "sm_changed_head")); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm2)); - cl_assert_equal_i( - GIT_SUBMODULE_RECURSE_NO, git_submodule_fetch_recurse_submodules(sm2)); - - /* set fetchRecurseSubmodules on-demand */ - cl_git_pass(git_submodule_reload(sm1, 0)); - git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_ONDEMAND); - cl_assert_equal_i( - GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1)); - /* call save */ - cl_git_pass(git_submodule_save(sm1)); - cl_git_pass(git_submodule_reload(sm1, 0)); - cl_assert_equal_i( - GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1)); git_submodule_free(sm1); git_submodule_free(sm2); From 486ba4cdd3d30c202d0c7ed202d6122a7e2b5164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 May 2015 09:13:52 +0200 Subject: [PATCH 09/18] submodule: make `_set_branch()` affect the configuration --- include/git2/submodule.h | 13 +++---- src/submodule.c | 76 +++++++++++++++------------------------- tests/submodule/modify.c | 34 ++++++++++-------- 3 files changed, 53 insertions(+), 70 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 63572150d..03b9efe96 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -373,20 +373,17 @@ GIT_EXTERN(int) git_submodule_resolve_url(git_buf *out, git_repository *repo, co GIT_EXTERN(const char *) git_submodule_branch(git_submodule *submodule); /** - * Set the branch for the submodule. + * Set the branch for the submodule in the configuration * - * This sets the branch in memory for the submodule. This will be used for - * any following submodule actions while this submodule data is in memory. - * - * After calling this, you may wish to call `git_submodule_save()` to write - * the changes back to the ".gitmodules" file and `git_submodule_sync()` to + * After calling this, you may wish to call `git_submodule_sync()` to * write the changes to the checked out submodule repository. * - * @param submodule Pointer to the submodule object + * @param repo the repository to affect + * @param name the name of the submodule to configure * @param branch Branch that should be used for the submodule * @return 0 on success, <0 on failure */ -GIT_EXTERN(int) git_submodule_set_branch(git_submodule *submodule, const char *branch); +GIT_EXTERN(int) git_submodule_set_branch(git_repository *repo, const char *name, const char *branch); /** * Set the URL for the submodule. diff --git a/src/submodule.c b/src/submodule.c index 7576ead33..3ad5d3eb3 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -792,19 +792,6 @@ int git_submodule_save(git_submodule *submodule) (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; - /* update internal defaults */ submodule->ignore_default = submodule->ignore; @@ -864,25 +851,43 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur return error; } +static int write_var(git_repository *repo, const char *name, const char *var, const char *val) +{ + 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; +} + const char *git_submodule_branch(git_submodule *submodule) { assert(submodule); return submodule->branch; } -int git_submodule_set_branch(git_submodule *submodule, const char *branch) +int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch) { - assert(submodule); - git__free(submodule->branch); - submodule->branch = NULL; + assert(repo && name); - if (branch != NULL) { - submodule->branch = git__strdup(branch); - GITERR_CHECK_ALLOC(submodule->branch); - } - - return 0; + return write_var(repo, name, "branch", branch); } int git_submodule_set_url(git_submodule *submodule, const char *url) @@ -945,31 +950,6 @@ git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule) GIT_SUBMODULE_IGNORE_NONE : submodule->ignore; } -static int write_var(git_repository *repo, const char *name, const char *var, const char *val) -{ - 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_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore) { const char *val; diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c index e26114783..d0902b3e1 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -166,34 +166,47 @@ void test_submodule_modify__set_fetch_recurse_submodules(void) git_submodule_free(sm); } +void test_submodule_modify__set_branch(void) +{ + git_submodule *sm; + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); + cl_assert(git_submodule_branch(sm) == NULL); + git_submodule_free(sm); + + cl_git_pass(git_submodule_set_branch(g_repo, "sm_changed_head", SM_LIBGIT2_BRANCH)); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); + cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm)); + git_submodule_free(sm); + + cl_git_pass(git_submodule_set_branch(g_repo, "sm_changed_head", NULL)); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); + cl_assert(git_submodule_branch(sm) == NULL); + git_submodule_free(sm); +} + void test_submodule_modify__edit_and_save(void) { git_submodule *sm1, *sm2; - char *old_url, *old_branch; + char *old_url; git_repository *r2; cl_git_pass(git_submodule_lookup(&sm1, g_repo, "sm_changed_head")); old_url = git__strdup(git_submodule_url(sm1)); - old_branch = NULL; /* modify properties of submodule */ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); - cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH)); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); - cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1)); /* revert without saving (and confirm setters return old value) */ cl_git_pass(git_submodule_set_url(sm1, old_url)); - cl_git_pass(git_submodule_set_branch(sm1, old_branch)); /* check that revert was successful */ cl_assert_equal_s(old_url, git_submodule_url(sm1)); - cl_assert_equal_s(old_branch, git_submodule_branch(sm1)); /* modify properties of submodule (again) */ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); - cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH)); /* call save */ cl_git_pass(git_submodule_save(sm1)); @@ -202,13 +215,6 @@ void test_submodule_modify__edit_and_save(void) cl_git_pass(git_submodule_reload(sm1, 0)); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); - cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1)); - - /* unset branch again and verify that the property is deleted in config */ - cl_git_pass(git_submodule_set_branch(sm1, NULL)); - cl_git_pass(git_submodule_save(sm1)); - cl_git_pass(git_submodule_reload(sm1, 0)); - cl_assert_equal_s(NULL, git_submodule_branch(sm1)); /* open a second copy of the repo and compare submodule */ cl_git_pass(git_repository_open(&r2, "submod2")); From d6073b30f38f95aa25b1545cccac38261b359de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 May 2015 09:22:35 +0200 Subject: [PATCH 10/18] submodule: make `_set_url()` affect the configuration With this one, we can get rid of the edit_and_save test. --- include/git2/submodule.h | 12 +++++------ src/submodule.c | 15 +++----------- tests/submodule/init.c | 6 +++--- tests/submodule/modify.c | 45 ++++++---------------------------------- 4 files changed, 17 insertions(+), 61 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 03b9efe96..16895ac3b 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -386,20 +386,18 @@ GIT_EXTERN(const char *) git_submodule_branch(git_submodule *submodule); GIT_EXTERN(int) git_submodule_set_branch(git_repository *repo, const char *name, const char *branch); /** - * Set the URL for the submodule. + * Set the URL for the submodule in the configuration * - * This sets the URL in memory for the submodule. This will be used for - * any following submodule actions while this submodule data is in memory. * - * After calling this, you may wish to call `git_submodule_save()` to write - * the changes back to the ".gitmodules" file and `git_submodule_sync()` to + * After calling this, you may wish to call `git_submodule_sync()` to * write the changes to the checked out submodule repository. * - * @param submodule Pointer to the submodule object + * @param repo the repository to affect + * @param name the name of the submodule to configure * @param url URL that should be used for the submodule * @return 0 on success, <0 on failure */ -GIT_EXTERN(int) git_submodule_set_url(git_submodule *submodule, const char *url); +GIT_EXTERN(int) git_submodule_set_url(git_repository *repo, const char *name, const char *url); /** * Get the OID for the submodule in the index. diff --git a/src/submodule.c b/src/submodule.c index 3ad5d3eb3..24d31fe77 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -788,10 +788,6 @@ int git_submodule_save(git_submodule *submodule) (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; - /* update internal defaults */ submodule->ignore_default = submodule->ignore; @@ -890,16 +886,11 @@ int git_submodule_set_branch(git_repository *repo, const char *name, const char return write_var(repo, name, "branch", branch); } -int git_submodule_set_url(git_submodule *submodule, const char *url) +int git_submodule_set_url(git_repository *repo, const char *name, const char *url) { - assert(submodule && url); + assert(repo && name && url); - git__free(submodule->url); - - submodule->url = git__strdup(url); - GITERR_CHECK_ALLOC(submodule->url); - - return 0; + return write_var(repo, name, "url", url); } const git_oid *git_submodule_index_id(git_submodule *submodule) diff --git a/tests/submodule/init.c b/tests/submodule/init.c index dbde0f284..9e0cf5753 100644 --- a/tests/submodule/init.c +++ b/tests/submodule/init.c @@ -23,10 +23,10 @@ void test_submodule_init__absolute_url(void) cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0); cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git")); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); - /* write the absolute url to the .gitmodules file*/ - cl_git_pass(git_submodule_set_url(sm, absolute_url.ptr)); + cl_git_pass(git_submodule_set_url(g_repo, "testrepo", absolute_url.ptr)); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); /* verify that the .gitmodules is set with an absolute path*/ cl_assert_equal_s(absolute_url.ptr, git_submodule_url(sm)); diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c index d0902b3e1..9c387a3b5 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -185,47 +185,14 @@ void test_submodule_modify__set_branch(void) git_submodule_free(sm); } -void test_submodule_modify__edit_and_save(void) +void test_submodule_modify__set_url(void) { - git_submodule *sm1, *sm2; - char *old_url; - git_repository *r2; + git_submodule *sm; - cl_git_pass(git_submodule_lookup(&sm1, g_repo, "sm_changed_head")); - - old_url = git__strdup(git_submodule_url(sm1)); - - /* modify properties of submodule */ - cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); - cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); - - /* revert without saving (and confirm setters return old value) */ - cl_git_pass(git_submodule_set_url(sm1, old_url)); - - /* check that revert was successful */ - cl_assert_equal_s(old_url, git_submodule_url(sm1)); - - /* modify properties of submodule (again) */ - cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); - - /* call save */ - cl_git_pass(git_submodule_save(sm1)); - - /* call reload and check that the new values are loaded */ - cl_git_pass(git_submodule_reload(sm1, 0)); - - cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); - - /* open a second copy of the repo and compare submodule */ - cl_git_pass(git_repository_open(&r2, "submod2")); - cl_git_pass(git_submodule_lookup(&sm2, r2, "sm_changed_head")); - - cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm2)); - - git_submodule_free(sm1); - git_submodule_free(sm2); - git_repository_free(r2); - git__free(old_url); + cl_git_pass(git_submodule_set_url(g_repo, "sm_changed_head", SM_LIBGIT2_URL)); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); + cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm)); + git_submodule_free(sm); } void test_submodule_modify__save_last(void) From 961861fafad45c9eab29f523e51261ba78ab10be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 May 2015 09:25:17 +0200 Subject: [PATCH 11/18] submodule: get rid of `_save()` We no longer have any setters which affect an instance, so `git_submodule_save()` is no longer relevant. --- include/git2/submodule.h | 14 -------------- src/submodule.c | 38 -------------------------------------- tests/submodule/modify.c | 9 --------- 3 files changed, 61 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 16895ac3b..0169d1c27 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -300,20 +300,6 @@ GIT_EXTERN(int) git_submodule_add_to_index( git_submodule *submodule, int write_index); -/** - * Write submodule settings to .gitmodules file. - * - * This commits any in-memory changes to the submodule to the gitmodules - * file on disk. You may also be interested in `git_submodule_init()` which - * writes submodule info to ".git/config" (which is better for local changes - * to submodule settings) and/or `git_submodule_sync()` which writes - * settings about remotes to the actual submodule repository. - * - * @param submodule The submodule to write. - * @return 0 on success, <0 on failure. - */ -GIT_EXTERN(int) git_submodule_save(git_submodule *submodule); - /** * Get the containing repository for a submodule. * diff --git a/src/submodule.c b/src/submodule.c index 24d31fe77..738d57921 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -764,44 +764,6 @@ const char *git_submodule_recurse_to_str(git_submodule_recurse_t recurse) return NULL; } -int git_submodule_save(git_submodule *submodule) -{ - int error = 0; - git_config_backend *mods; - git_buf key = GIT_BUF_INIT; - - assert(submodule); - - mods = open_gitmodules(submodule->repo, 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; - - /* 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); diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c index 9c387a3b5..77f86e514 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -194,12 +194,3 @@ void test_submodule_modify__set_url(void) cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm)); git_submodule_free(sm); } - -void test_submodule_modify__save_last(void) -{ - git_submodule *sm; - - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); - cl_git_pass(git_submodule_save(sm)); - git_submodule_free(sm); -} From adb58f7d646091f835d900867e22cb8329799dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 May 2015 16:21:57 +0200 Subject: [PATCH 12/18] submodule: fix potential leak in the tests --- tests/submodule/submodule_helpers.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c index d5e02360a..2647e1514 100644 --- a/tests/submodule/submodule_helpers.c +++ b/tests/submodule/submodule_helpers.c @@ -156,10 +156,9 @@ void refute__submodule_exists( git_repository *repo, const char *name, int expected_error, const char *msg, const char *file, int line) { - git_submodule *sm; clar__assert_equal( file, line, msg, 1, "%i", - expected_error, (int)(git_submodule_lookup(&sm, repo, name))); + expected_error, (int)(git_submodule_lookup(NULL, repo, name))); } unsigned int get_submodule_status(git_repository *repo, const char *name) From 4536574acc85d8c78ceeac872eed6e4f79ef9aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 5 May 2015 16:29:38 +0200 Subject: [PATCH 13/18] submodule: test more accurately for non-existence The current code will always fail, but only because it's asking for a string on a live config. Take a snapshot and make sure we fail with ENOTFOUND instead of any old error. --- tests/submodule/modify.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c index 77f86e514..81c34de46 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -41,10 +41,10 @@ void test_submodule_modify__init(void) git_config_free(cfg); /* confirm no submodule data in config */ - cl_git_pass(git_repository_config(&cfg, g_repo)); - cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url")); - cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url")); - cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url")); + cl_git_pass(git_repository_config_snapshot(&cfg, g_repo)); + cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_unchanged.url")); + cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_changed_head.url")); + cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url")); git_config_free(cfg); /* call init and see that settings are copied */ From 7d6dacdc17303305748fed226af35d8a4e5abc77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 9 May 2015 10:42:00 +0200 Subject: [PATCH 14/18] examples: adjust to submodule status API change --- examples/status.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/examples/status.c b/examples/status.c index 62cb5b24f..8e242dd39 100644 --- a/examples/status.c +++ b/examples/status.c @@ -384,25 +384,19 @@ static void print_short(git_repository *repo, git_status_list *status) if (s->index_to_workdir && s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT) { - git_submodule *sm = NULL; unsigned int smstatus = 0; - if (!git_submodule_lookup( - &sm, repo, s->index_to_workdir->new_file.path)) { - - if (!git_submodule_status(&smstatus, sm)) { - if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED) - extra = " (new commits)"; - else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) - extra = " (modified content)"; - else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) - extra = " (modified content)"; - else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED) - extra = " (untracked content)"; - } + if (!git_submodule_status(&smstatus, repo, s->index_to_workdir->new_file.path, + GIT_SUBMODULE_IGNORE_FALLBACK)) { + if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED) + extra = " (new commits)"; + else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) + extra = " (modified content)"; + else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) + extra = " (modified content)"; + else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED) + extra = " (untracked content)"; } - - git_submodule_free(sm); } /** From 15c381031391cbc24691f9bd009b991eaa440d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 9 May 2015 11:22:27 +0200 Subject: [PATCH 15/18] config: provide a function to reverse-lookup mapped cvars --- src/config.c | 20 ++++++++++++++++++++ src/config.h | 6 ++++++ 2 files changed, 26 insertions(+) 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 From c4e3a3dbfa3abbe155d47d3040ecbb2c46242a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 9 May 2015 11:22:57 +0200 Subject: [PATCH 16/18] submodule: handle writing out all enum values for settings We currently do not handle those enum values which require us to set "true" or unset variables in all cases. Use a common function which does understand this by looking at our mapping directly. --- src/submodule.c | 82 +++++++++++++------------------------------------ src/submodule.h | 4 --- 2 files changed, 21 insertions(+), 65 deletions(-) diff --git a/src/submodule.c b/src/submodule.c index 738d57921..45163da1b 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -737,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; @@ -755,15 +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; -} - git_repository *git_submodule_owner(git_submodule *submodule) { assert(submodule); @@ -834,6 +816,22 @@ cleanup: return error; } +static int write_mapped_var(git_repository *repo, const char *name, git_cvar_map *maps, size_t nmaps, const char *var, int ival) +{ + git_cvar_t type; + const char *val; + + if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) { + giterr_set(GITERR_SUBMODULE, "invalid value for %s", var); + return -1; + } + + if (type == GIT_CVAR_TRUE) + val = "true"; + + return write_var(repo, name, var, val); +} + const char *git_submodule_branch(git_submodule *submodule) { assert(submodule); @@ -905,18 +903,9 @@ git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule) int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore) { - const char *val; - int error; + assert(repo && name); - val = git_submodule_ignore_to_str(ignore); - if (!val) { - giterr_set(GITERR_SUBMODULE, "invalid ignore value"); - return -1; - } - - error = write_var(repo, name, "ignore", val); - - return error; + 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) @@ -928,18 +917,9 @@ git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update) { - const char *val; - int error; + assert(repo && name); - val = git_submodule_update_to_str(update); - if (!val) { - giterr_set(GITERR_SUBMODULE, "invalid update value"); - return -1; - } - - error = write_var(repo, name, "update", val); - - return error; + 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( @@ -951,29 +931,9 @@ git_submodule_recurse_t git_submodule_fetch_recurse_submodules( int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse) { - const char *val; - int error; - assert(repo && name); - val = git_submodule_recurse_to_str(recurse); - if (!val) { - switch (recurse) { - case GIT_SUBMODULE_RECURSE_YES: - val = "true"; - break; - case GIT_SUBMODULE_RECURSE_NO: - val = NULL; - break; - default: - giterr_set(GITERR_SUBMODULE, "invalid recurse value"); - return -1; - } - } - - error = write_var(repo, name, "fetchRecurseSubmodules", val); - - return error; + return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse); } static int submodule_repo_create( diff --git a/src/submodule.h b/src/submodule.h index d67796193..2ef2031b3 100644 --- a/src/submodule.h +++ b/src/submodule.h @@ -143,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 From 0496330004e64680903131cbae2ac27610517cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 11 May 2015 11:57:05 +0200 Subject: [PATCH 17/18] submodule: test unsetting config options In addition to mapping enums to strings in the configuration, we need to know to delete the configuration option when given the "none" or "no" option. --- tests/submodule/modify.c | 50 ++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c index 81c34de46..f7a089e72 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -128,42 +128,58 @@ void test_submodule_modify__sync(void) git_submodule_free(sm3); } -void test_submodule_modify__set_ignore(void) +void assert_ignore_change(git_submodule_ignore_t ignore) { git_submodule *sm; - cl_git_pass(git_submodule_set_ignore(g_repo, "sm_changed_head", GIT_SUBMODULE_IGNORE_UNTRACKED)); + cl_git_pass(git_submodule_set_ignore(g_repo, "sm_changed_head", ignore)); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_assert_equal_i(GIT_SUBMODULE_IGNORE_UNTRACKED, git_submodule_ignore(sm)); + cl_assert_equal_i(ignore, git_submodule_ignore(sm)); + git_submodule_free(sm); +} + +void test_submodule_modify__set_ignore(void) +{ + assert_ignore_change(GIT_SUBMODULE_IGNORE_UNTRACKED); + assert_ignore_change(GIT_SUBMODULE_IGNORE_NONE); + assert_ignore_change(GIT_SUBMODULE_IGNORE_ALL); +} + +void assert_update_change(git_submodule_update_t update) +{ + git_submodule *sm; + + cl_git_pass(git_submodule_set_update(g_repo, "sm_changed_head", update)); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); + cl_assert_equal_i(update, git_submodule_update_strategy(sm)); git_submodule_free(sm); } void test_submodule_modify__set_update(void) +{ + assert_update_change(GIT_SUBMODULE_UPDATE_REBASE); + assert_update_change(GIT_SUBMODULE_UPDATE_NONE); + assert_update_change(GIT_SUBMODULE_UPDATE_CHECKOUT); +} + +void assert_recurse_change(git_submodule_recurse_t recurse) { git_submodule *sm; - cl_git_pass(git_submodule_set_update(g_repo, "sm_changed_head", GIT_SUBMODULE_UPDATE_REBASE)); + cl_git_pass(git_submodule_set_fetch_recurse_submodules(g_repo, "sm_changed_head", recurse)); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_assert_equal_i(GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm)); + cl_assert_equal_i(recurse, git_submodule_fetch_recurse_submodules(sm)); git_submodule_free(sm); } void test_submodule_modify__set_fetch_recurse_submodules(void) { - git_submodule *sm; - - cl_git_pass(git_submodule_set_fetch_recurse_submodules(g_repo, "sm_changed_head", GIT_SUBMODULE_RECURSE_YES)); - - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm)); - git_submodule_free(sm); - - git_submodule_set_fetch_recurse_submodules(g_repo, "sm_changed_head", GIT_SUBMODULE_RECURSE_ONDEMAND); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_assert_equal_i(GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm)); - git_submodule_free(sm); + assert_recurse_change(GIT_SUBMODULE_RECURSE_YES); + assert_recurse_change(GIT_SUBMODULE_RECURSE_NO); + assert_recurse_change(GIT_SUBMODULE_RECURSE_ONDEMAND); } void test_submodule_modify__set_branch(void) From 783672fa5be4f0e9dce72bcd74690258bdbac0a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 11 May 2015 12:01:08 +0200 Subject: [PATCH 18/18] submodule: remove the RESET enum values These are not useful anymore, as we don't affect the instance's configuration. --- include/git2/submodule.h | 4 +--- include/git2/types.h | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 0169d1c27..cbafccd1b 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -459,9 +459,7 @@ GIT_EXTERN(int) git_submodule_set_ignore( * Get the update rule that will be used for the submodule. * * This value controls the behavior of the `git submodule update` command. - * There are four useful values documented with `git_submodule_update_t` - * plus the `GIT_SUBMODULE_UPDATE_RESET` which can be used to revert to - * the on-disk setting. + * There are four useful values documented with `git_submodule_update_t`. * * @param submodule The submodule to check * @return The current git_submodule_update_t value that will be used diff --git a/include/git2/types.h b/include/git2/types.h index aa0f31a9a..3da0d4a26 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -337,7 +337,6 @@ typedef struct git_submodule git_submodule; * * The values are: * - * - GIT_SUBMODULE_UPDATE_RESET: reset to the on-disk value. * - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is * updated, checkout the new detached HEAD to the submodule directory. * - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked @@ -350,8 +349,6 @@ typedef struct git_submodule git_submodule; * when we don't want any particular update rule to be specified. */ typedef enum { - GIT_SUBMODULE_UPDATE_RESET = -1, - GIT_SUBMODULE_UPDATE_CHECKOUT = 1, GIT_SUBMODULE_UPDATE_REBASE = 2, GIT_SUBMODULE_UPDATE_MERGE = 3, @@ -401,15 +398,12 @@ typedef enum { * * Represent the value of `submodule.$name.fetchRecurseSubmodules` * - * * GIT_SUBMODULE_RECURSE_RESET - reset to the on-disk value * * GIT_SUBMODULE_RECURSE_NO - do no recurse into submodules * * GIT_SUBMODULE_RECURSE_YES - recurse into submodules * * GIT_SUBMODULE_RECURSE_ONDEMAND - recurse into submodules only when * commit not already in local clone */ typedef enum { - GIT_SUBMODULE_RECURSE_RESET = -1, - GIT_SUBMODULE_RECURSE_NO = 0, GIT_SUBMODULE_RECURSE_YES = 1, GIT_SUBMODULE_RECURSE_ONDEMAND = 2,