Commit daacf96d by Carlos Martín Nieto

Merge pull request #3097 from libgit2/cmn/submodule-config-state

Remove run-time configuration settings from submodules
parents e1f434f8 783672fa
...@@ -384,13 +384,10 @@ static void print_short(git_repository *repo, git_status_list *status) ...@@ -384,13 +384,10 @@ static void print_short(git_repository *repo, git_status_list *status)
if (s->index_to_workdir && if (s->index_to_workdir &&
s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT) s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
{ {
git_submodule *sm = NULL;
unsigned int smstatus = 0; unsigned int smstatus = 0;
if (!git_submodule_lookup( if (!git_submodule_status(&smstatus, repo, s->index_to_workdir->new_file.path,
&sm, repo, s->index_to_workdir->new_file.path)) { GIT_SUBMODULE_IGNORE_FALLBACK)) {
if (!git_submodule_status(&smstatus, sm)) {
if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED) if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
extra = " (new commits)"; extra = " (new commits)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
...@@ -402,9 +399,6 @@ static void print_short(git_repository *repo, git_status_list *status) ...@@ -402,9 +399,6 @@ static void print_short(git_repository *repo, git_status_list *status)
} }
} }
git_submodule_free(sm);
}
/** /**
* Now that we have all the information, format the output. * Now that we have all the information, format the output.
*/ */
......
...@@ -399,7 +399,7 @@ typedef struct { ...@@ -399,7 +399,7 @@ typedef struct {
* `git_diff_options_init` programmatic initialization. * `git_diff_options_init` programmatic initialization.
*/ */
#define GIT_DIFF_OPTIONS_INIT \ #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 * Initializes a `git_diff_options` with default values. Equivalent to
......
...@@ -301,20 +301,6 @@ GIT_EXTERN(int) git_submodule_add_to_index( ...@@ -301,20 +301,6 @@ GIT_EXTERN(int) git_submodule_add_to_index(
int write_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. * Get the containing repository for a submodule.
* *
* This returns a pointer to the repository that contains the 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 ...@@ -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); 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 * After calling this, you may wish to call `git_submodule_sync()` to
* 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
* write the changes to the checked out submodule repository. * 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 * @param branch Branch that should be used for the submodule
* @return 0 on success, <0 on failure * @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 * After calling this, you may wish to call `git_submodule_sync()` to
* the changes back to the ".gitmodules" file and `git_submodule_sync()` to
* write the changes to the checked out submodule repository. * 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 * @param url URL that should be used for the submodule
* @return 0 on success, <0 on failure * @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. * 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); ...@@ -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 * The working directory will be consider clean so long as there is a
* checked out version present. * 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 * @param submodule The submodule to check
* @return The current git_submodule_ignore_t valyue what will be used for * @return The current git_submodule_ignore_t valyue what will be used for
* this submodule. * this submodule.
...@@ -463,32 +441,25 @@ GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore( ...@@ -463,32 +441,25 @@ GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
git_submodule *submodule); 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()`.
* *
* To make changes persistent, call `git_submodule_save()` to write the * This does not affect any currently-loaded instances.
* value to disk (in the ".gitmodules" and ".git/config" files).
* *
* Call with `GIT_SUBMODULE_IGNORE_RESET` or call `git_submodule_reload()` * @param repo the repository to affect
* to revert the in-memory rule to the value that is on disk. * @param name the name of the submdule
*
* @param submodule The submodule to update
* @param ignore The new value for the ignore rule * @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_EXTERN(int) git_submodule_set_ignore(
git_submodule *submodule, git_repository *repo,
const char *name,
git_submodule_ignore_t ignore); git_submodule_ignore_t ignore);
/** /**
* Get the update rule that will be used for the submodule. * Get the update rule that will be used for the submodule.
* *
* This value controls the behavior of the `git submodule update` command. * This value controls the behavior of the `git submodule update` command.
* There are four useful values documented with `git_submodule_update_t` * 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.
* *
* @param submodule The submodule to check * @param submodule The submodule to check
* @return The current git_submodule_update_t value that will be used * @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( ...@@ -498,23 +469,18 @@ GIT_EXTERN(git_submodule_update_t) git_submodule_update_strategy(
git_submodule *submodule); 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.
* *
* Calling this again with GIT_SUBMODULE_UPDATE_RESET or calling * This setting won't affect any existing instances.
* `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 * @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_EXTERN(int) git_submodule_set_update(
git_submodule *submodule, git_repository *repo,
const char *name,
git_submodule_update_t update); git_submodule_update_t update);
/** /**
...@@ -532,18 +498,18 @@ GIT_EXTERN(git_submodule_recurse_t) git_submodule_fetch_recurse_submodules( ...@@ -532,18 +498,18 @@ GIT_EXTERN(git_submodule_recurse_t) git_submodule_fetch_recurse_submodules(
git_submodule *submodule); 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 * This setting won't affect any existing instances.
* the submodule. You should call `git_submodule_save()` if you want
* to persist the new value.
* *
* @param submodule The submodule to modify * @param repo the repository to affect
* @param name the submodule to configure
* @param fetch_recurse_submodules Boolean value * @param fetch_recurse_submodules Boolean value
* @return old value for fetchRecurseSubmodules * @return old value for fetchRecurseSubmodules
*/ */
GIT_EXTERN(git_submodule_recurse_t) git_submodule_set_fetch_recurse_submodules( GIT_EXTERN(int) git_submodule_set_fetch_recurse_submodules(
git_submodule *submodule, git_repository *repo,
const char *name,
git_submodule_recurse_t fetch_recurse_submodules); git_submodule_recurse_t fetch_recurse_submodules);
/** /**
...@@ -634,16 +600,19 @@ GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo, int force); ...@@ -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 * This looks at a submodule and tries to determine the status. It
* will return a combination of the `GIT_SUBMODULE_STATUS` values above. * will return a combination of the `GIT_SUBMODULE_STATUS` values above.
* How deeply it examines the working directory to do this will depend * 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 * on the `git_submodule_ignore_t` value for the submodule.
* set either temporarily or permanently with `git_submodule_set_ignore()`.
* *
* @param status Combination of `GIT_SUBMODULE_STATUS` flags * @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 * @return 0 on success, <0 on error
*/ */
GIT_EXTERN(int) git_submodule_status( GIT_EXTERN(int) git_submodule_status(
unsigned int *status, unsigned int *status,
git_submodule *submodule); git_repository *repo,
const char *name,
git_submodule_ignore_t ignore);
/** /**
* Get the locations of submodule information. * Get the locations of submodule information.
......
...@@ -349,7 +349,6 @@ typedef struct git_submodule git_submodule; ...@@ -349,7 +349,6 @@ typedef struct git_submodule git_submodule;
* *
* The values are: * The values are:
* *
* - GIT_SUBMODULE_UPDATE_RESET: reset to the on-disk value.
* - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is * - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is
* updated, checkout the new detached HEAD to the submodule directory. * updated, checkout the new detached HEAD to the submodule directory.
* - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked * - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked
...@@ -362,8 +361,6 @@ typedef struct git_submodule git_submodule; ...@@ -362,8 +361,6 @@ typedef struct git_submodule git_submodule;
* when we don't want any particular update rule to be specified. * when we don't want any particular update rule to be specified.
*/ */
typedef enum { typedef enum {
GIT_SUBMODULE_UPDATE_RESET = -1,
GIT_SUBMODULE_UPDATE_CHECKOUT = 1, GIT_SUBMODULE_UPDATE_CHECKOUT = 1,
GIT_SUBMODULE_UPDATE_REBASE = 2, GIT_SUBMODULE_UPDATE_REBASE = 2,
GIT_SUBMODULE_UPDATE_MERGE = 3, GIT_SUBMODULE_UPDATE_MERGE = 3,
...@@ -386,7 +383,7 @@ typedef enum { ...@@ -386,7 +383,7 @@ typedef enum {
* *
* The values are: * 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 * - GIT_SUBMODULE_IGNORE_NONE: don't ignore any change - i.e. even an
* untracked file, will mark the submodule as dirty. Ignored files are * untracked file, will mark the submodule as dirty. Ignored files are
* still ignored, of course. * still ignored, of course.
...@@ -400,14 +397,12 @@ typedef enum { ...@@ -400,14 +397,12 @@ typedef enum {
* when we don't want any particular ignore rule to be specified. * when we don't want any particular ignore rule to be specified.
*/ */
typedef enum { 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_NONE = 1, /**< any change or untracked == dirty */
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */ GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */
GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */ GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */
GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */ GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */
GIT_SUBMODULE_IGNORE_DEFAULT = 0
} git_submodule_ignore_t; } git_submodule_ignore_t;
/** /**
...@@ -415,15 +410,12 @@ typedef enum { ...@@ -415,15 +410,12 @@ typedef enum {
* *
* Represent the value of `submodule.$name.fetchRecurseSubmodules` * 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_NO - do no recurse into submodules
* * GIT_SUBMODULE_RECURSE_YES - recurse into submodules * * GIT_SUBMODULE_RECURSE_YES - recurse into submodules
* * GIT_SUBMODULE_RECURSE_ONDEMAND - recurse into submodules only when * * GIT_SUBMODULE_RECURSE_ONDEMAND - recurse into submodules only when
* commit not already in local clone * commit not already in local clone
*/ */
typedef enum { typedef enum {
GIT_SUBMODULE_RECURSE_RESET = -1,
GIT_SUBMODULE_RECURSE_NO = 0, GIT_SUBMODULE_RECURSE_NO = 0,
GIT_SUBMODULE_RECURSE_YES = 1, GIT_SUBMODULE_RECURSE_YES = 1,
GIT_SUBMODULE_RECURSE_ONDEMAND = 2, GIT_SUBMODULE_RECURSE_ONDEMAND = 2,
......
...@@ -180,7 +180,7 @@ static bool checkout_is_workdir_modified( ...@@ -180,7 +180,7 @@ static bool checkout_is_workdir_modified(
return true; 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)) GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
rval = true; rval = true;
else if ((sm_oid = git_submodule_wd_id(sm)) == NULL) else if ((sm_oid = git_submodule_wd_id(sm)) == NULL)
...@@ -1860,11 +1860,6 @@ static int checkout_create_submodules( ...@@ -1860,11 +1860,6 @@ static int checkout_create_submodules(
git_diff_delta *delta; git_diff_delta *delta;
size_t i; 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) { git_vector_foreach(&data->diff->deltas, i, delta) {
if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) { if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
/* this has a blocker directory that should only be removed iff /* this has a blocker directory that should only be removed iff
...@@ -1885,8 +1880,7 @@ static int checkout_create_submodules( ...@@ -1885,8 +1880,7 @@ static int checkout_create_submodules(
} }
} }
/* final reload once submodules have been updated */ return 0;
return git_submodule_reload_all(data->repo, 1);
} }
static int checkout_lookup_head_tree(git_tree **out, git_repository *repo) static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
......
...@@ -1194,6 +1194,26 @@ fail_parse: ...@@ -1194,6 +1194,26 @@ fail_parse:
return -1; 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) int git_config_parse_bool(int *out, const char *value)
{ {
if (git__parse_bool(out, value) == 0) if (git__parse_bool(out, value) == 0)
......
...@@ -82,4 +82,10 @@ extern int git_config__get_int_force( ...@@ -82,4 +82,10 @@ extern int git_config__get_int_force(
extern int git_config__cvar( extern int git_config__cvar(
int *out, git_config *config, git_cvar_cached 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 #endif
...@@ -186,7 +186,7 @@ static int diff_file_content_commit_to_str( ...@@ -186,7 +186,7 @@ static int diff_file_content_commit_to_str(
return error; 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); git_submodule_free(sm);
return error; return error;
} }
......
...@@ -111,7 +111,6 @@ void git_repository__cleanup(git_repository *repo) ...@@ -111,7 +111,6 @@ void git_repository__cleanup(git_repository *repo)
git_cache_clear(&repo->objects); git_cache_clear(&repo->objects);
git_attr_cache_flush(repo); git_attr_cache_flush(repo);
git_submodule_cache_free(repo);
set_config(repo, NULL); set_config(repo, NULL);
set_index(repo, NULL); set_index(repo, NULL);
......
...@@ -121,7 +121,6 @@ struct git_repository { ...@@ -121,7 +121,6 @@ struct git_repository {
git_refdb *_refdb; git_refdb *_refdb;
git_config *_config; git_config *_config;
git_index *_index; git_index *_index;
git_submodule_cache *_submodules;
git_cache objects; git_cache objects;
git_attr_cache *attrcache; git_attr_cache *attrcache;
......
...@@ -87,17 +87,16 @@ __KHASH_IMPL( ...@@ -87,17 +87,16 @@ __KHASH_IMPL(
str, static kh_inline, const char *, void *, 1, str, static kh_inline, const char *, void *, 1,
str_hash_no_trailing_slash, str_equal_no_trailing_slash) str_hash_no_trailing_slash, str_equal_no_trailing_slash)
static int submodule_cache_init(git_repository *repo, int refresh); static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name);
static void submodule_cache_free(git_submodule_cache *cache); static git_config_backend *open_gitmodules(git_repository *repo, int gitmod);
static git_config_backend *open_gitmodules(git_submodule_cache *, int gitmod);
static int get_url_base(git_buf *url, git_repository *repo); 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 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_config(const git_config_entry *, void *);
static int submodule_load_from_wd_lite(git_submodule *); static int submodule_load_from_wd_lite(git_submodule *);
static void submodule_get_index_status(unsigned int *, 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_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) 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) ...@@ -111,69 +110,10 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
return git_buf_puts(key, 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 * 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) static void submodule_set_lookup_error(int error, const char *name)
{ {
if (!error) if (!error)
...@@ -184,42 +124,95 @@ static void submodule_set_lookup_error(int error, const char *name) ...@@ -184,42 +124,95 @@ static void submodule_set_lookup_error(int error, const char *name)
"Submodule '%s' has not been added yet", name); "Submodule '%s' has not been added yet", name);
} }
int git_submodule__lookup( 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_submodule **out, /* NULL if user only wants to test existence */
git_repository *repo, git_repository *repo,
const char *name) /* trailing slash is allowed */ const char *name) /* trailing slash is allowed */
{ {
int error; int error;
unsigned int location;
git_submodule *sm;
assert(repo && name); assert(repo && name);
if ((error = submodule_cache_init(repo, CACHE_OK)) < 0) if ((error = submodule_alloc(&sm, repo, name)) < 0)
return error; return error;
if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) if ((error = git_submodule_reload(sm, false)) < 0) {
submodule_set_lookup_error(error, name); git_submodule_free(sm);
return error;
}
if ((error = git_submodule_location(&location, sm)) < 0) {
git_submodule_free(sm);
return error; return error;
} }
int git_submodule_lookup( /* If it's not configured, we need to check for the path */
git_submodule **out, /* NULL if user only wants to test existence */ if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
git_repository *repo, git_config_backend *mods;
const char *name) /* trailing slash is allowed */ const char *pattern = "submodule\\..*\\.path";
{ fbp_data data = { name, NULL };
int error;
assert(repo && name); mods = open_gitmodules(repo, GITMODULES_EXISTING);
if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0) 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; return error;
}
if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) { 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)) { if (git_repository_workdir(repo)) {
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
if (git_buf_join3(&path, if (git_buf_join3(&path,
'/', git_repository_workdir(repo), name, DOT_GIT) < 0) '/', git_repository_workdir(repo), name, DOT_GIT) < 0)
return -1; return -1;
...@@ -231,9 +224,15 @@ int git_submodule_lookup( ...@@ -231,9 +224,15 @@ int git_submodule_lookup(
} }
submodule_set_lookup_error(error, name); 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) static void submodule_free_dup(void *sm)
...@@ -241,41 +240,221 @@ static void submodule_free_dup(void *sm) ...@@ -241,41 +240,221 @@ static void submodule_free_dup(void *sm)
git_submodule_free(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( int git_submodule_foreach(
git_repository *repo, git_repository *repo,
int (*callback)(git_submodule *sm, const char *name, void *payload), int (*callback)(git_submodule *sm, const char *name, void *payload),
void *payload) void *payload)
{ {
git_vector snapshot = GIT_VECTOR_INIT;
git_strmap *submodules;
git_submodule *sm;
int error; int error;
size_t i; 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; return error;
cache = repo->_submodules; if ((error = all_submodules(repo, submodules)) < 0)
goto done;
if (git_mutex_lock(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache");
return -1;
}
if (!(error = git_vector_init( 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) if ((error = git_vector_insert(&snapshot, sm)) < 0)
break; break;
GIT_REFCOUNT_INC(sm); GIT_REFCOUNT_INC(sm);
}); });
} }
git_mutex_unlock(&cache->lock);
if (error < 0) if (error < 0)
goto done; goto done;
...@@ -293,17 +472,12 @@ done: ...@@ -293,17 +472,12 @@ done:
git_submodule_free(sm); git_submodule_free(sm);
git_vector_free(&snapshot); git_vector_free(&snapshot);
return error; git_strmap_foreach_value(submodules, sm, {
} git_submodule_free(sm);
});
void git_submodule_cache_free(git_repository *repo) git_strmap_free(submodules);
{
git_submodule_cache *cache;
assert(repo);
if ((cache = git__swap(repo->_submodules, NULL)) != NULL) return error;
submodule_cache_free(cache);
} }
static int submodule_repo_init( static int submodule_repo_init(
...@@ -394,7 +568,7 @@ int git_submodule_add_setup( ...@@ -394,7 +568,7 @@ int git_submodule_add_setup(
/* update .gitmodules */ /* update .gitmodules */
if (!(mods = open_gitmodules(repo->_submodules, GITMODULES_CREATE))) { if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
giterr_set(GITERR_SUBMODULE, giterr_set(GITERR_SUBMODULE,
"Adding submodules to a bare repository is not supported"); "Adding submodules to a bare repository is not supported");
return -1; return -1;
...@@ -430,20 +604,11 @@ int git_submodule_add_setup( ...@@ -430,20 +604,11 @@ int git_submodule_add_setup(
goto cleanup; goto cleanup;
} }
/* add submodule to hash and "reload" it */ if ((error = git_submodule_lookup(&sm, repo, path)) < 0)
if (git_mutex_lock(&repo->_submodules->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache");
error = -1;
goto cleanup; goto cleanup;
}
if (!(error = submodule_get(&sm, repo->_submodules, path, NULL)) &&
!(error = git_submodule_reload(sm, false)))
error = git_submodule_init(sm, false); error = git_submodule_init(sm, false);
git_mutex_unlock(&repo->_submodules->lock);
cleanup: cleanup:
if (error && sm) { if (error && sm) {
git_submodule_free(sm); git_submodule_free(sm);
...@@ -572,15 +737,6 @@ cleanup: ...@@ -572,15 +737,6 @@ cleanup:
return error; 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) const char *git_submodule_update_to_str(git_submodule_update_t update)
{ {
int i; int i;
...@@ -590,89 +746,6 @@ const char *git_submodule_update_to_str(git_submodule_update_t update) ...@@ -590,89 +746,6 @@ const char *git_submodule_update_to_str(git_submodule_update_t update)
return NULL; 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) git_repository *git_submodule_owner(git_submodule *submodule)
{ {
assert(submodule); assert(submodule);
...@@ -718,37 +791,66 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur ...@@ -718,37 +791,66 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur
return error; 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); git_buf key = GIT_BUF_INIT;
return submodule->branch; 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_cvar_t type;
const char *val;
git__free(submodule->branch);
submodule->branch = NULL;
if (branch != NULL) { if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) {
submodule->branch = git__strdup(branch); giterr_set(GITERR_SUBMODULE, "invalid value for %s", var);
GITERR_CHECK_ALLOC(submodule->branch); 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); assert(repo && name);
GITERR_CHECK_ALLOC(submodule->url);
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) const git_oid *git_submodule_index_id(git_submodule *submodule)
...@@ -799,19 +901,11 @@ git_submodule_ignore_t git_submodule_ignore(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_NONE : submodule->ignore;
} }
git_submodule_ignore_t git_submodule_set_ignore( int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore)
git_submodule *submodule, git_submodule_ignore_t ignore)
{ {
git_submodule_ignore_t old; assert(repo && name);
assert(submodule);
if (ignore == GIT_SUBMODULE_IGNORE_RESET)
ignore = submodule->ignore_default;
old = submodule->ignore; return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore);
submodule->ignore = ignore;
return old;
} }
git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) 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) ...@@ -821,19 +915,11 @@ git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update; GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update;
} }
git_submodule_update_t git_submodule_set_update( int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update)
git_submodule *submodule, git_submodule_update_t update)
{ {
git_submodule_update_t old; assert(repo && name);
assert(submodule);
if (update == GIT_SUBMODULE_UPDATE_RESET)
update = submodule->update_default;
old = submodule->update; return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update);
submodule->update = update;
return old;
} }
git_submodule_recurse_t git_submodule_fetch_recurse_submodules( git_submodule_recurse_t git_submodule_fetch_recurse_submodules(
...@@ -843,20 +929,11 @@ 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; return submodule->fetch_recurse;
} }
git_submodule_recurse_t git_submodule_set_fetch_recurse_submodules( int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse)
git_submodule *submodule, {
git_submodule_recurse_t fetch_recurse_submodules) assert(repo && name);
{
git_submodule_recurse_t old;
assert(submodule);
if (fetch_recurse_submodules == GIT_SUBMODULE_RECURSE_RESET)
fetch_recurse_submodules = submodule->fetch_recurse_default;
old = submodule->fetch_recurse; return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse);
submodule->fetch_recurse = fetch_recurse_submodules;
return old;
} }
static int submodule_repo_create( static int submodule_repo_create(
...@@ -953,7 +1030,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio ...@@ -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)); 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 */ /* 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; goto done;
/* /*
...@@ -1197,11 +1274,6 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm) ...@@ -1197,11 +1274,6 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm)
return git_submodule__open(subrepo, sm, false); 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( static void submodule_update_from_index_entry(
git_submodule *sm, const git_index_entry *ie) git_submodule *sm, const git_index_entry *ie)
{ {
...@@ -1280,14 +1352,12 @@ int git_submodule_reload(git_submodule *sm, int force) ...@@ -1280,14 +1352,12 @@ int git_submodule_reload(git_submodule *sm, int force)
{ {
int error = 0; int error = 0;
git_config_backend *mods; git_config_backend *mods;
git_submodule_cache *cache; lfc_data data = { 0 };
GIT_UNUSED(force); GIT_UNUSED(force);
assert(sm); assert(sm);
cache = sm->repo->_submodules;
/* refresh index data */ /* refresh index data */
if ((error = submodule_update_index(sm)) < 0) if ((error = submodule_update_index(sm)) < 0)
return error; return error;
...@@ -1301,7 +1371,7 @@ int git_submodule_reload(git_submodule *sm, int force) ...@@ -1301,7 +1371,7 @@ int git_submodule_reload(git_submodule *sm, int force)
return error; return error;
/* refresh config data */ /* refresh config data */
mods = open_gitmodules(cache, GITMODULES_EXISTING); mods = open_gitmodules(sm->repo, GITMODULES_EXISTING);
if (mods != NULL) { if (mods != NULL) {
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
...@@ -1309,11 +1379,14 @@ int git_submodule_reload(git_submodule *sm, int force) ...@@ -1309,11 +1379,14 @@ int git_submodule_reload(git_submodule *sm, int force)
git_buf_text_puts_escape_regex(&path, sm->name); git_buf_text_puts_escape_regex(&path, sm->name);
git_buf_puts(&path, ".*"); git_buf_puts(&path, ".*");
if (git_buf_oom(&path)) if (git_buf_oom(&path)) {
error = -1; error = -1;
else } else {
data.have_sm = 1;
data.sm = sm;
error = git_config_file_foreach_match( 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_buf_free(&path);
git_config_file_free(mods); git_config_file_free(mods);
...@@ -1352,7 +1425,7 @@ int git_submodule__status( ...@@ -1352,7 +1425,7 @@ int git_submodule__status(
unsigned int status; unsigned int status;
git_repository *smrepo = NULL; git_repository *smrepo = NULL;
if (ign < GIT_SUBMODULE_IGNORE_NONE) if (ign == GIT_SUBMODULE_IGNORE_FALLBACK)
ign = sm->ignore; ign = sm->ignore;
/* only return location info if ignore == all */ /* only return location info if ignore == all */
...@@ -1401,11 +1474,20 @@ int git_submodule__status( ...@@ -1401,11 +1474,20 @@ int git_submodule__status(
return 0; 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);
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 git_submodule__status(status, NULL, NULL, NULL, sm, 0); return error;
} }
int git_submodule_location(unsigned int *location, git_submodule *sm) int git_submodule_location(unsigned int *location, git_submodule *sm)
...@@ -1422,7 +1504,7 @@ 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( 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; size_t namelen;
git_submodule *sm; git_submodule *sm;
...@@ -1445,56 +1527,20 @@ static int submodule_alloc( ...@@ -1445,56 +1527,20 @@ static int submodule_alloc(
sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE; sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT; sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO; sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO;
sm->repo = cache->repo; sm->repo = repo;
sm->branch = NULL; sm->branch = NULL;
*out = sm; *out = sm;
return 0; 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) static void submodule_release(git_submodule *sm)
{ {
if (!sm) if (!sm)
return; return;
if (sm->repo) { if (sm->repo) {
git_submodule_cache *cache = sm->repo->_submodules;
sm->repo = NULL; sm->repo = NULL;
submodule_cache_remove_item(cache, sm, false);
} }
if (sm->path != sm->name) if (sm->path != sm->name)
...@@ -1513,54 +1559,6 @@ void git_submodule_free(git_submodule *sm) ...@@ -1513,54 +1559,6 @@ void git_submodule_free(git_submodule *sm)
GIT_REFCOUNT_DEC(sm, submodule_release); 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) static int submodule_config_error(const char *property, const char *value)
{ {
giterr_set(GITERR_INVALID, giterr_set(GITERR_INVALID,
...@@ -1613,12 +1611,12 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value) ...@@ -1613,12 +1611,12 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
static int submodule_load_from_config( static int submodule_load_from_config(
const git_config_entry *entry, void *payload) const git_config_entry *entry, void *payload)
{ {
git_submodule_cache *cache = payload;
const char *namestart, *property; const char *namestart, *property;
const char *key = entry->name, *value = entry->value, *path; const char *key = entry->name, *value = entry->value, *path;
char *alternate = NULL, *replaced = NULL; char *alternate = NULL, *replaced = NULL;
git_buf name = GIT_BUF_INIT; git_buf name = GIT_BUF_INIT;
git_submodule *sm = NULL; lfc_data *data = payload;
git_submodule *sm;
int error = 0; int error = 0;
if (git__prefixcmp(key, "submodule.") != 0) if (git__prefixcmp(key, "submodule.") != 0)
...@@ -1633,10 +1631,29 @@ static int submodule_load_from_config( ...@@ -1633,10 +1631,29 @@ static int submodule_load_from_config(
property++; property++;
path = !strcasecmp(property, "path") ? value : NULL; path = !strcasecmp(property, "path") ? value : NULL;
if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 || if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0)
(error = submodule_get(&sm, cache, name.ptr, path)) < 0)
goto done; 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; sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
/* Only from config might we get differing names & paths. If so, then /* Only from config might we get differing names & paths. If so, then
...@@ -1648,7 +1665,7 @@ static int submodule_load_from_config( ...@@ -1648,7 +1665,7 @@ static int submodule_load_from_config(
*/ */
if (strcmp(sm->name, name.ptr) != 0) { /* name changed */ 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; replaced = sm->name;
sm->name = sm->path; sm->name = sm->path;
} else { } else {
...@@ -1674,7 +1691,6 @@ static int submodule_load_from_config( ...@@ -1674,7 +1691,6 @@ static int submodule_load_from_config(
/* Deregister under name being replaced */ /* Deregister under name being replaced */
if (replaced) { if (replaced) {
git_strmap_delete(cache->submodules, replaced);
git_submodule_free(sm); git_submodule_free(sm);
git__free(replaced); git__free(replaced);
} }
...@@ -1682,7 +1698,6 @@ static int submodule_load_from_config( ...@@ -1682,7 +1698,6 @@ static int submodule_load_from_config(
/* Insert under alternate key */ /* Insert under alternate key */
if (alternate) { if (alternate) {
void *old_sm = NULL; void *old_sm = NULL;
git_strmap_insert2(cache->submodules, alternate, sm, old_sm, error);
if (error < 0) if (error < 0)
goto done; goto done;
...@@ -1742,7 +1757,6 @@ static int submodule_load_from_config( ...@@ -1742,7 +1757,6 @@ static int submodule_load_from_config(
/* ignore other unknown submodule properties */ /* ignore other unknown submodule properties */
done: done:
git_submodule_free(sm); /* offset refcount inc from submodule_get() */
git_buf_free(&name); git_buf_free(&name);
return error; return error;
} }
...@@ -1764,86 +1778,11 @@ static int submodule_load_from_wd_lite(git_submodule *sm) ...@@ -1764,86 +1778,11 @@ static int submodule_load_from_wd_lite(git_submodule *sm)
return 0; 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( static git_config_backend *open_gitmodules(
git_submodule_cache *cache, git_repository *repo,
int okay_to_create) 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_buf path = GIT_BUF_INIT;
git_config_backend *mods = NULL; git_config_backend *mods = NULL;
...@@ -1868,198 +1807,6 @@ static git_config_backend *open_gitmodules( ...@@ -1868,198 +1807,6 @@ static git_config_backend *open_gitmodules(
return mods; 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 */ /* 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) static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo)
{ {
......
...@@ -99,23 +99,6 @@ struct git_submodule { ...@@ -99,23 +99,6 @@ struct git_submodule {
git_oid wd_oid; 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) */ /* Force revalidation of submodule data cache (alloc as needed) */
extern int git_submodule_cache_refresh(git_repository *repo); extern int git_submodule_cache_refresh(git_repository *repo);
...@@ -137,9 +120,6 @@ enum { ...@@ -137,9 +120,6 @@ enum {
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \ #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
((S) & ~(0xFFFFFFFFu << 20)) ((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 */ /* Internal lookup does not attempt to refresh cached data */
extern int git_submodule__lookup( extern int git_submodule__lookup(
git_submodule **out, git_repository *repo, const char *path); git_submodule **out, git_repository *repo, const char *path);
...@@ -163,8 +143,4 @@ extern int git_submodule_parse_ignore( ...@@ -163,8 +143,4 @@ extern int git_submodule_parse_ignore(
extern int git_submodule_parse_update( extern int git_submodule_parse_update(
git_submodule_update_t *out, const char *value); 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 #endif
...@@ -273,13 +273,13 @@ void test_diff_submodules__invalid_cache(void) ...@@ -273,13 +273,13 @@ void test_diff_submodules__invalid_cache(void)
/* create untracked file in submodule working directory */ /* create untracked file in submodule working directory */
cl_git_mkfile("submod2/sm_changed_head/new_around_here", "hello"); 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)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty); check_diff_patches(diff, expected_dirty);
git_diff_free(diff); 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)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged); check_diff_patches(diff, expected_unchanged);
...@@ -301,7 +301,7 @@ void test_diff_submodules__invalid_cache(void) ...@@ -301,7 +301,7 @@ void test_diff_submodules__invalid_cache(void)
check_diff_patches(diff, expected_dirty); check_diff_patches(diff, expected_dirty);
git_diff_free(diff); 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)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged); check_diff_patches(diff, expected_unchanged);
...@@ -312,13 +312,13 @@ void test_diff_submodules__invalid_cache(void) ...@@ -312,13 +312,13 @@ void test_diff_submodules__invalid_cache(void)
cl_git_pass(git_repository_index(&smindex, smrepo)); cl_git_pass(git_repository_index(&smindex, smrepo));
cl_git_pass(git_index_add_bypath(smindex, "file_to_modify")); 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)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty); check_diff_patches(diff, expected_dirty);
git_diff_free(diff); 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)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged); check_diff_patches(diff, expected_unchanged);
...@@ -327,19 +327,19 @@ void test_diff_submodules__invalid_cache(void) ...@@ -327,19 +327,19 @@ void test_diff_submodules__invalid_cache(void)
/* commit changed index of submodule */ /* commit changed index of submodule */
cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Move it"); 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)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_moved); check_diff_patches(diff, expected_moved);
git_diff_free(diff); 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)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged); check_diff_patches(diff, expected_unchanged);
git_diff_free(diff); 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)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_moved_dirty); check_diff_patches(diff, expected_moved_dirty);
......
...@@ -89,7 +89,7 @@ void test_diff_tree__0(void) ...@@ -89,7 +89,7 @@ void test_diff_tree__0(void)
} }
#define DIFF_OPTS(FLAGS, CTXT) \ #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} {NULL,0}, NULL, NULL, (CTXT), 1}
void test_diff_tree__options(void) void test_diff_tree__options(void)
......
...@@ -23,10 +23,10 @@ void test_submodule_init__absolute_url(void) ...@@ -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_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_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*/ /* 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*/ /* verify that the .gitmodules is set with an absolute path*/
cl_assert_equal_s(absolute_url.ptr, git_submodule_url(sm)); cl_assert_equal_s(absolute_url.ptr, git_submodule_url(sm));
......
...@@ -41,17 +41,15 @@ void test_submodule_modify__init(void) ...@@ -41,17 +41,15 @@ void test_submodule_modify__init(void)
git_config_free(cfg); git_config_free(cfg);
/* confirm no submodule data in config */ /* confirm no submodule data in config */
cl_git_pass(git_repository_config(&cfg, g_repo)); cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url")); cl_git_fail_with(GIT_ENOTFOUND, 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_with(GIT_ENOTFOUND, 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_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
git_config_free(cfg); git_config_free(cfg);
/* call init and see that settings are copied */ /* call init and see that settings are copied */
cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL)); cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL));
git_submodule_reload_all(g_repo, 1);
/* confirm submodule data in config */ /* confirm submodule data in config */
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo)); cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url")); cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
...@@ -130,132 +128,85 @@ void test_submodule_modify__sync(void) ...@@ -130,132 +128,85 @@ void test_submodule_modify__sync(void)
git_submodule_free(sm3); 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; git_submodule *sm;
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_free(sm1); cl_git_pass(git_submodule_set_ignore(g_repo, "sm_changed_head", ignore));
git_submodule_free(sm2);
git_repository_free(r2); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
git__free(old_url); 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; git_submodule *sm;
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); cl_git_pass(git_submodule_set_url(g_repo, "sm_changed_head", SM_LIBGIT2_URL));
cl_git_pass(git_submodule_save(sm)); 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); git_submodule_free(sm);
} }
...@@ -21,19 +21,11 @@ void test_submodule_nosubs__lookup(void) ...@@ -21,19 +21,11 @@ void test_submodule_nosubs__lookup(void)
cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo")); 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_ENOTFOUND, git_submodule_lookup(&sm, repo, "subdir"));
cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo")); 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) static int fake_submod_cb(git_submodule *sm, const char *n, void *p)
{ {
GIT_UNUSED(sm); GIT_UNUSED(n); GIT_UNUSED(p); GIT_UNUSED(sm); GIT_UNUSED(n); GIT_UNUSED(p);
...@@ -57,41 +49,7 @@ void test_submodule_nosubs__add(void) ...@@ -57,41 +49,7 @@ void test_submodule_nosubs__add(void)
git_submodule_free(sm2); git_submodule_free(sm2);
cl_git_pass(git_submodule_foreach(repo, fake_submod_cb, NULL)); 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); git_submodule_free(sm);
} }
...@@ -100,10 +58,8 @@ void test_submodule_nosubs__bad_gitmodules(void) ...@@ -100,10 +58,8 @@ void test_submodule_nosubs__bad_gitmodules(void)
git_repository *repo = cl_git_sandbox_init("status"); 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_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_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_git_pass(git_submodule_lookup(NULL, repo, "foobar"));
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(NULL, repo, "subdir")); cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(NULL, repo, "subdir"));
......
...@@ -107,56 +107,47 @@ void test_submodule_status__ignore_none(void) ...@@ -107,56 +107,47 @@ void test_submodule_status__ignore_none(void)
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0); 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) void test_submodule_status__ignore_untracked(void)
{ {
unsigned int status; unsigned int status;
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED;
rm_submodule("sm_unchanged"); 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, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", 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); 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); 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); 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)); 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); 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); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
/* removed sm_unchanged for deleted workdir */ /* 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); cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */ /* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); 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); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
/* update sm_changed_head in index */ /* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head"); 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); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
} }
...@@ -166,42 +157,41 @@ void test_submodule_status__ignore_dirty(void) ...@@ -166,42 +157,41 @@ void test_submodule_status__ignore_dirty(void)
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY;
rm_submodule("sm_unchanged"); 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, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", 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)); 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); 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)); 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)); 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); 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); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
/* removed sm_unchanged for deleted workdir */ /* 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); cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */ /* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); 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); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
/* update sm_changed_head in index */ /* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head"); 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); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
} }
...@@ -211,42 +201,41 @@ void test_submodule_status__ignore_all(void) ...@@ -211,42 +201,41 @@ void test_submodule_status__ignore_all(void)
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL;
rm_submodule("sm_unchanged"); 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, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", 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)); 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)); 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)); 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)); 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)); 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)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* removed sm_unchanged for deleted workdir */ /* 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)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* now mkdir sm_unchanged to test uninitialized */ /* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); 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)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* update sm_changed_head in index */ /* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head"); 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)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
} }
......
...@@ -156,21 +156,18 @@ void refute__submodule_exists( ...@@ -156,21 +156,18 @@ void refute__submodule_exists(
git_repository *repo, const char *name, int expected_error, git_repository *repo, const char *name, int expected_error,
const char *msg, const char *file, int line) const char *msg, const char *file, int line)
{ {
git_submodule *sm;
clar__assert_equal( clar__assert_equal(
file, line, msg, 1, "%i", 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) unsigned int get_submodule_status(git_repository *repo, const char *name)
{ {
git_submodule *sm = NULL;
unsigned int status = 0; unsigned int status = 0;
cl_git_pass(git_submodule_lookup(&sm, repo, name)); assert(repo && name);
cl_assert(sm);
cl_git_pass(git_submodule_status(&status, sm)); cl_git_pass(git_submodule_status(&status, repo, name, GIT_SUBMODULE_IGNORE_FALLBACK));
git_submodule_free(sm);
return status; return status;
} }
......
...@@ -103,7 +103,7 @@ void test_submodule_update__update_submodule(void) ...@@ -103,7 +103,7 @@ void test_submodule_update__update_submodule(void)
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* verify the initial state of the submodule */ /* 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 | cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_CONFIG |
...@@ -114,7 +114,7 @@ void test_submodule_update__update_submodule(void) ...@@ -114,7 +114,7 @@ void test_submodule_update__update_submodule(void)
cl_git_pass(git_submodule_update(sm, 0, &update_options)); cl_git_pass(git_submodule_update(sm, 0, &update_options));
/* verify state */ /* 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 | cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_CONFIG |
...@@ -142,7 +142,7 @@ void test_submodule_update__update_and_init_submodule(void) ...@@ -142,7 +142,7 @@ void test_submodule_update__update_and_init_submodule(void)
/* get the submodule */ /* get the submodule */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); 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 | cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_CONFIG |
...@@ -177,7 +177,7 @@ void test_submodule_update__update_already_checked_out_submodule(void) ...@@ -177,7 +177,7 @@ void test_submodule_update__update_already_checked_out_submodule(void)
/* Initialize and update the sub repository */ /* Initialize and update the sub repository */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); 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 | cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_CONFIG |
...@@ -203,7 +203,11 @@ void test_submodule_update__update_already_checked_out_submodule(void) ...@@ -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. * 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 | cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_CONFIG |
...@@ -251,7 +255,7 @@ void test_submodule_update__update_blocks_on_dirty_wd(void) ...@@ -251,7 +255,7 @@ void test_submodule_update__update_blocks_on_dirty_wd(void)
/* Initialize and update the sub repository */ /* Initialize and update the sub repository */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); 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 | cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_CONFIG |
...@@ -277,7 +281,11 @@ void test_submodule_update__update_blocks_on_dirty_wd(void) ...@@ -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. * 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 | cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_CONFIG |
...@@ -324,7 +332,7 @@ void test_submodule_update__can_force_update(void) ...@@ -324,7 +332,7 @@ void test_submodule_update__can_force_update(void)
/* Initialize and update the sub repository */ /* Initialize and update the sub repository */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); 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 | cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_CONFIG |
...@@ -349,7 +357,11 @@ void test_submodule_update__can_force_update(void) ...@@ -349,7 +357,11 @@ void test_submodule_update__can_force_update(void)
* Verify state after checkout of parent repository. The submodule ID in the * Verify state after checkout of parent repository. The submodule ID in the
* HEAD commit and index should be updated, but not the workdir. * 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 | cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_CONFIG |
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment