Merge pull request #2204 from libgit2/rb/submodule-reference-counting

Make submodules externally refcounted
This commit is contained in:
Vicent Marti 2014-03-26 18:29:34 +01:00
commit dc7efa1aef
15 changed files with 461 additions and 364 deletions

View File

@ -363,18 +363,21 @@ static void print_short(git_repository *repo, git_status_list *status)
unsigned int smstatus = 0; unsigned int smstatus = 0;
if (!git_submodule_lookup( if (!git_submodule_lookup(
&sm, repo, s->index_to_workdir->new_file.path) && &sm, repo, s->index_to_workdir->new_file.path)) {
!git_submodule_status(&smstatus, sm))
{ 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)
extra = " (modified content)"; extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
extra = " (modified content)"; extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED) else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
extra = " (untracked content)"; extra = " (untracked content)";
}
} }
git_submodule_free(sm);
} }
/** /**

View File

@ -123,21 +123,27 @@ typedef enum {
* There may or may not be anything else at that path, but nothing that * There may or may not be anything else at that path, but nothing that
* looks like a submodule. In this case, this returns GIT_ENOTFOUND. * looks like a submodule. In this case, this returns GIT_ENOTFOUND.
* *
* The submodule object is owned by the containing repo and will be freed * You must call `git_submodule_free` when done with the submodule.
* when the repo is freed. The caller need not free the submodule.
* *
* @param submodule Pointer to submodule description object pointer.. * @param out Output ptr to submodule; pass NULL to just get return code
* @param repo The repository. * @param repo The parent repository
* @param name The name of the submodule. Trailing slashes will be ignored. * @param name The name of or path to the submodule; trailing slashes okay
* @return 0 on success, GIT_ENOTFOUND if submodule does not exist, * @return 0 on success, GIT_ENOTFOUND if submodule does not exist,
* GIT_EEXISTS if submodule exists in working directory only, -1 on * GIT_EEXISTS if submodule exists in working directory only,
* other errors. * -1 on other errors.
*/ */
GIT_EXTERN(int) git_submodule_lookup( GIT_EXTERN(int) git_submodule_lookup(
git_submodule **submodule, git_submodule **out,
git_repository *repo, git_repository *repo,
const char *name); const char *name);
/**
* Release a submodule
*
* @param submodule Submodule object
*/
GIT_EXTERN(void) git_submodule_free(git_submodule *submodule);
/** /**
* Iterate over all tracked submodules of a repository. * Iterate over all tracked submodules of a repository.
* *
@ -175,9 +181,11 @@ GIT_EXTERN(int) git_submodule_foreach(
* `git_submodule_add_finalize()` to wrap up adding the new submodule and * `git_submodule_add_finalize()` to wrap up adding the new submodule and
* .gitmodules to the index to be ready to commit. * .gitmodules to the index to be ready to commit.
* *
* @param submodule The newly created submodule ready to open for clone * You must call `git_submodule_free` on the submodule object when done.
* @param repo Superproject repository to contain the new submodule *
* @param url URL for the submodules remote * @param out The newly created submodule ready to open for clone
* @param repo The repository in which you want to create the submodule
* @param url URL for the submodule's remote
* @param path Path at which the submodule should be created * @param path Path at which the submodule should be created
* @param use_gitlink Should workdir contain a gitlink to the repo in * @param use_gitlink Should workdir contain a gitlink to the repo in
* .git/modules vs. repo directly in workdir. * .git/modules vs. repo directly in workdir.
@ -185,7 +193,7 @@ GIT_EXTERN(int) git_submodule_foreach(
* -1 on other errors. * -1 on other errors.
*/ */
GIT_EXTERN(int) git_submodule_add_setup( GIT_EXTERN(int) git_submodule_add_setup(
git_submodule **submodule, git_submodule **out,
git_repository *repo, git_repository *repo,
const char *url, const char *url,
const char *path, const char *path,
@ -493,15 +501,23 @@ GIT_EXTERN(int) git_submodule_open(
* *
* Call this to reread cached submodule information for this submodule if * Call this to reread cached submodule information for this submodule if
* you have reason to believe that it has changed. * you have reason to believe that it has changed.
*
* @param submodule The submodule to reload
* @param force Force reload even if the data doesn't seem out of date
* @return 0 on success, <0 on error
*/ */
GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule); GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule, int force);
/** /**
* Reread all submodule info. * Reread all submodule info.
* *
* Call this to reload all cached submodule information for the repo. * Call this to reload all cached submodule information for the repo.
*
* @param repo The repository to reload submodule data for
* @param force Force full reload even if the data doesn't seem out of date
* @return 0 on success, <0 on error
*/ */
GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo); GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo, int force);
/** /**
* Get the status for a submodule. * Get the status for a submodule.

View File

@ -147,19 +147,23 @@ static bool checkout_is_workdir_modified(
git_submodule *sm; git_submodule *sm;
unsigned int sm_status = 0; unsigned int sm_status = 0;
const git_oid *sm_oid = NULL; const git_oid *sm_oid = NULL;
bool rval = false;
if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0 || if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0) {
git_submodule_status(&sm_status, sm) < 0) giterr_clear();
return true; return true;
}
if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) if (git_submodule_status(&sm_status, sm) < 0 ||
return true; GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
rval = true;
else if ((sm_oid = git_submodule_wd_id(sm)) == NULL)
rval = false;
else
rval = (git_oid__cmp(&baseitem->id, sm_oid) != 0);
sm_oid = git_submodule_wd_id(sm); git_submodule_free(sm);
if (!sm_oid) return rval;
return false;
return (git_oid__cmp(&baseitem->id, sm_oid) != 0);
} }
/* Look at the cache to decide if the workdir is modified. If not, /* Look at the cache to decide if the workdir is modified. If not,
@ -324,12 +328,17 @@ static bool submodule_is_config_only(
{ {
git_submodule *sm = NULL; git_submodule *sm = NULL;
unsigned int sm_loc = 0; unsigned int sm_loc = 0;
bool rval = false;
if (git_submodule_lookup(&sm, data->repo, path) < 0 || if (git_submodule_lookup(&sm, data->repo, path) < 0)
git_submodule_location(&sm_loc, sm) < 0 ||
sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG)
return true; return true;
if (git_submodule_location(&sm_loc, sm) < 0 ||
sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG)
rval = true;
git_submodule_free(sm);
return false; return false;
} }
@ -1258,7 +1267,6 @@ static int checkout_submodule(
const git_diff_file *file) const git_diff_file *file)
{ {
int error = 0; int error = 0;
git_submodule *sm;
/* Until submodules are supported, UPDATE_ONLY means do nothing here */ /* Until submodules are supported, UPDATE_ONLY means do nothing here */
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
@ -1269,7 +1277,7 @@ static int checkout_submodule(
data->opts.dir_mode, GIT_MKDIR_PATH)) < 0) data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
return error; return error;
if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0) { if ((error = git_submodule_lookup(NULL, data->repo, file->path)) < 0) {
/* I've observed repos with submodules in the tree that do not /* I've observed repos with submodules in the tree that do not
* have a .gitmodules - core Git just makes an empty directory * have a .gitmodules - core Git just makes an empty directory
*/ */
@ -1510,7 +1518,7 @@ static int checkout_create_submodules(
/* initial reload of submodules if .gitmodules was changed */ /* initial reload of submodules if .gitmodules was changed */
if (data->reload_submodules && if (data->reload_submodules &&
(error = git_submodule_reload_all(data->repo)) < 0) (error = git_submodule_reload_all(data->repo, 1)) < 0)
return error; return error;
git_vector_foreach(&data->diff->deltas, i, delta) { git_vector_foreach(&data->diff->deltas, i, delta) {
@ -1534,7 +1542,7 @@ static int checkout_create_submodules(
} }
/* final reload once submodules have been updated */ /* final reload once submodules have been updated */
return git_submodule_reload_all(data->repo); 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)

View File

@ -528,12 +528,15 @@ int git_diff__oid_for_file(
/* calculate OID for file if possible */ /* calculate OID for file if possible */
if (S_ISGITLINK(mode)) { if (S_ISGITLINK(mode)) {
git_submodule *sm; git_submodule *sm;
const git_oid *sm_oid;
if (!git_submodule_lookup(&sm, repo, path) && memset(oid, 0, sizeof(*oid));
(sm_oid = git_submodule_wd_id(sm)) != NULL)
git_oid_cpy(oid, sm_oid); if (!git_submodule_lookup(&sm, repo, path)) {
else { const git_oid *sm_oid = git_submodule_wd_id(sm);
if (sm_oid)
git_oid_cpy(oid, sm_oid);
git_submodule_free(sm);
} else {
/* if submodule lookup failed probably just in an intermediate /* if submodule lookup failed probably just in an intermediate
* state where some init hasn't happened, so ignore the error * state where some init hasn't happened, so ignore the error
*/ */
@ -615,24 +618,24 @@ static int maybe_modified_submodule(
} }
if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL) if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
return 0; /* ignore it */;
else if ((error = git_submodule__status(
if ((error = git_submodule__status(
&sm_status, NULL, NULL, found_oid, sub, ign)) < 0) &sm_status, NULL, NULL, found_oid, sub, ign)) < 0)
return error; /* return error below */;
/* check IS_WD_UNMODIFIED because this case is only used /* check IS_WD_UNMODIFIED because this case is only used
* when the new side of the diff is the working directory * when the new side of the diff is the working directory
*/ */
if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status)) else if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
*status = GIT_DELTA_MODIFIED; *status = GIT_DELTA_MODIFIED;
/* now that we have a HEAD OID, check if HEAD moved */ /* now that we have a HEAD OID, check if HEAD moved */
if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 && else if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
!git_oid_equal(&info->oitem->id, found_oid)) !git_oid_equal(&info->oitem->id, found_oid))
*status = GIT_DELTA_MODIFIED; *status = GIT_DELTA_MODIFIED;
return 0; git_submodule_free(sub);
return error;
} }
static int maybe_modified( static int maybe_modified(
@ -960,10 +963,8 @@ static int handle_unmatched_new_item(
delta_type = GIT_DELTA_ADDED; delta_type = GIT_DELTA_ADDED;
else if (nitem->mode == GIT_FILEMODE_COMMIT) { else if (nitem->mode == GIT_FILEMODE_COMMIT) {
git_submodule *sm;
/* ignore things that are not actual submodules */ /* ignore things that are not actual submodules */
if (git_submodule_lookup(&sm, info->repo, nitem->path) != 0) { if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) {
giterr_clear(); giterr_clear();
delta_type = GIT_DELTA_IGNORED; delta_type = GIT_DELTA_IGNORED;
} }

View File

@ -177,11 +177,17 @@ static int diff_file_content_commit_to_str(
unsigned int sm_status = 0; unsigned int sm_status = 0;
const git_oid *sm_head; const git_oid *sm_head;
if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0 || if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0) {
(error = git_submodule_status(&sm_status, sm)) < 0) {
/* GIT_EEXISTS means a "submodule" that has not been git added */ /* GIT_EEXISTS means a "submodule" that has not been git added */
if (error == GIT_EEXISTS) if (error == GIT_EEXISTS) {
giterr_clear();
error = 0; error = 0;
}
return error;
}
if ((error = git_submodule_status(&sm_status, sm)) < 0) {
git_submodule_free(sm);
return error; return error;
} }
@ -196,6 +202,8 @@ static int diff_file_content_commit_to_str(
if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
status = "-dirty"; status = "-dirty";
git_submodule_free(sm);
} }
git_oid_tostr(oid, sizeof(oid), &fc->file->id); git_oid_tostr(oid, sizeof(oid), &fc->file->id);
@ -312,7 +320,8 @@ static int diff_file_content_load_workdir_file(
error = git_filter_list_apply_to_data(&out, fl, &raw); error = git_filter_list_apply_to_data(&out, fl, &raw);
git_buf_free(&raw); if (out.ptr != raw.ptr)
git_buf_free(&raw);
if (!error) { if (!error) {
fc->map.len = out.size; fc->map.len = out.size;

View File

@ -77,12 +77,12 @@ __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 load_submodule_config(git_repository *repo); static int load_submodule_config(git_repository *repo, bool reload);
static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *); static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *);
static int lookup_head_remote(git_buf *url, git_repository *repo); static int lookup_head_remote(git_buf *url, git_repository *repo);
static int submodule_get(git_submodule **, git_repository *, const char *, const char *); static int submodule_get(git_submodule **, git_repository *, 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 *, const char *, void *); static int submodule_load_from_wd_lite(git_submodule *);
static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool); static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);
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);
@ -99,27 +99,55 @@ 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_strmap *cache,
const char *name,
const char *alternate)
{
khiter_t pos;
/* lock cache */
pos = git_strmap_lookup_index(cache, name);
if (!git_strmap_valid_index(cache, pos) && alternate)
pos = git_strmap_lookup_index(cache, alternate);
if (!git_strmap_valid_index(cache, pos)) {
/* unlock cache */
return GIT_ENOTFOUND; /* don't set error - caller will cope */
}
if (out != NULL) {
git_submodule *sm = git_strmap_value_at(cache, pos);
GIT_REFCOUNT_INC(sm);
*out = sm;
}
/* unlock cache */
return 0;
}
/* /*
* PUBLIC APIS * PUBLIC APIS
*/ */
int git_submodule_lookup( int git_submodule_lookup(
git_submodule **sm_ptr, /* 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;
khiter_t pos;
assert(repo && name); assert(repo && name);
if ((error = load_submodule_config(repo)) < 0) if ((error = load_submodule_config(repo, false)) < 0)
return error; return error;
pos = git_strmap_lookup_index(repo->submodules, name); if ((error = submodule_lookup(out, repo->submodules, name, NULL)) < 0) {
if (!git_strmap_valid_index(repo->submodules, pos)) {
error = GIT_ENOTFOUND;
/* check if a plausible submodule exists at path */ /* check if a plausible submodule exists at path */
if (git_repository_workdir(repo)) { if (git_repository_workdir(repo)) {
@ -137,14 +165,9 @@ int git_submodule_lookup(
giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ?
"No submodule named '%s'" : "No submodule named '%s'" :
"Submodule '%s' has not been added yet", name); "Submodule '%s' has not been added yet", name);
return error;
} }
if (sm_ptr) return error;
*sm_ptr = git_strmap_value_at(repo->submodules, pos);
return 0;
} }
int git_submodule_foreach( int git_submodule_foreach(
@ -159,7 +182,7 @@ int git_submodule_foreach(
assert(repo && callback); assert(repo && callback);
if ((error = load_submodule_config(repo)) < 0) if ((error = load_submodule_config(repo, true)) < 0)
return error; return error;
git_strmap_foreach_value(repo->submodules, sm, { git_strmap_foreach_value(repo->submodules, sm, {
@ -198,12 +221,15 @@ void git_submodule_config_free(git_repository *repo)
if (smcfg == NULL) if (smcfg == NULL)
return; return;
git_strmap_foreach_value(smcfg, sm, { git_submodule_free(sm); }); git_strmap_foreach_value(smcfg, sm, {
sm->repo = NULL; /* disconnect from repo */;
git_submodule_free(sm);
});
git_strmap_free(smcfg); git_strmap_free(smcfg);
} }
int git_submodule_add_setup( int git_submodule_add_setup(
git_submodule **submodule, git_submodule **out,
git_repository *repo, git_repository *repo,
const char *url, const char *url,
const char *path, const char *path,
@ -211,7 +237,7 @@ int git_submodule_add_setup(
{ {
int error = 0; int error = 0;
git_config_backend *mods = NULL; git_config_backend *mods = NULL;
git_submodule *sm; git_submodule *sm = NULL;
git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT; git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
git_repository *subrepo = NULL; git_repository *subrepo = NULL;
@ -223,6 +249,7 @@ int git_submodule_add_setup(
if (git_submodule_lookup(&sm, repo, path) < 0) if (git_submodule_lookup(&sm, repo, path) < 0)
giterr_clear(); giterr_clear();
else { else {
git_submodule_free(sm);
giterr_set(GITERR_SUBMODULE, giterr_set(GITERR_SUBMODULE,
"Attempt to add a submodule that already exists"); "Attempt to add a submodule that already exists");
return GIT_EEXISTS; return GIT_EEXISTS;
@ -307,12 +334,16 @@ int git_submodule_add_setup(
/* add submodule to hash and "reload" it */ /* add submodule to hash and "reload" it */
if (!(error = submodule_get(&sm, repo, path, NULL)) && if (!(error = submodule_get(&sm, repo, path, NULL)) &&
!(error = git_submodule_reload(sm))) !(error = git_submodule_reload(sm, false)))
error = git_submodule_init(sm, false); error = git_submodule_init(sm, false);
cleanup: cleanup:
if (submodule != NULL) if (error && sm) {
*submodule = !error ? sm : NULL; git_submodule_free(sm);
sm = NULL;
}
if (out != NULL)
*out = sm;
if (mods != NULL) if (mods != NULL)
git_config_file_free(mods); git_config_file_free(mods);
@ -775,11 +806,60 @@ 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) static void submodule_cache_remove_item(
git_strmap *cache,
const char *name,
git_submodule *expected,
bool free_after_remove)
{ {
khiter_t pos;
git_submodule *found;
if (!cache)
return;
pos = git_strmap_lookup_index(cache, name);
if (!git_strmap_valid_index(cache, pos))
return;
found = git_strmap_value_at(cache, pos);
if (expected && found != expected)
return;
git_strmap_set_value_at(cache, pos, NULL);
git_strmap_delete_at(cache, pos);
if (free_after_remove)
git_submodule_free(found);
}
int git_submodule_reload_all(git_repository *repo, int force)
{
int error = 0;
git_submodule *sm;
GIT_UNUSED(force);
assert(repo); assert(repo);
git_submodule_config_free(repo);
return load_submodule_config(repo); if (repo->submodules)
git_strmap_foreach_value(repo->submodules, sm, { sm->flags = 0; });
error = load_submodule_config(repo, true);
git_strmap_foreach_value(repo->submodules, sm, {
git_strmap *cache = repo->submodules;
if ((sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS) == 0) {
submodule_cache_remove_item(cache, sm->name, sm, true);
if (sm->path != sm->name)
submodule_cache_remove_item(cache, sm->path, sm, true);
}
});
return error;
} }
static void submodule_update_from_index_entry( static void submodule_update_from_index_entry(
@ -855,11 +935,14 @@ static int submodule_update_head(git_submodule *submodule)
return 0; return 0;
} }
int git_submodule_reload(git_submodule *submodule)
int git_submodule_reload(git_submodule *submodule, int force)
{ {
int error = 0; int error = 0;
git_config_backend *mods; git_config_backend *mods;
GIT_UNUSED(force);
assert(submodule); assert(submodule);
/* refresh index data */ /* refresh index data */
@ -870,6 +953,10 @@ int git_submodule_reload(git_submodule *submodule)
if ((error = submodule_update_head(submodule)) < 0) if ((error = submodule_update_head(submodule)) < 0)
return error; return error;
/* done if bare */
if (git_repository_is_bare(submodule->repo))
return error;
/* refresh config data */ /* refresh config data */
mods = open_gitmodules(submodule->repo, false, NULL); mods = open_gitmodules(submodule->repo, false, NULL);
if (mods != NULL) { if (mods != NULL) {
@ -893,11 +980,9 @@ int git_submodule_reload(git_submodule *submodule)
} }
/* refresh wd data */ /* refresh wd data */
submodule->flags = submodule->flags & submodule->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS;
~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID);
return submodule_load_from_wd_lite( return submodule_load_from_wd_lite(submodule);
submodule, submodule->path, submodule->repo);
} }
static void submodule_copy_oid_maybe( static void submodule_copy_oid_maybe(
@ -1026,6 +1111,13 @@ static void submodule_release(git_submodule *sm)
if (!sm) if (!sm)
return; return;
if (sm->repo) {
git_strmap *cache = sm->repo->submodules;
submodule_cache_remove_item(cache, sm->name, sm, false);
if (sm->path != sm->name)
submodule_cache_remove_item(cache, sm->path, sm, false);
}
if (sm->path != sm->name) if (sm->path != sm->name)
git__free(sm->path); git__free(sm->path);
git__free(sm->name); git__free(sm->name);
@ -1042,17 +1134,15 @@ void git_submodule_free(git_submodule *sm)
} }
static int submodule_get( static int submodule_get(
git_submodule **sm_ptr, git_submodule **out,
git_repository *repo, git_repository *repo,
const char *name, const char *name,
const char *alternate) const char *alternate)
{ {
int error = 0;
git_strmap *smcfg = repo->submodules; git_strmap *smcfg = repo->submodules;
khiter_t pos; khiter_t pos;
git_submodule *sm; git_submodule *sm;
int error;
assert(repo && name);
pos = git_strmap_lookup_index(smcfg, name); pos = git_strmap_lookup_index(smcfg, name);
@ -1068,22 +1158,28 @@ static int submodule_get(
*/ */
pos = kh_put(str, smcfg, sm->name, &error); pos = kh_put(str, smcfg, sm->name, &error);
if (error < 0) { if (error < 0)
git_submodule_free(sm); goto done;
sm = NULL; else if (error == 0) {
} else if (error == 0) {
git_submodule_free(sm); git_submodule_free(sm);
sm = git_strmap_value_at(smcfg, pos); sm = git_strmap_value_at(smcfg, pos);
} else { } else {
error = 0;
git_strmap_set_value_at(smcfg, pos, sm); git_strmap_set_value_at(smcfg, pos, sm);
} }
} else { } else {
sm = git_strmap_value_at(smcfg, pos); sm = git_strmap_value_at(smcfg, pos);
} }
*sm_ptr = sm; done:
if (error < 0)
git_submodule_free(sm);
else if (out) {
GIT_REFCOUNT_INC(sm);
*out = sm;
}
return (sm != NULL) ? 0 : -1; return error;
} }
static int submodule_config_error(const char *property, const char *value) static int submodule_config_error(const char *property, const char *value)
@ -1143,7 +1239,7 @@ static int submodule_load_from_config(
const char *namestart, *property, *alternate = NULL; const char *namestart, *property, *alternate = NULL;
const char *key = entry->name, *value = entry->value, *path; const char *key = entry->name, *value = entry->value, *path;
git_buf name = GIT_BUF_INIT; git_buf name = GIT_BUF_INIT;
git_submodule *sm; git_submodule *sm = NULL;
int error = 0; int error = 0;
if (git__prefixcmp(key, "submodule.") != 0) if (git__prefixcmp(key, "submodule.") != 0)
@ -1230,8 +1326,8 @@ static int submodule_load_from_config(
sm->update_default = sm->update; sm->update_default = sm->update;
} }
else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) { else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
if (git_submodule_parse_recurse(&sm->fetch_recurse, value) < 0) if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0)
return -1; goto done;
sm->fetch_recurse_default = sm->fetch_recurse; sm->fetch_recurse_default = sm->fetch_recurse;
} }
else if (strcasecmp(property, "ignore") == 0) { else if (strcasecmp(property, "ignore") == 0) {
@ -1242,20 +1338,15 @@ 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;
} }
static int submodule_load_from_wd_lite( static int submodule_load_from_wd_lite(git_submodule *sm)
git_submodule *sm, const char *name, void *payload)
{ {
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
GIT_UNUSED(name); GIT_UNUSED(payload);
if (git_repository_is_bare(sm->repo))
return 0;
if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0) if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0)
return -1; return -1;
@ -1293,8 +1384,10 @@ static int load_submodule_config_from_index(
else else
sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
} else if (S_ISGITLINK(entry->mode)) { } else if (S_ISGITLINK(entry->mode)) {
if (!submodule_get(&sm, repo, entry->path, NULL)) if (!submodule_get(&sm, repo, entry->path, NULL)) {
submodule_update_from_index_entry(sm, entry); submodule_update_from_index_entry(sm, entry);
git_submodule_free(sm);
}
} else if (strcmp(entry->path, GIT_MODULES_FILE) == 0) } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
git_oid_cpy(gitmodules_oid, &entry->id); git_oid_cpy(gitmodules_oid, &entry->id);
} }
@ -1339,9 +1432,11 @@ static int load_submodule_config_from_head(
else else
sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
} else if (S_ISGITLINK(entry->mode)) { } else if (S_ISGITLINK(entry->mode)) {
if (!submodule_get(&sm, repo, entry->path, NULL)) if (!submodule_get(&sm, repo, entry->path, NULL)) {
submodule_update_from_head_data( submodule_update_from_head_data(
sm, entry->mode, &entry->id); sm, entry->mode, &entry->id);
git_submodule_free(sm);
}
} else if (strcmp(entry->path, GIT_MODULES_FILE) == 0 && } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
git_oid_iszero(gitmodules_oid)) { git_oid_iszero(gitmodules_oid)) {
git_oid_cpy(gitmodules_oid, &entry->id); git_oid_cpy(gitmodules_oid, &entry->id);
@ -1395,13 +1490,13 @@ static git_config_backend *open_gitmodules(
return mods; return mods;
} }
static int load_submodule_config(git_repository *repo) static int load_submodule_config(git_repository *repo, bool reload)
{ {
int error; int error;
git_oid gitmodules_oid; git_oid gitmodules_oid;
git_config_backend *mods = NULL; git_config_backend *mods = NULL;
if (repo->submodules) if (!reload && repo->submodules)
return 0; return 0;
memset(&gitmodules_oid, 0, sizeof(gitmodules_oid)); memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
@ -1414,6 +1509,8 @@ static int load_submodule_config(git_repository *repo)
GITERR_CHECK_ALLOC(repo->submodules); GITERR_CHECK_ALLOC(repo->submodules);
} }
/* TODO: only do the following if the sources appear modified */
/* add submodule information from index */ /* add submodule information from index */
if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0) if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0)
@ -1433,8 +1530,16 @@ static int load_submodule_config(git_repository *repo)
/* shallow scan submodules in work tree */ /* shallow scan submodules in work tree */
if (!git_repository_is_bare(repo)) if (!git_repository_is_bare(repo)) {
error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL); git_submodule *sm;
git_strmap_foreach_value(repo->submodules, sm, {
sm->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS;
});
git_strmap_foreach_value(repo->submodules, sm, {
submodule_load_from_wd_lite(sm);
});
}
cleanup: cleanup:
if (mods != NULL) if (mods != NULL)

View File

@ -111,6 +111,11 @@ enum {
GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27), GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27),
}; };
#define GIT_SUBMODULE_STATUS__ALL_WD_FLAGS \
(GIT_SUBMODULE_STATUS_IN_WD | \
GIT_SUBMODULE_STATUS__WD_OID_VALID | \
GIT_SUBMODULE_STATUS__WD_FLAGS)
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \ #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
((S) & ~(0xFFFFFFFFu << 20)) ((S) & ~(0xFFFFFFFFu << 20))
@ -128,9 +133,6 @@ extern int git_submodule_open_bare(
git_repository **repo, git_repository **repo,
git_submodule *submodule); git_submodule *submodule);
/* Release reference to submodule object - not currently for external use */
extern void git_submodule_free(git_submodule *sm);
extern int git_submodule_parse_ignore( extern int git_submodule_parse_ignore(
git_submodule_ignore_t *out, const char *value); git_submodule_ignore_t *out, const char *value);
extern int git_submodule_parse_update( extern int git_submodule_parse_update(

View File

@ -131,7 +131,7 @@ void test_diff_submodules__dirty_submodule_2(void)
g_repo = setup_fixture_submodules(); g_repo = setup_fixture_submodules();
cl_git_pass(git_submodule_reload_all(g_repo)); cl_git_pass(git_submodule_reload_all(g_repo, 1));
opts.flags = GIT_DIFF_INCLUDE_UNTRACKED | opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_SHOW_UNTRACKED_CONTENT | GIT_DIFF_SHOW_UNTRACKED_CONTENT |
@ -165,7 +165,7 @@ void test_diff_submodules__dirty_submodule_2(void)
git_diff_free(diff); git_diff_free(diff);
cl_git_pass(git_submodule_reload_all(g_repo)); cl_git_pass(git_submodule_reload_all(g_repo, 1));
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);
@ -291,7 +291,9 @@ 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);
cl_git_pass(git_submodule_reload_all(g_repo)); git_submodule_free(sm);
cl_git_pass(git_submodule_reload_all(g_repo, 1));
cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath)); cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
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));
@ -344,6 +346,8 @@ void test_diff_submodules__invalid_cache(void)
p_unlink("submod2/sm_changed_head/new_around_here"); p_unlink("submod2/sm_changed_head/new_around_here");
git_submodule_free(sm);
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);

View File

@ -19,6 +19,9 @@ void test_stash_submodules__initialize(void)
void test_stash_submodules__cleanup(void) void test_stash_submodules__cleanup(void)
{ {
git_submodule_free(sm);
sm = NULL;
git_signature_free(signature); git_signature_free(signature);
signature = NULL; signature = NULL;
} }

View File

@ -29,6 +29,7 @@ void test_status_submodules__api(void)
cl_assert(sm != NULL); cl_assert(sm != NULL);
cl_assert_equal_s("testrepo", git_submodule_name(sm)); cl_assert_equal_s("testrepo", git_submodule_name(sm));
cl_assert_equal_s("testrepo", git_submodule_path(sm)); cl_assert_equal_s("testrepo", git_submodule_path(sm));
git_submodule_free(sm);
} }
void test_status_submodules__0(void) void test_status_submodules__0(void)
@ -136,6 +137,7 @@ void test_status_submodules__moved_head(void)
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_open(&smrepo, sm)); cl_git_pass(git_submodule_open(&smrepo, sm));
git_submodule_free(sm);
/* move submodule HEAD to c47800c7266a2be04c571c04d5a6614691ea99bd */ /* move submodule HEAD to c47800c7266a2be04c571c04d5a6614691ea99bd */
cl_git_pass( cl_git_pass(

View File

@ -12,31 +12,25 @@ void test_submodule_lookup__initialize(void)
void test_submodule_lookup__simple_lookup(void) void test_submodule_lookup__simple_lookup(void)
{ {
git_submodule *sm; assert_submodule_exists(g_repo, "sm_unchanged");
/* lookup existing */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
cl_assert(sm);
/* lookup pending change in .gitmodules that is not in HEAD */ /* lookup pending change in .gitmodules that is not in HEAD */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); assert_submodule_exists(g_repo, "sm_added_and_uncommited");
cl_assert(sm);
/* lookup pending change in .gitmodules that is neither in HEAD nor index */ /* lookup pending change in .gitmodules that is not in HEAD nor index */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); assert_submodule_exists(g_repo, "sm_gitmodules_only");
cl_assert(sm);
/* lookup git repo subdir that is not added as submodule */ /* lookup git repo subdir that is not added as submodule */
cl_assert(git_submodule_lookup(&sm, g_repo, "not-submodule") == GIT_EEXISTS); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
/* lookup existing directory that is not a submodule */ /* lookup existing directory that is not a submodule */
cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_dir") == GIT_ENOTFOUND); refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
/* lookup existing file that is not a submodule */ /* lookup existing file that is not a submodule */
cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_file") == GIT_ENOTFOUND); refute_submodule_exists(g_repo, "just_a_file", GIT_ENOTFOUND);
/* lookup non-existent item */ /* lookup non-existent item */
cl_assert(git_submodule_lookup(&sm, g_repo, "no_such_file") == GIT_ENOTFOUND); refute_submodule_exists(g_repo, "no_such_file", GIT_ENOTFOUND);
} }
void test_submodule_lookup__accessors(void) void test_submodule_lookup__accessors(void)
@ -57,6 +51,9 @@ void test_submodule_lookup__accessors(void)
cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE); cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
cl_assert(git_submodule_update(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT); cl_assert(git_submodule_update(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_s("sm_changed_head", git_submodule_name(sm)); cl_assert_equal_s("sm_changed_head", git_submodule_name(sm));
@ -65,6 +62,9 @@ void test_submodule_lookup__accessors(void)
cl_assert(git_oid_streq(git_submodule_wd_id(sm), cl_assert(git_oid_streq(git_submodule_wd_id(sm),
"3d9386c507f6b093471a3e324085657a3c2b4247") == 0); "3d9386c507f6b093471a3e324085657a3c2b4247") == 0);
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm)); cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm));
@ -72,6 +72,9 @@ void test_submodule_lookup__accessors(void)
cl_assert(git_submodule_head_id(sm) == NULL); cl_assert(git_submodule_head_id(sm) == NULL);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0); cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm)); cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm));
@ -79,6 +82,8 @@ void test_submodule_lookup__accessors(void)
cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0); cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), cl_assert(git_oid_streq(git_submodule_wd_id(sm),
"5e4963595a9774b90524d35a807169049de8ccad") == 0); "5e4963595a9774b90524d35a807169049de8ccad") == 0);
git_submodule_free(sm);
} }
typedef struct { typedef struct {
@ -104,69 +109,35 @@ void test_submodule_lookup__foreach(void)
void test_submodule_lookup__lookup_even_with_unborn_head(void) void test_submodule_lookup__lookup_even_with_unborn_head(void)
{ {
git_reference *head; git_reference *head;
git_submodule *sm;
/* put us on an unborn branch */ /* put us on an unborn branch */
cl_git_pass(git_reference_symbolic_create( cl_git_pass(git_reference_symbolic_create(
&head, g_repo, "HEAD", "refs/heads/garbage", 1, NULL, NULL)); &head, g_repo, "HEAD", "refs/heads/garbage", 1, NULL, NULL));
git_reference_free(head); git_reference_free(head);
/* lookup existing */ assert_submodule_exists(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); assert_submodule_exists(g_repo, "sm_added_and_uncommited");
cl_assert(sm); assert_submodule_exists(g_repo, "sm_gitmodules_only");
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
/* lookup pending change in .gitmodules that is not in HEAD */ refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); refute_submodule_exists(g_repo, "just_a_file", GIT_ENOTFOUND);
cl_assert(sm); refute_submodule_exists(g_repo, "no_such_file", GIT_ENOTFOUND);
/* lookup pending change in .gitmodules that is neither in HEAD nor index */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
cl_assert(sm);
/* lookup git repo subdir that is not added as submodule */
cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule"));
/* lookup existing directory that is not a submodule */
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir"));
/* lookup existing file that is not a submodule */
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file"));
/* lookup non-existent item */
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file"));
} }
void test_submodule_lookup__lookup_even_with_missing_index(void) void test_submodule_lookup__lookup_even_with_missing_index(void)
{ {
git_index *idx; git_index *idx;
git_submodule *sm;
/* give the repo an empty index */ /* give the repo an empty index */
cl_git_pass(git_index_new(&idx)); cl_git_pass(git_index_new(&idx));
git_repository_set_index(g_repo, idx); git_repository_set_index(g_repo, idx);
git_index_free(idx); git_index_free(idx);
/* lookup existing */ assert_submodule_exists(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); assert_submodule_exists(g_repo, "sm_added_and_uncommited");
cl_assert(sm); assert_submodule_exists(g_repo, "sm_gitmodules_only");
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
/* lookup pending change in .gitmodules that is not in HEAD */ refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); refute_submodule_exists(g_repo, "just_a_file", GIT_ENOTFOUND);
cl_assert(sm); refute_submodule_exists(g_repo, "no_such_file", GIT_ENOTFOUND);
/* lookup pending change in .gitmodules that is neither in HEAD nor index */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
cl_assert(sm);
/* lookup git repo subdir that is not added as submodule */
cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule"));
/* lookup existing directory that is not a submodule */
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir"));
/* lookup existing file that is not a submodule */
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file"));
/* lookup non-existent item */
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file"));
} }

View File

@ -21,15 +21,16 @@ void test_submodule_modify__add(void)
const char *s; const char *s;
/* re-add existing submodule */ /* re-add existing submodule */
cl_assert( cl_assert_equal_i(
git_submodule_add_setup(NULL, g_repo, "whatever", "sm_unchanged", 1) == GIT_EEXISTS,
GIT_EEXISTS ); git_submodule_add_setup(NULL, g_repo, "whatever", "sm_unchanged", 1));
/* add a submodule using a gitlink */ /* add a submodule using a gitlink */
cl_git_pass( cl_git_pass(
git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2, 1) git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2, 1)
); );
git_submodule_free(sm);
cl_assert(git_path_isfile("submod2/" SM_LIBGIT2 "/.git")); cl_assert(git_path_isfile("submod2/" SM_LIBGIT2 "/.git"));
@ -48,6 +49,7 @@ void test_submodule_modify__add(void)
cl_git_pass( cl_git_pass(
git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2B, 0) git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2B, 0)
); );
git_submodule_free(sm);
cl_assert(git_path_isdir("submod2/" SM_LIBGIT2B "/.git")); cl_assert(git_path_isdir("submod2/" SM_LIBGIT2B "/.git"));
cl_assert(git_path_isfile("submod2/" SM_LIBGIT2B "/.git/HEAD")); cl_assert(git_path_isfile("submod2/" SM_LIBGIT2B "/.git/HEAD"));
@ -95,7 +97,7 @@ void test_submodule_modify__init(void)
/* 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); git_submodule_reload_all(g_repo, 1);
/* confirm submodule data in config */ /* confirm submodule data in config */
cl_git_pass(git_repository_config(&cfg, g_repo)); cl_git_pass(git_repository_config(&cfg, g_repo));
@ -159,6 +161,10 @@ void test_submodule_modify__sync(void)
cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url")); cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url"));
cl_assert_equal_s(git_submodule_url(sm3), str); cl_assert_equal_s(git_submodule_url(sm3), str);
git_config_free(cfg); git_config_free(cfg);
git_submodule_free(sm1);
git_submodule_free(sm2);
git_submodule_free(sm3);
} }
void test_submodule_modify__edit_and_save(void) void test_submodule_modify__edit_and_save(void)
@ -231,7 +237,7 @@ void test_submodule_modify__edit_and_save(void)
cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(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 */ /* call reload and check that the new values are loaded */
cl_git_pass(git_submodule_reload(sm1)); 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_URL, git_submodule_url(sm1));
cl_assert_equal_i( cl_assert_equal_i(
@ -253,16 +259,18 @@ void test_submodule_modify__edit_and_save(void)
GIT_SUBMODULE_RECURSE_NO, git_submodule_fetch_recurse_submodules(sm2)); GIT_SUBMODULE_RECURSE_NO, git_submodule_fetch_recurse_submodules(sm2));
/* set fetchRecurseSubmodules on-demand */ /* set fetchRecurseSubmodules on-demand */
cl_git_pass(git_submodule_reload(sm1)); cl_git_pass(git_submodule_reload(sm1, 0));
git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_ONDEMAND); git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_ONDEMAND);
cl_assert_equal_i( cl_assert_equal_i(
GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1)); GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1));
/* call save */ /* call save */
cl_git_pass(git_submodule_save(sm1)); cl_git_pass(git_submodule_save(sm1));
cl_git_pass(git_submodule_reload(sm1)); cl_git_pass(git_submodule_reload(sm1, 0));
cl_assert_equal_i( cl_assert_equal_i(
GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1)); GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1));
git_submodule_free(sm1);
git_submodule_free(sm2);
git_repository_free(r2); git_repository_free(r2);
git__free(old_url); git__free(old_url);
} }

View File

@ -18,19 +18,43 @@ void test_submodule_status__cleanup(void)
void test_submodule_status__unchanged(void) void test_submodule_status__unchanged(void)
{ {
unsigned int status, expected; unsigned int status = get_submodule_status(g_repo, "sm_unchanged");
git_submodule *sm; unsigned int expected =
GIT_SUBMODULE_STATUS_IN_HEAD |
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
expected = 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 |
GIT_SUBMODULE_STATUS_IN_WD; GIT_SUBMODULE_STATUS_IN_WD;
cl_assert(status == expected); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_assert(expected == status);
}
static void rm_submodule(const char *name)
{
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), name));
cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES));
git_buf_free(&path);
}
static void add_submodule_to_index(const char *name)
{
git_submodule *sm;
cl_git_pass(git_submodule_lookup(&sm, g_repo, name));
cl_git_pass(git_submodule_add_to_index(sm, true));
git_submodule_free(sm);
}
static void rm_submodule_from_index(const char *name)
{
git_index *index;
size_t pos;
cl_git_pass(git_repository_index(&index, g_repo));
cl_assert(!git_index_find(&pos, index, name));
cl_git_pass(git_index_remove(index, name, 0));
cl_git_pass(git_index_write(index));
git_index_free(index);
} }
/* 4 values of GIT_SUBMODULE_IGNORE to check */ /* 4 values of GIT_SUBMODULE_IGNORE to check */
@ -38,81 +62,49 @@ void test_submodule_status__unchanged(void)
void test_submodule_status__ignore_none(void) void test_submodule_status__ignore_none(void)
{ {
unsigned int status; unsigned int status;
git_submodule *sm;
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); rm_submodule("sm_unchanged");
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert_equal_i(GIT_ENOTFOUND, refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
git_submodule_lookup(&sm, g_repo, "just_a_dir")); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
cl_assert_equal_i(GIT_EEXISTS, refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
git_submodule_lookup(&sm, g_repo, "not-submodule"));
cl_assert_equal_i(GIT_EEXISTS,
git_submodule_lookup(&sm, g_repo, "not"));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); status = get_submodule_status(g_repo, "sm_changed_index");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); status = get_submodule_status(g_repo, "sm_changed_file");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); status = get_submodule_status(g_repo, "sm_changed_untracked_file");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNTRACKED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNTRACKED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); status = get_submodule_status(g_repo, "sm_missing_commits");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); status = get_submodule_status(g_repo, "sm_added_and_uncommited");
cl_git_pass(git_submodule_status(&status, sm));
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 */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, sm));
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(git_buf_cstr(&path), NULL, 0755, 0)); cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_reload(sm));
cl_git_pass(git_submodule_status(&status, sm));
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 */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); add_submodule_to_index("sm_changed_head");
cl_git_pass(git_submodule_add_to_index(sm, true)); status = get_submodule_status(g_repo, "sm_changed_head");
/* reload is not needed because add_to_index updates the submodule data */
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
/* remove sm_changed_head from index */ /* remove sm_changed_head from index */
{ rm_submodule_from_index("sm_changed_head");
git_index *index; status = get_submodule_status(g_repo, "sm_changed_head");
size_t pos;
cl_git_pass(git_repository_index(&index, g_repo));
cl_assert(!git_index_find(&pos, index, "sm_changed_head"));
cl_git_pass(git_index_remove(index, "sm_changed_head", 0));
cl_git_pass(git_index_write(index));
git_index_free(index);
}
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_git_pass(git_submodule_reload(sm));
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0);
git_buf_free(&path);
} }
static int set_sm_ignore(git_submodule *sm, const char *name, void *payload) static int set_sm_ignore(git_submodule *sm, const char *name, void *payload)
@ -126,191 +118,136 @@ static int set_sm_ignore(git_submodule *sm, const char *name, void *payload)
void test_submodule_status__ignore_untracked(void) void test_submodule_status__ignore_untracked(void)
{ {
unsigned int status; unsigned int status;
git_submodule *sm;
git_buf path = GIT_BUF_INIT;
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); rm_submodule("sm_unchanged");
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
cl_git_fail(git_submodule_lookup(&sm, g_repo, "not-submodule")); refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); status = get_submodule_status(g_repo, "sm_changed_index");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); status = get_submodule_status(g_repo, "sm_changed_file");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); status = get_submodule_status(g_repo, "sm_changed_untracked_file");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); status = get_submodule_status(g_repo, "sm_missing_commits");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); status = get_submodule_status(g_repo, "sm_added_and_uncommited");
cl_git_pass(git_submodule_status(&status, sm));
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 */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, sm));
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(git_buf_cstr(&path), NULL, 0755, 0)); cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_reload(sm));
cl_git_pass(git_submodule_status(&status, sm));
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 */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); add_submodule_to_index("sm_changed_head");
cl_git_pass(git_submodule_add_to_index(sm, true)); status = get_submodule_status(g_repo, "sm_changed_head");
/* reload is not needed because add_to_index updates the submodule data */
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
git_buf_free(&path);
} }
void test_submodule_status__ignore_dirty(void) void test_submodule_status__ignore_dirty(void)
{ {
unsigned int status; unsigned int status;
git_submodule *sm;
git_buf path = GIT_BUF_INIT;
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); rm_submodule("sm_unchanged");
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
cl_assert_equal_i(GIT_ENOTFOUND, refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
git_submodule_lookup(&sm, g_repo, "just_a_dir")); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
cl_assert_equal_i(GIT_EEXISTS, refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
git_submodule_lookup(&sm, g_repo, "not-submodule"));
cl_assert_equal_i(GIT_EEXISTS,
git_submodule_lookup(&sm, g_repo, "not"));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); status = get_submodule_status(g_repo, "sm_changed_index");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); status = get_submodule_status(g_repo, "sm_changed_file");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); status = get_submodule_status(g_repo, "sm_changed_untracked_file");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); status = get_submodule_status(g_repo, "sm_missing_commits");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); status = get_submodule_status(g_repo, "sm_added_and_uncommited");
cl_git_pass(git_submodule_status(&status, sm));
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 */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, sm));
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(git_buf_cstr(&path), NULL, 0755, 0)); cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_reload(sm));
cl_git_pass(git_submodule_status(&status, sm));
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 */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); add_submodule_to_index("sm_changed_head");
cl_git_pass(git_submodule_add_to_index(sm, true)); status = get_submodule_status(g_repo, "sm_changed_head");
/* reload is not needed because add_to_index updates the submodule data */
cl_git_pass(git_submodule_status(&status, sm));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
git_buf_free(&path);
} }
void test_submodule_status__ignore_all(void) void test_submodule_status__ignore_all(void)
{ {
unsigned int status; unsigned int status;
git_submodule *sm;
git_buf path = GIT_BUF_INIT;
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); rm_submodule("sm_unchanged");
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
cl_assert_equal_i(GIT_ENOTFOUND, refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
git_submodule_lookup(&sm, g_repo, "just_a_dir")); refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
cl_assert_equal_i(GIT_EEXISTS, refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
git_submodule_lookup(&sm, g_repo, "not-submodule"));
cl_assert_equal_i(GIT_EEXISTS,
git_submodule_lookup(&sm, g_repo, "not"));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); status = get_submodule_status(g_repo, "sm_changed_index");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); status = get_submodule_status(g_repo, "sm_changed_file");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); status = get_submodule_status(g_repo, "sm_changed_untracked_file");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); status = get_submodule_status(g_repo, "sm_missing_commits");
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); status = get_submodule_status(g_repo, "sm_added_and_uncommited");
cl_git_pass(git_submodule_status(&status, sm));
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 */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, sm));
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(git_buf_cstr(&path), NULL, 0755, 0)); cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_reload(sm));
cl_git_pass(git_submodule_status(&status, sm));
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 */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); add_submodule_to_index("sm_changed_head");
cl_git_pass(git_submodule_add_to_index(sm, true)); status = get_submodule_status(g_repo, "sm_changed_head");
/* reload is not needed because add_to_index updates the submodule data */
cl_git_pass(git_submodule_status(&status, sm));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
git_buf_free(&path);
} }
typedef struct { typedef struct {
@ -401,29 +338,23 @@ void test_submodule_status__iterator(void)
void test_submodule_status__untracked_dirs_containing_ignored_files(void) void test_submodule_status__untracked_dirs_containing_ignored_files(void)
{ {
git_buf path = GIT_BUF_INIT;
unsigned int status, expected; unsigned int status, expected;
git_submodule *sm;
cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), "modules/sm_unchanged/info/exclude")); cl_git_append2file(
cl_git_append2file(git_buf_cstr(&path), "\n*.ignored\n"); "submod2/.git/modules/sm_unchanged/info/exclude", "\n*.ignored\n");
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged/directory")); cl_git_pass(
cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0)); git_futils_mkdir("sm_unchanged/directory", "submod2", 0755, 0));
cl_git_pass(git_buf_joinpath(&path, git_buf_cstr(&path), "i_am.ignored")); cl_git_mkfile(
cl_git_mkfile(git_buf_cstr(&path), "ignored this file, please\n"); "submod2/sm_unchanged/directory/i_am.ignored",
"ignore this file, please\n");
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
cl_git_pass(git_submodule_status(&status, sm));
status = get_submodule_status(g_repo, "sm_unchanged");
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
expected = GIT_SUBMODULE_STATUS_IN_HEAD | expected = 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 |
GIT_SUBMODULE_STATUS_IN_WD; GIT_SUBMODULE_STATUS_IN_WD;
cl_assert(status == expected); cl_assert(status == expected);
git_buf_free(&path);
} }

View File

@ -125,3 +125,32 @@ git_repository *setup_fixture_submod2(void)
return repo; return repo;
} }
void assert_submodule_exists(git_repository *repo, const char *name)
{
git_submodule *sm;
cl_git_pass(git_submodule_lookup(&sm, repo, name));
cl_assert(sm);
git_submodule_free(sm);
}
void refute_submodule_exists(
git_repository *repo, const char *name, int expected_error)
{
git_submodule *sm;
cl_assert_equal_i(
expected_error, git_submodule_lookup(&sm, repo, name));
}
unsigned int get_submodule_status(git_repository *repo, const char *name)
{
git_submodule *sm = NULL;
unsigned int status = 0;
cl_git_pass(git_submodule_lookup(&sm, repo, name));
cl_assert(sm);
cl_git_pass(git_submodule_status(&status, sm));
git_submodule_free(sm);
return status;
}

View File

@ -3,3 +3,8 @@ extern void rewrite_gitmodules(const char *workdir);
/* these will automatically set a cleanup callback */ /* these will automatically set a cleanup callback */
extern git_repository *setup_fixture_submodules(void); extern git_repository *setup_fixture_submodules(void);
extern git_repository *setup_fixture_submod2(void); extern git_repository *setup_fixture_submod2(void);
extern unsigned int get_submodule_status(git_repository *, const char *);
extern void assert_submodule_exists(git_repository *, const char *);
extern void refute_submodule_exists(git_repository *, const char *, int err);