diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2015-06-24 23:34:40 +0200 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2015-06-24 23:34:40 +0200 |
commit | daacf96d101b9d2100a5028090b5af5249d8893d (patch) | |
tree | 450942a1431c22f634f9c16916408bd80f165702 | |
parent | e1f434f8643f26d48ee8de21d715069de76b14e1 (diff) | |
parent | 783672fa5be4f0e9dce72bcd74690258bdbac0a9 (diff) | |
download | libgit2-daacf96d101b9d2100a5028090b5af5249d8893d.tar.gz |
Merge pull request #3097 from libgit2/cmn/submodule-config-state
Remove run-time configuration settings from submodules
-rw-r--r-- | examples/status.c | 26 | ||||
-rw-r--r-- | include/git2/diff.h | 2 | ||||
-rw-r--r-- | include/git2/submodule.h | 113 | ||||
-rw-r--r-- | include/git2/types.h | 12 | ||||
-rw-r--r-- | src/checkout.c | 10 | ||||
-rw-r--r-- | src/config.c | 20 | ||||
-rw-r--r-- | src/config.h | 6 | ||||
-rw-r--r-- | src/diff_file.c | 2 | ||||
-rw-r--r-- | src/repository.c | 1 | ||||
-rw-r--r-- | src/repository.h | 1 | ||||
-rw-r--r-- | src/submodule.c | 1037 | ||||
-rw-r--r-- | src/submodule.h | 24 | ||||
-rw-r--r-- | tests/diff/submodules.c | 16 | ||||
-rw-r--r-- | tests/diff/tree.c | 2 | ||||
-rw-r--r-- | tests/submodule/init.c | 6 | ||||
-rw-r--r-- | tests/submodule/modify.c | 203 | ||||
-rw-r--r-- | tests/submodule/nosubs.c | 44 | ||||
-rw-r--r-- | tests/submodule/status.c | 65 | ||||
-rw-r--r-- | tests/submodule/submodule_helpers.c | 11 | ||||
-rw-r--r-- | tests/submodule/update.c | 30 |
20 files changed, 616 insertions, 1015 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); } /** 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 48faf8a49..cbafccd1b 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -301,20 +301,6 @@ GIT_EXTERN(int) git_submodule_add_to_index( 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. * * This returns a pointer to the repository that contains the submodule. @@ -373,36 +359,31 @@ 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. + * 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. @@ -452,9 +433,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,32 +441,25 @@ GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore( git_submodule *submodule); /** - * Set the ignore rule for the submodule. - * - * This sets the in-memory ignore rule for the submodule which will - * control the behavior of `git_submodule_status()`. + * Set the ignore rule for the submodule in the configuration * - * To make changes persistent, call `git_submodule_save()` to write the - * value to disk (in the ".gitmodules" and ".git/config" files). + * This does not affect any currently-loaded instances. * - * 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); /** * 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 @@ -498,23 +469,18 @@ GIT_EXTERN(git_submodule_update_t) git_submodule_update_strategy( git_submodule *submodule); /** - * Set the update rule for the submodule. - * - * 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. + * Set the update rule for the submodule in the configuration * - * Calling this again with GIT_SUBMODULE_UPDATE_RESET or calling - * `git_submodule_reload()` will revert the rule to the on disk value. + * This setting won't affect any existing instances. * - * @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); /** @@ -532,18 +498,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.<name>.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); /** @@ -634,16 +600,19 @@ 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 + * @param ignore the ignore rules to follow * @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, + git_submodule_ignore_t ignore); /** * Get the locations of submodule information. diff --git a/include/git2/types.h b/include/git2/types.h index c97e5ba61..e975c6097 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -349,7 +349,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 @@ -362,8 +361,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, @@ -386,7 +383,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. @@ -400,14 +397,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; /** @@ -415,15 +410,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, diff --git a/src/checkout.c b/src/checkout.c index 2893c63de..351e8b0ef 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -180,7 +180,7 @@ static bool checkout_is_workdir_modified( return true; } - if (git_submodule_status(&sm_status, sm) < 0 || + if (git_submodule_status(&sm_status, data->repo, wditem->path, GIT_SUBMODULE_IGNORE_FALLBACK) < 0 || GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) rval = true; else if ((sm_oid = git_submodule_wd_id(sm)) == NULL) @@ -1860,11 +1860,6 @@ static int checkout_create_submodules( git_diff_delta *delta; size_t i; - /* initial reload of submodules if .gitmodules was changed */ - if (data->reload_submodules && - (error = git_submodule_reload_all(data->repo, 1)) < 0) - return error; - git_vector_foreach(&data->diff->deltas, i, delta) { if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) { /* this has a blocker directory that should only be removed iff @@ -1885,8 +1880,7 @@ static int checkout_create_submodules( } } - /* final reload once submodules have been updated */ - return git_submodule_reload_all(data->repo, 1); + return 0; } static int checkout_lookup_head_tree(git_tree **out, git_repository *repo) diff --git a/src/config.c b/src/config.c index 1400b9513..77cf573e6 100644 --- a/src/config.c +++ b/src/config.c @@ -1194,6 +1194,26 @@ fail_parse: return -1; } +int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out, + const git_cvar_map *maps, size_t map_n, int enum_val) +{ + size_t i; + + for (i = 0; i < map_n; i++) { + const git_cvar_map *m = &maps[i]; + + if (m->map_value != enum_val) + continue; + + *type_out = m->cvar_type; + *str_out = m->str_match; + return 0; + } + + giterr_set(GITERR_CONFIG, "invalid enum value"); + return GIT_ENOTFOUND; +} + int git_config_parse_bool(int *out, const char *value) { if (git__parse_bool(out, value) == 0) diff --git a/src/config.h b/src/config.h index 691868b1d..f257cc90f 100644 --- a/src/config.h +++ b/src/config.h @@ -82,4 +82,10 @@ extern int git_config__get_int_force( extern int git_config__cvar( int *out, git_config *config, git_cvar_cached cvar); +/** + * The opposite of git_config_lookup_map_value, we take an enum value + * and map it to the string or bool value on the config. + */ +int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out, + const git_cvar_map *maps, size_t map_n, int enum_val); #endif diff --git a/src/diff_file.c b/src/diff_file.c index cef4bc169..28edcd4c1 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -186,7 +186,7 @@ static int diff_file_content_commit_to_str( return error; } - if ((error = git_submodule_status(&sm_status, sm)) < 0) { + if ((error = git_submodule_status(&sm_status, fc->repo, fc->file->path, GIT_SUBMODULE_IGNORE_FALLBACK)) < 0) { git_submodule_free(sm); return error; } diff --git a/src/repository.c b/src/repository.c index de1c0dc1f..82d998124 100644 --- a/src/repository.c +++ b/src/repository.c @@ -111,7 +111,6 @@ void git_repository__cleanup(git_repository *repo) git_cache_clear(&repo->objects); git_attr_cache_flush(repo); - git_submodule_cache_free(repo); set_config(repo, NULL); set_index(repo, NULL); diff --git a/src/repository.h b/src/repository.h index 253287368..fd679b483 100644 --- a/src/repository.h +++ b/src/repository.h @@ -121,7 +121,6 @@ struct git_repository { git_refdb *_refdb; git_config *_config; git_index *_index; - git_submodule_cache *_submodules; git_cache objects; git_attr_cache *attrcache; diff --git a/src/submodule.c b/src/submodule.c index 246502e99..45163da1b 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -87,17 +87,16 @@ __KHASH_IMPL( str, static kh_inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash) -static int submodule_cache_init(git_repository *repo, int refresh); -static void submodule_cache_free(git_submodule_cache *cache); - -static git_config_backend *open_gitmodules(git_submodule_cache *, int gitmod); +static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name); +static git_config_backend *open_gitmodules(git_repository *repo, int gitmod); static int get_url_base(git_buf *url, git_repository *repo); static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo); -static int submodule_get(git_submodule **, git_submodule_cache *, const char *, const char *); static int submodule_load_from_config(const git_config_entry *, void *); static int submodule_load_from_wd_lite(git_submodule *); static void submodule_get_index_status(unsigned int *, git_submodule *); static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t); +static void submodule_update_from_index_entry(git_submodule *sm, const git_index_entry *ie); +static void submodule_update_from_head_data(git_submodule *sm, mode_t mode, const git_oid *id); static int submodule_cmp(const void *a, const void *b) { @@ -111,69 +110,10 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix) return git_buf_puts(key, suffix); } -/* lookup submodule or return ENOTFOUND if it doesn't exist */ -static int submodule_lookup( - git_submodule **out, - git_submodule_cache *cache, - const char *name, - const char *alternate) -{ - khiter_t pos; - - /* lock cache */ - - pos = git_strmap_lookup_index(cache->submodules, name); - - if (!git_strmap_valid_index(cache->submodules, pos) && alternate) - pos = git_strmap_lookup_index(cache->submodules, alternate); - - if (!git_strmap_valid_index(cache->submodules, pos)) { - /* unlock cache */ - return GIT_ENOTFOUND; /* don't set error - caller will cope */ - } - - if (out != NULL) { - git_submodule *sm = git_strmap_value_at(cache->submodules, pos); - GIT_REFCOUNT_INC(sm); - *out = sm; - } - - /* unlock cache */ - - return 0; -} - -/* clear a set of flags on all submodules */ -static void submodule_cache_clear_flags( - git_submodule_cache *cache, uint32_t mask) -{ - git_submodule *sm; - uint32_t inverted_mask = ~mask; - - git_strmap_foreach_value(cache->submodules, sm, { - sm->flags &= inverted_mask; - }); -} - /* * PUBLIC APIS */ -bool git_submodule__is_submodule(git_repository *repo, const char *name) -{ - git_strmap *map; - - if (submodule_cache_init(repo, CACHE_OK) < 0) { - giterr_clear(); - return false; - } - - if (!repo->_submodules || !(map = repo->_submodules->submodules)) - return false; - - return git_strmap_valid_index(map, git_strmap_lookup_index(map, name)); -} - static void submodule_set_lookup_error(int error, const char *name) { if (!error) @@ -184,22 +124,24 @@ static void submodule_set_lookup_error(int error, const char *name) "Submodule '%s' has not been added yet", name); } -int git_submodule__lookup( - git_submodule **out, /* NULL if user only wants to test existence */ - git_repository *repo, - const char *name) /* trailing slash is allowed */ -{ - int error; +typedef struct { + const char *path; + char *name; +} fbp_data; - assert(repo && name); - - if ((error = submodule_cache_init(repo, CACHE_OK)) < 0) - return error; +static int find_by_path(const git_config_entry *entry, void *payload) +{ + fbp_data *data = payload; - if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) - submodule_set_lookup_error(error, name); + if (!strcmp(entry->value, data->path)) { + const char *fdot, *ldot; + fdot = strchr(entry->name, '.'); + ldot = strrchr(entry->name, '.'); + data->name = git__strndup(fdot + 1, ldot - fdot - 1); + GITERR_CHECK_ALLOC(data->name); + } - return error; + return 0; } int git_submodule_lookup( @@ -208,20 +150,71 @@ int git_submodule_lookup( const char *name) /* trailing slash is allowed */ { int error; + unsigned int location; + git_submodule *sm; assert(repo && name); - if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0) + if ((error = submodule_alloc(&sm, repo, name)) < 0) + return error; + + if ((error = git_submodule_reload(sm, false)) < 0) { + git_submodule_free(sm); + return error; + } + + if ((error = git_submodule_location(&location, sm)) < 0) { + git_submodule_free(sm); return error; + } + + /* If it's not configured, we need to check for the path */ + if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) { + git_config_backend *mods; + const char *pattern = "submodule\\..*\\.path"; + fbp_data data = { name, NULL }; + + mods = open_gitmodules(repo, GITMODULES_EXISTING); + + if (mods) + error = git_config_file_foreach_match(mods, pattern, find_by_path, &data); - if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) { + git_config_file_free(mods); + + if (error < 0) { + git_submodule_free(sm); + return error; + } + + if (data.name) { + git__free(sm->name); + sm->name = data.name; + sm->path = git__strdup(name); + GITERR_CHECK_ALLOC(sm->path); - /* check if a plausible submodule exists at path */ + /* Try to load again with the right name */ + if ((error = git_submodule_reload(sm, false)) < 0) { + git_submodule_free(sm); + return error; + } + } + } + + if ((error = git_submodule_location(&location, sm)) < 0) { + git_submodule_free(sm); + return error; + } + + /* If we still haven't found it, do the WD check */ + if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) { + git_submodule_free(sm); + error = GIT_ENOTFOUND; + + /* If it's not configured, we still check if there's a repo at the path */ if (git_repository_workdir(repo)) { git_buf path = GIT_BUF_INIT; - if (git_buf_join3(&path, - '/', git_repository_workdir(repo), name, DOT_GIT) < 0) + '/', git_repository_workdir(repo), name, DOT_GIT) < 0) return -1; if (git_path_exists(path.ptr)) @@ -231,9 +224,15 @@ int git_submodule_lookup( } submodule_set_lookup_error(error, name); + return error; } - return error; + if (out) + *out = sm; + else + git_submodule_free(sm); + + return 0; } static void submodule_free_dup(void *sm) @@ -241,41 +240,221 @@ static void submodule_free_dup(void *sm) git_submodule_free(sm); } +static int submodule_get_or_create(git_submodule **out, git_repository *repo, git_strmap *map, const char *name) +{ + int error = 0; + khiter_t pos; + git_submodule *sm = NULL; + + pos = git_strmap_lookup_index(map, name); + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); + goto done; + } + + /* if the submodule doesn't exist yet in the map, create it */ + if ((error = submodule_alloc(&sm, repo, name)) < 0) + return error; + + pos = kh_put(str, map, sm->name, &error); + /* nobody can beat us to adding it */ + assert(error != 0); + if (error < 0) { + git_submodule_free(sm); + return error; + } + + git_strmap_set_value_at(map, pos, sm); + +done: + GIT_REFCOUNT_INC(sm); + *out = sm; + return 0; +} + +static int submodules_from_index(git_strmap *map, git_index *idx) +{ + int error; + git_iterator *i; + const git_index_entry *entry; + + if ((error = git_iterator_for_index(&i, idx, 0, NULL, NULL)) < 0) + return error; + + while (!(error = git_iterator_advance(&entry, i))) { + khiter_t pos = git_strmap_lookup_index(map, entry->path); + git_submodule *sm; + + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); + + if (S_ISGITLINK(entry->mode)) + submodule_update_from_index_entry(sm, entry); + else + sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; + } else if (S_ISGITLINK(entry->mode)) { + if (!submodule_get_or_create(&sm, git_index_owner(idx), map, entry->path)) { + submodule_update_from_index_entry(sm, entry); + git_submodule_free(sm); + } + } + } + + if (error == GIT_ITEROVER) + error = 0; + + git_iterator_free(i); + + return error; +} + +static int submodules_from_head(git_strmap *map, git_tree *head) +{ + int error; + git_iterator *i; + const git_index_entry *entry; + + if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) + return error; + + while (!(error = git_iterator_advance(&entry, i))) { + khiter_t pos = git_strmap_lookup_index(map, entry->path); + git_submodule *sm; + + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); + + if (S_ISGITLINK(entry->mode)) + submodule_update_from_head_data(sm, entry->mode, &entry->id); + else + sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; + } else if (S_ISGITLINK(entry->mode)) { + if (!submodule_get_or_create(&sm, git_tree_owner(head), map, entry->path)) { + submodule_update_from_head_data( + sm, entry->mode, &entry->id); + git_submodule_free(sm); + } + } + } + + if (error == GIT_ITEROVER) + error = 0; + + git_iterator_free(i); + + return error; +} + +/* If have_sm is true, sm is populated, otherwise map an repo are. */ +typedef struct { + int have_sm; + git_submodule *sm; + git_strmap *map; + git_repository *repo; +} lfc_data; + +static int all_submodules(git_repository *repo, git_strmap *map) +{ + int error = 0; + git_index *idx = NULL; + git_tree *head = NULL; + const char *wd = NULL; + git_buf path = GIT_BUF_INIT; + git_submodule *sm; + git_config_backend *mods = NULL; + uint32_t mask; + + assert(repo && map); + + /* get sources that we will need to check */ + if (git_repository_index(&idx, repo) < 0) + giterr_clear(); + if (git_repository_head_tree(&head, repo) < 0) + giterr_clear(); + + wd = git_repository_workdir(repo); + if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0) + goto cleanup; + + /* clear submodule flags that are to be refreshed */ + mask = 0; + mask |= GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS__INDEX_FLAGS | + GIT_SUBMODULE_STATUS__INDEX_OID_VALID | + GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES; + + mask |= GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS__HEAD_OID_VALID; + mask |= GIT_SUBMODULE_STATUS_IN_CONFIG; + if (mask != 0) + mask |= GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS__WD_SCANNED | + GIT_SUBMODULE_STATUS__WD_FLAGS | + GIT_SUBMODULE_STATUS__WD_OID_VALID; + + /* add back submodule information from index */ + if (idx) { + if ((error = submodules_from_index(map, idx)) < 0) + goto cleanup; + } + /* add submodule information from HEAD */ + if (head) { + if ((error = submodules_from_head(map, head)) < 0) + goto cleanup; + } + /* add submodule information from .gitmodules */ + if (wd) { + lfc_data data = { 0 }; + data.map = map; + data.repo = repo; + if ((mods = open_gitmodules(repo, false)) != NULL && + (error = git_config_file_foreach( + mods, submodule_load_from_config, &data)) < 0) + goto cleanup; + } + /* shallow scan submodules in work tree as needed */ + if (wd && mask != 0) { + git_strmap_foreach_value(map, sm, { + submodule_load_from_wd_lite(sm); + }); + } + +cleanup: + git_config_file_free(mods); + /* TODO: if we got an error, mark submodule config as invalid? */ + git_index_free(idx); + git_tree_free(head); + git_buf_free(&path); + return error; +} + int git_submodule_foreach( git_repository *repo, int (*callback)(git_submodule *sm, const char *name, void *payload), void *payload) { + git_vector snapshot = GIT_VECTOR_INIT; + git_strmap *submodules; + git_submodule *sm; int error; size_t i; - git_submodule *sm; - git_submodule_cache *cache; - git_vector snapshot = GIT_VECTOR_INIT; - - assert(repo && callback); - if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0) + if ((error = git_strmap_alloc(&submodules)) < 0) return error; - cache = repo->_submodules; - - if (git_mutex_lock(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache"); - return -1; - } + if ((error = all_submodules(repo, submodules)) < 0) + goto done; if (!(error = git_vector_init( - &snapshot, kh_size(cache->submodules), submodule_cmp))) { + &snapshot, kh_size(submodules), submodule_cmp))) { - git_strmap_foreach_value(cache->submodules, sm, { + git_strmap_foreach_value(submodules, sm, { if ((error = git_vector_insert(&snapshot, sm)) < 0) break; GIT_REFCOUNT_INC(sm); }); } - git_mutex_unlock(&cache->lock); - if (error < 0) goto done; @@ -293,17 +472,12 @@ done: git_submodule_free(sm); git_vector_free(&snapshot); - return error; -} - -void git_submodule_cache_free(git_repository *repo) -{ - git_submodule_cache *cache; - - assert(repo); + git_strmap_foreach_value(submodules, sm, { + git_submodule_free(sm); + }); + git_strmap_free(submodules); - if ((cache = git__swap(repo->_submodules, NULL)) != NULL) - submodule_cache_free(cache); + return error; } static int submodule_repo_init( @@ -394,7 +568,7 @@ int git_submodule_add_setup( /* update .gitmodules */ - if (!(mods = open_gitmodules(repo->_submodules, GITMODULES_CREATE))) { + if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) { giterr_set(GITERR_SUBMODULE, "Adding submodules to a bare repository is not supported"); return -1; @@ -430,19 +604,10 @@ int git_submodule_add_setup( goto cleanup; } - /* add submodule to hash and "reload" it */ - - if (git_mutex_lock(&repo->_submodules->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache"); - error = -1; + if ((error = git_submodule_lookup(&sm, repo, path)) < 0) goto cleanup; - } - if (!(error = submodule_get(&sm, repo->_submodules, path, NULL)) && - !(error = git_submodule_reload(sm, false))) - error = git_submodule_init(sm, false); - - git_mutex_unlock(&repo->_submodules->lock); + error = git_submodule_init(sm, false); cleanup: if (error && sm) { @@ -572,15 +737,6 @@ cleanup: return error; } -const char *git_submodule_ignore_to_str(git_submodule_ignore_t ignore) -{ - int i; - for (i = 0; i < (int)ARRAY_SIZE(_sm_ignore_map); ++i) - if (_sm_ignore_map[i].map_value == ignore) - return _sm_ignore_map[i].str_match; - return NULL; -} - const char *git_submodule_update_to_str(git_submodule_update_t update) { int i; @@ -590,89 +746,6 @@ const char *git_submodule_update_to_str(git_submodule_update_t update) return NULL; } -const char *git_submodule_recurse_to_str(git_submodule_recurse_t recurse) -{ - int i; - for (i = 0; i < (int)ARRAY_SIZE(_sm_recurse_map); ++i) - if (_sm_recurse_map[i].map_value == recurse) - return _sm_recurse_map[i].str_match; - return NULL; -} - -int git_submodule_save(git_submodule *submodule) -{ - int error = 0; - git_config_backend *mods; - git_buf key = GIT_BUF_INIT; - const char *val; - - assert(submodule); - - mods = open_gitmodules(submodule->repo->_submodules, GITMODULES_CREATE); - if (!mods) { - giterr_set(GITERR_SUBMODULE, - "Adding submodules to a bare repository is not supported"); - return -1; - } - - if ((error = git_buf_printf(&key, "submodule.%s.", submodule->name)) < 0) - goto cleanup; - - /* save values for path, url, update, ignore, fetchRecurseSubmodules */ - - if ((error = submodule_config_key_trunc_puts(&key, "path")) < 0 || - (error = git_config_file_set_string(mods, key.ptr, submodule->path)) < 0) - goto cleanup; - - if ((error = submodule_config_key_trunc_puts(&key, "url")) < 0 || - (error = git_config_file_set_string(mods, key.ptr, submodule->url)) < 0) - goto cleanup; - - if ((error = submodule_config_key_trunc_puts(&key, "branch")) < 0) - goto cleanup; - if (submodule->branch == NULL) - error = git_config_file_delete(mods, key.ptr); - else - error = git_config_file_set_string(mods, key.ptr, submodule->branch); - if (error == GIT_ENOTFOUND) { - error = 0; - giterr_clear(); - } - if (error < 0) - goto cleanup; - - if (!(error = submodule_config_key_trunc_puts(&key, "update")) && - (val = git_submodule_update_to_str(submodule->update)) != NULL) - error = git_config_file_set_string(mods, key.ptr, val); - if (error < 0) - goto cleanup; - - if (!(error = submodule_config_key_trunc_puts(&key, "ignore")) && - (val = git_submodule_ignore_to_str(submodule->ignore)) != NULL) - error = git_config_file_set_string(mods, key.ptr, val); - if (error < 0) - goto cleanup; - - if (!(error = submodule_config_key_trunc_puts(&key, "fetchRecurseSubmodules")) && - (val = git_submodule_recurse_to_str(submodule->fetch_recurse)) != NULL) - error = git_config_file_set_string(mods, key.ptr, val); - if (error < 0) - goto cleanup; - - /* update internal defaults */ - - submodule->ignore_default = submodule->ignore; - submodule->update_default = submodule->update; - submodule->fetch_recurse_default = submodule->fetch_recurse; - submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; - -cleanup: - git_config_file_free(mods); - git_buf_free(&key); - - return error; -} - git_repository *git_submodule_owner(git_submodule *submodule) { assert(submodule); @@ -718,37 +791,66 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur return error; } -const char *git_submodule_branch(git_submodule *submodule) +static int write_var(git_repository *repo, const char *name, const char *var, const char *val) { - assert(submodule); - return submodule->branch; + git_buf key = GIT_BUF_INIT; + git_config_backend *mods; + int error; + + mods = open_gitmodules(repo, GITMODULES_CREATE); + if (!mods) + return -1; + + if ((error = git_buf_printf(&key, "submodule.%s.%s", name, var)) < 0) + goto cleanup; + + if (val) + error = git_config_file_set_string(mods, key.ptr, val); + else + error = git_config_file_delete(mods, key.ptr); + + git_buf_free(&key); + +cleanup: + git_config_file_free(mods); + return error; } -int git_submodule_set_branch(git_submodule *submodule, const char *branch) +static int write_mapped_var(git_repository *repo, const char *name, git_cvar_map *maps, size_t nmaps, const char *var, int ival) { - assert(submodule); - - git__free(submodule->branch); - submodule->branch = NULL; + git_cvar_t type; + const char *val; - if (branch != NULL) { - submodule->branch = git__strdup(branch); - GITERR_CHECK_ALLOC(submodule->branch); + if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) { + giterr_set(GITERR_SUBMODULE, "invalid value for %s", var); + return -1; } - return 0; + if (type == GIT_CVAR_TRUE) + val = "true"; + + return write_var(repo, name, var, val); } -int git_submodule_set_url(git_submodule *submodule, const char *url) +const char *git_submodule_branch(git_submodule *submodule) { - assert(submodule && url); + assert(submodule); + return submodule->branch; +} - git__free(submodule->url); +int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch) +{ - submodule->url = git__strdup(url); - GITERR_CHECK_ALLOC(submodule->url); + assert(repo && name); - return 0; + return write_var(repo, name, "branch", branch); +} + +int git_submodule_set_url(git_repository *repo, const char *name, const char *url) +{ + assert(repo && name && url); + + return write_var(repo, name, "url", url); } const git_oid *git_submodule_index_id(git_submodule *submodule) @@ -799,19 +901,11 @@ git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule) GIT_SUBMODULE_IGNORE_NONE : submodule->ignore; } -git_submodule_ignore_t git_submodule_set_ignore( - git_submodule *submodule, git_submodule_ignore_t ignore) +int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore) { - git_submodule_ignore_t old; - - assert(submodule); - - if (ignore == GIT_SUBMODULE_IGNORE_RESET) - ignore = submodule->ignore_default; + assert(repo && name); - old = submodule->ignore; - submodule->ignore = ignore; - return old; + return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore); } git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) @@ -821,19 +915,11 @@ git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update; } -git_submodule_update_t git_submodule_set_update( - git_submodule *submodule, git_submodule_update_t update) +int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update) { - git_submodule_update_t old; - - assert(submodule); - - if (update == GIT_SUBMODULE_UPDATE_RESET) - update = submodule->update_default; + assert(repo && name); - old = submodule->update; - submodule->update = update; - return old; + return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update); } git_submodule_recurse_t git_submodule_fetch_recurse_submodules( @@ -843,20 +929,11 @@ git_submodule_recurse_t git_submodule_fetch_recurse_submodules( return submodule->fetch_recurse; } -git_submodule_recurse_t git_submodule_set_fetch_recurse_submodules( - git_submodule *submodule, - git_submodule_recurse_t fetch_recurse_submodules) +int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse) { - git_submodule_recurse_t old; - - assert(submodule); - - if (fetch_recurse_submodules == GIT_SUBMODULE_RECURSE_RESET) - fetch_recurse_submodules = submodule->fetch_recurse_default; + assert(repo && name); - old = submodule->fetch_recurse; - submodule->fetch_recurse = fetch_recurse_submodules; - return old; + return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse); } static int submodule_repo_create( @@ -953,7 +1030,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio memcpy(&clone_options.fetch_opts, &update_options.fetch_opts, sizeof(git_fetch_options)); /* Get the status of the submodule to determine if it is already initialized */ - if ((error = git_submodule_status(&submodule_status, sm)) < 0) + if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_FALLBACK)) < 0) goto done; /* @@ -1197,11 +1274,6 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm) return git_submodule__open(subrepo, sm, false); } -int git_submodule_reload_all(git_repository *repo, int force) -{ - return submodule_cache_init(repo, force ? CACHE_FLUSH : CACHE_REFRESH); -} - static void submodule_update_from_index_entry( git_submodule *sm, const git_index_entry *ie) { @@ -1280,14 +1352,12 @@ int git_submodule_reload(git_submodule *sm, int force) { int error = 0; git_config_backend *mods; - git_submodule_cache *cache; + lfc_data data = { 0 }; GIT_UNUSED(force); assert(sm); - cache = sm->repo->_submodules; - /* refresh index data */ if ((error = submodule_update_index(sm)) < 0) return error; @@ -1301,7 +1371,7 @@ int git_submodule_reload(git_submodule *sm, int force) return error; /* refresh config data */ - mods = open_gitmodules(cache, GITMODULES_EXISTING); + mods = open_gitmodules(sm->repo, GITMODULES_EXISTING); if (mods != NULL) { git_buf path = GIT_BUF_INIT; @@ -1309,11 +1379,14 @@ int git_submodule_reload(git_submodule *sm, int force) git_buf_text_puts_escape_regex(&path, sm->name); git_buf_puts(&path, ".*"); - if (git_buf_oom(&path)) + if (git_buf_oom(&path)) { error = -1; - else + } else { + data.have_sm = 1; + data.sm = sm; error = git_config_file_foreach_match( - mods, path.ptr, submodule_load_from_config, cache); + mods, path.ptr, submodule_load_from_config, &data); + } git_buf_free(&path); git_config_file_free(mods); @@ -1352,7 +1425,7 @@ int git_submodule__status( unsigned int status; git_repository *smrepo = NULL; - if (ign < GIT_SUBMODULE_IGNORE_NONE) + if (ign == GIT_SUBMODULE_IGNORE_FALLBACK) ign = sm->ignore; /* only return location info if ignore == all */ @@ -1401,11 +1474,20 @@ int git_submodule__status( return 0; } -int git_submodule_status(unsigned int *status, git_submodule *sm) +int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore) { - assert(status && sm); + git_submodule *sm; + int error; + + assert(status && repo && name); - return git_submodule__status(status, NULL, NULL, NULL, sm, 0); + if ((error = git_submodule_lookup(&sm, repo, name)) < 0) + return error; + + error = git_submodule__status(status, NULL, NULL, NULL, sm, ignore); + git_submodule_free(sm); + + return error; } int git_submodule_location(unsigned int *location, git_submodule *sm) @@ -1422,7 +1504,7 @@ int git_submodule_location(unsigned int *location, git_submodule *sm) */ static int submodule_alloc( - git_submodule **out, git_submodule_cache *cache, const char *name) + git_submodule **out, git_repository *repo, const char *name) { size_t namelen; git_submodule *sm; @@ -1445,56 +1527,20 @@ static int submodule_alloc( sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE; sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT; sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO; - sm->repo = cache->repo; + sm->repo = repo; sm->branch = NULL; *out = sm; return 0; } -static void submodule_cache_remove_item( - git_submodule_cache *cache, - git_submodule *item, - bool free_after_remove) -{ - git_strmap *map; - const char *name, *alt; - - if (!cache || !(map = cache->submodules) || !item) - return; - - name = item->name; - alt = (item->path != item->name) ? item->path : NULL; - - for (; name; name = alt, alt = NULL) { - khiter_t pos = git_strmap_lookup_index(map, name); - git_submodule *found; - - if (!git_strmap_valid_index(map, pos)) - continue; - - found = git_strmap_value_at(map, pos); - - if (found != item) - continue; - - git_strmap_set_value_at(map, pos, NULL); - git_strmap_delete_at(map, pos); - - if (free_after_remove) - git_submodule_free(found); - } -} - static void submodule_release(git_submodule *sm) { if (!sm) return; if (sm->repo) { - git_submodule_cache *cache = sm->repo->_submodules; sm->repo = NULL; - submodule_cache_remove_item(cache, sm, false); } if (sm->path != sm->name) @@ -1513,54 +1559,6 @@ void git_submodule_free(git_submodule *sm) GIT_REFCOUNT_DEC(sm, submodule_release); } -static int submodule_get( - git_submodule **out, - git_submodule_cache *cache, - const char *name, - const char *alternate) -{ - int error = 0; - khiter_t pos; - git_submodule *sm; - - pos = git_strmap_lookup_index(cache->submodules, name); - - if (!git_strmap_valid_index(cache->submodules, pos) && alternate) - pos = git_strmap_lookup_index(cache->submodules, alternate); - - if (!git_strmap_valid_index(cache->submodules, pos)) { - if ((error = submodule_alloc(&sm, cache, name)) < 0) - return error; - - /* insert value at name - if another thread beats us to it, then use - * their record and release our own. - */ - pos = kh_put(str, cache->submodules, sm->name, &error); - - if (error < 0) - goto done; - else if (error == 0) { - git_submodule_free(sm); - sm = git_strmap_value_at(cache->submodules, pos); - } else { - error = 0; - git_strmap_set_value_at(cache->submodules, pos, sm); - } - } else { - sm = git_strmap_value_at(cache->submodules, pos); - } - -done: - if (error < 0) - git_submodule_free(sm); - else if (out) { - GIT_REFCOUNT_INC(sm); - *out = sm; - } - - return error; -} - static int submodule_config_error(const char *property, const char *value) { giterr_set(GITERR_INVALID, @@ -1613,12 +1611,12 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value) static int submodule_load_from_config( const git_config_entry *entry, void *payload) { - git_submodule_cache *cache = payload; const char *namestart, *property; const char *key = entry->name, *value = entry->value, *path; char *alternate = NULL, *replaced = NULL; git_buf name = GIT_BUF_INIT; - git_submodule *sm = NULL; + lfc_data *data = payload; + git_submodule *sm; int error = 0; if (git__prefixcmp(key, "submodule.") != 0) @@ -1633,10 +1631,29 @@ static int submodule_load_from_config( property++; path = !strcasecmp(property, "path") ? value : NULL; - if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 || - (error = submodule_get(&sm, cache, name.ptr, path)) < 0) + if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0) goto done; + if (data->have_sm) { + sm = data->sm; + } else { + khiter_t pos; + git_strmap *map = data->map; + pos = git_strmap_lookup_index(map, name.ptr); + if (git_strmap_valid_index(map, pos)) { + sm = git_strmap_value_at(map, pos); + } else { + if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0) + goto done; + + git_strmap_insert(map, sm->name, sm, error); + assert(error != 0); + if (error < 0) + goto done; + error = 0; + } + } + sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; /* Only from config might we get differing names & paths. If so, then @@ -1648,7 +1665,7 @@ static int submodule_load_from_config( */ if (strcmp(sm->name, name.ptr) != 0) { /* name changed */ - if (!strcmp(sm->path, name.ptr)) { /* already set as path */ + if (sm->path && !strcmp(sm->path, name.ptr)) { /* already set as path */ replaced = sm->name; sm->name = sm->path; } else { @@ -1674,7 +1691,6 @@ static int submodule_load_from_config( /* Deregister under name being replaced */ if (replaced) { - git_strmap_delete(cache->submodules, replaced); git_submodule_free(sm); git__free(replaced); } @@ -1682,7 +1698,6 @@ static int submodule_load_from_config( /* Insert under alternate key */ if (alternate) { void *old_sm = NULL; - git_strmap_insert2(cache->submodules, alternate, sm, old_sm, error); if (error < 0) goto done; @@ -1742,7 +1757,6 @@ static int submodule_load_from_config( /* ignore other unknown submodule properties */ done: - git_submodule_free(sm); /* offset refcount inc from submodule_get() */ git_buf_free(&name); return error; } @@ -1764,86 +1778,11 @@ static int submodule_load_from_wd_lite(git_submodule *sm) return 0; } -static int submodule_cache_refresh_from_index( - git_submodule_cache *cache, git_index *idx) -{ - int error; - git_iterator *i; - const git_index_entry *entry; - - if ((error = git_iterator_for_index(&i, idx, 0, NULL, NULL)) < 0) - return error; - - while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path); - git_submodule *sm; - - if (git_strmap_valid_index(cache->submodules, pos)) { - sm = git_strmap_value_at(cache->submodules, pos); - - if (S_ISGITLINK(entry->mode)) - submodule_update_from_index_entry(sm, entry); - else - sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; - } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get(&sm, cache, entry->path, NULL)) { - submodule_update_from_index_entry(sm, entry); - git_submodule_free(sm); - } - } - } - - if (error == GIT_ITEROVER) - error = 0; - - git_iterator_free(i); - - return error; -} - -static int submodule_cache_refresh_from_head( - git_submodule_cache *cache, git_tree *head) -{ - int error; - git_iterator *i; - const git_index_entry *entry; - - if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) - return error; - - while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path); - git_submodule *sm; - - if (git_strmap_valid_index(cache->submodules, pos)) { - sm = git_strmap_value_at(cache->submodules, pos); - - if (S_ISGITLINK(entry->mode)) - submodule_update_from_head_data(sm, entry->mode, &entry->id); - else - sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; - } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get(&sm, cache, entry->path, NULL)) { - submodule_update_from_head_data( - sm, entry->mode, &entry->id); - git_submodule_free(sm); - } - } - } - - if (error == GIT_ITEROVER) - error = 0; - - git_iterator_free(i); - - return error; -} - static git_config_backend *open_gitmodules( - git_submodule_cache *cache, + git_repository *repo, int okay_to_create) { - const char *workdir = git_repository_workdir(cache->repo); + const char *workdir = git_repository_workdir(repo); git_buf path = GIT_BUF_INIT; git_config_backend *mods = NULL; @@ -1868,198 +1807,6 @@ static git_config_backend *open_gitmodules( return mods; } -static void submodule_cache_free(git_submodule_cache *cache) -{ - git_submodule *sm; - - if (!cache) - return; - - git_strmap_foreach_value(cache->submodules, sm, { - sm->repo = NULL; /* disconnect from repo */ - git_submodule_free(sm); - }); - git_strmap_free(cache->submodules); - - git_buf_free(&cache->gitmodules_path); - git_mutex_free(&cache->lock); - git__free(cache); -} - -static int submodule_cache_alloc( - git_submodule_cache **out, git_repository *repo) -{ - git_submodule_cache *cache = git__calloc(1, sizeof(git_submodule_cache)); - GITERR_CHECK_ALLOC(cache); - - if (git_mutex_init(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to initialize submodule cache lock"); - git__free(cache); - return -1; - } - - if (git_strmap_alloc(&cache->submodules) < 0) { - submodule_cache_free(cache); - return -1; - } - - cache->repo = repo; - git_buf_init(&cache->gitmodules_path, 0); - - *out = cache; - return 0; -} - -static int submodule_cache_refresh(git_submodule_cache *cache, int refresh) -{ - int error = 0, update_index, update_head, update_gitmod; - git_index *idx = NULL; - git_tree *head = NULL; - const char *wd = NULL; - git_buf path = GIT_BUF_INIT; - git_submodule *sm; - git_config_backend *mods = NULL; - uint32_t mask; - - if (!cache || !cache->repo || !refresh) - return 0; - - if (git_mutex_lock(&cache->lock) < 0) { - giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache"); - return -1; - } - - /* get sources that we will need to check */ - - if (git_repository_index(&idx, cache->repo) < 0) - giterr_clear(); - if (git_repository_head_tree(&head, cache->repo) < 0) - giterr_clear(); - - wd = git_repository_workdir(cache->repo); - if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0) - goto cleanup; - - /* check for invalidation */ - - if (refresh == CACHE_FLUSH) - update_index = update_head = update_gitmod = true; - else { - update_index = - !idx || git_index__changed_relative_to(idx, &cache->index_checksum); - update_head = - !head || !git_oid_equal(&cache->head_id, git_tree_id(head)); - - update_gitmod = (wd != NULL) ? - git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr) : - (cache->gitmodules_stamp.mtime != 0); - } - - /* clear submodule flags that are to be refreshed */ - - mask = 0; - if (!idx || update_index) - mask |= GIT_SUBMODULE_STATUS_IN_INDEX | - GIT_SUBMODULE_STATUS__INDEX_FLAGS | - GIT_SUBMODULE_STATUS__INDEX_OID_VALID | - GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES; - if (!head || update_head) - mask |= GIT_SUBMODULE_STATUS_IN_HEAD | - GIT_SUBMODULE_STATUS__HEAD_OID_VALID; - if (update_gitmod) - mask |= GIT_SUBMODULE_STATUS_IN_CONFIG; - if (mask != 0) - mask |= GIT_SUBMODULE_STATUS_IN_WD | - GIT_SUBMODULE_STATUS__WD_SCANNED | - GIT_SUBMODULE_STATUS__WD_FLAGS | - GIT_SUBMODULE_STATUS__WD_OID_VALID; - else - goto cleanup; /* nothing to do */ - - submodule_cache_clear_flags(cache, mask); - - /* add back submodule information from index */ - - if (idx && update_index) { - if ((error = submodule_cache_refresh_from_index(cache, idx)) < 0) - goto cleanup; - - git_oid_cpy(&cache->index_checksum, git_index_checksum(idx)); - } - - /* add submodule information from HEAD */ - - if (head && update_head) { - if ((error = submodule_cache_refresh_from_head(cache, head)) < 0) - goto cleanup; - - git_oid_cpy(&cache->head_id, git_tree_id(head)); - } - - /* add submodule information from .gitmodules */ - - if (wd && update_gitmod > 0) { - if ((mods = open_gitmodules(cache, false)) != NULL && - (error = git_config_file_foreach( - mods, submodule_load_from_config, cache)) < 0) - goto cleanup; - } - - /* shallow scan submodules in work tree as needed */ - - if (wd && mask != 0) { - git_strmap_foreach_value(cache->submodules, sm, { - submodule_load_from_wd_lite(sm); - }); - } - - /* remove submodules that no longer exist */ - - git_strmap_foreach_value(cache->submodules, sm, { - /* purge unless in HEAD, index, or .gitmodules; no sm for wd only */ - if (sm != NULL && - !(sm->flags & - (GIT_SUBMODULE_STATUS_IN_HEAD | - GIT_SUBMODULE_STATUS_IN_INDEX | - GIT_SUBMODULE_STATUS_IN_CONFIG))) - submodule_cache_remove_item(cache, sm, true); - }); - -cleanup: - git_config_file_free(mods); - - /* TODO: if we got an error, mark submodule config as invalid? */ - - git_mutex_unlock(&cache->lock); - - git_index_free(idx); - git_tree_free(head); - git_buf_free(&path); - - return error; -} - -static int submodule_cache_init(git_repository *repo, int cache_refresh) -{ - int error = 0; - git_submodule_cache *cache = NULL; - - /* if submodules already exist, just refresh as requested */ - if (repo->_submodules) - return submodule_cache_refresh(repo->_submodules, cache_refresh); - - /* otherwise create a new cache, load it, and atomically swap it in */ - if (!(error = submodule_cache_alloc(&cache, repo)) && - !(error = submodule_cache_refresh(cache, CACHE_FLUSH))) - cache = git__compare_and_swap(&repo->_submodules, NULL, cache); - - /* might have raced with another thread to set cache, so free if needed */ - if (cache) - submodule_cache_free(cache); - - return error; -} - /* Lookup name of remote of the local tracking branch HEAD points to */ static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo) { diff --git a/src/submodule.h b/src/submodule.h index 7a9bf9c92..2ef2031b3 100644 --- a/src/submodule.h +++ b/src/submodule.h @@ -99,23 +99,6 @@ struct git_submodule { git_oid wd_oid; }; -/** - * The git_submodule_cache stores known submodules along with timestamps, - * etc. about when they were loaded - */ -typedef struct { - git_repository *repo; - git_strmap *submodules; - git_mutex lock; - - /* cache invalidation data */ - git_oid head_id; - git_oid index_checksum; - git_buf gitmodules_path; - git_futils_filestamp gitmodules_stamp; - git_futils_filestamp config_stamp; -} git_submodule_cache; - /* Force revalidation of submodule data cache (alloc as needed) */ extern int git_submodule_cache_refresh(git_repository *repo); @@ -137,9 +120,6 @@ enum { #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \ ((S) & ~(0xFFFFFFFFu << 20)) -/* Internal submodule check does not attempt to refresh cached data */ -extern bool git_submodule__is_submodule(git_repository *repo, const char *name); - /* Internal lookup does not attempt to refresh cached data */ extern int git_submodule__lookup( git_submodule **out, git_repository *repo, const char *path); @@ -163,8 +143,4 @@ extern int git_submodule_parse_ignore( extern int git_submodule_parse_update( git_submodule_update_t *out, const char *value); -extern const char *git_submodule_ignore_to_str(git_submodule_ignore_t); -extern const char *git_submodule_update_to_str(git_submodule_update_t); -extern const char *git_submodule_recurse_to_str(git_submodule_recurse_t); - #endif 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/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/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 bbbb2d56e..f7a089e72 100644 --- a/tests/submodule/modify.c +++ b/tests/submodule/modify.c @@ -41,17 +41,15 @@ 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 */ 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")); @@ -130,132 +128,85 @@ void test_submodule_modify__sync(void) git_submodule_free(sm3); } -void test_submodule_modify__edit_and_save(void) +void assert_ignore_change(git_submodule_ignore_t ignore) { - 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; - - 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)); - 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( - 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_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)); - 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((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)); - - /* 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); - - /* call save */ - 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)); - - /* 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( - (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)); - - /* 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")); - 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( - 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 *sm; - git_submodule_free(sm1); - git_submodule_free(sm2); - git_repository_free(r2); - git__free(old_url); + 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(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_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(recurse, git_submodule_fetch_recurse_submodules(sm)); + git_submodule_free(sm); +} + +void test_submodule_modify__set_fetch_recurse_submodules(void) +{ + 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) +{ + 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__save_last(void) +void test_submodule_modify__set_url(void) { git_submodule *sm; - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); - cl_git_pass(git_submodule_save(sm)); + 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); } 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,41 +49,7 @@ 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); } @@ -100,10 +58,8 @@ 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")); diff --git a/tests/submodule/status.c b/tests/submodule/status.c index 6efae35c6..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(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 19bb04f75..2647e1514 100644 --- a/tests/submodule/submodule_helpers.c +++ b/tests/submodule/submodule_helpers.c @@ -156,21 +156,18 @@ 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) { - 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, GIT_SUBMODULE_IGNORE_FALLBACK)); return status; } diff --git a/tests/submodule/update.c b/tests/submodule/update.c index e7f1b76b8..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, sm)); + 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, sm)); + 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, sm)); + 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, sm)); + 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,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_IGNORE_FALLBACK)); + + 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", 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 | @@ -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_IGNORE_FALLBACK)); + + 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", 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 | @@ -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_IGNORE_FALLBACK)); + + 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 | |