diff options
author | Vicent Marti <vicent@github.com> | 2014-03-26 18:29:34 +0100 |
---|---|---|
committer | Vicent Marti <vicent@github.com> | 2014-03-26 18:29:34 +0100 |
commit | dc7efa1aef2d3694e7a1955d746d26013402a21d (patch) | |
tree | 53fe478631e02e372508d981093c2abace29ce42 /src | |
parent | 77b699e0da3d4a4fed742893bd172f3ee7b936d5 (diff) | |
parent | 591e82952a2835c3d411ee5abec78be3b0816861 (diff) | |
download | libgit2-dc7efa1aef2d3694e7a1955d746d26013402a21d.tar.gz |
Merge pull request #2204 from libgit2/rb/submodule-reference-counting
Make submodules externally refcounted
Diffstat (limited to 'src')
-rw-r--r-- | src/checkout.c | 40 | ||||
-rw-r--r-- | src/diff.c | 31 | ||||
-rw-r--r-- | src/diff_file.c | 17 | ||||
-rw-r--r-- | src/submodule.c | 219 | ||||
-rw-r--r-- | src/submodule.h | 8 |
5 files changed, 220 insertions, 95 deletions
diff --git a/src/checkout.c b/src/checkout.c index f882f3593..468c8dc6e 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -147,19 +147,23 @@ static bool checkout_is_workdir_modified( git_submodule *sm; unsigned int sm_status = 0; const git_oid *sm_oid = NULL; + bool rval = false; - if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0 || - git_submodule_status(&sm_status, sm) < 0) - return true; - - if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) + if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0) { + giterr_clear(); return true; + } - sm_oid = git_submodule_wd_id(sm); - if (!sm_oid) - return false; + if (git_submodule_status(&sm_status, sm) < 0 || + 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); - return (git_oid__cmp(&baseitem->id, sm_oid) != 0); + git_submodule_free(sm); + return rval; } /* 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; unsigned int sm_loc = 0; + bool rval = false; - if (git_submodule_lookup(&sm, data->repo, path) < 0 || - git_submodule_location(&sm_loc, sm) < 0 || - sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG) + if (git_submodule_lookup(&sm, data->repo, path) < 0) 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; } @@ -1258,7 +1267,6 @@ static int checkout_submodule( const git_diff_file *file) { int error = 0; - git_submodule *sm; /* Until submodules are supported, UPDATE_ONLY means do nothing here */ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) @@ -1269,7 +1277,7 @@ static int checkout_submodule( data->opts.dir_mode, GIT_MKDIR_PATH)) < 0) 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 * 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 */ if (data->reload_submodules && - (error = git_submodule_reload_all(data->repo)) < 0) + (error = git_submodule_reload_all(data->repo, 1)) < 0) return error; git_vector_foreach(&data->diff->deltas, i, delta) { @@ -1534,7 +1542,7 @@ static int checkout_create_submodules( } /* 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) diff --git a/src/diff.c b/src/diff.c index dc7735f4f..25c5937e6 100644 --- a/src/diff.c +++ b/src/diff.c @@ -528,12 +528,15 @@ int git_diff__oid_for_file( /* calculate OID for file if possible */ if (S_ISGITLINK(mode)) { git_submodule *sm; - const git_oid *sm_oid; - if (!git_submodule_lookup(&sm, repo, path) && - (sm_oid = git_submodule_wd_id(sm)) != NULL) - git_oid_cpy(oid, sm_oid); - else { + memset(oid, 0, sizeof(*oid)); + + if (!git_submodule_lookup(&sm, repo, path)) { + 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 * 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) - return 0; - - if ((error = git_submodule__status( + /* ignore it */; + else if ((error = git_submodule__status( &sm_status, NULL, NULL, found_oid, sub, ign)) < 0) - return error; + /* return error below */; /* check IS_WD_UNMODIFIED because this case is only used * 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; /* 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)) *status = GIT_DELTA_MODIFIED; - return 0; + git_submodule_free(sub); + return error; } static int maybe_modified( @@ -960,10 +963,8 @@ static int handle_unmatched_new_item( delta_type = GIT_DELTA_ADDED; else if (nitem->mode == GIT_FILEMODE_COMMIT) { - git_submodule *sm; - /* 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(); delta_type = GIT_DELTA_IGNORED; } diff --git a/src/diff_file.c b/src/diff_file.c index 7dabf8d6f..b9f92df3f 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -177,11 +177,17 @@ static int diff_file_content_commit_to_str( unsigned int sm_status = 0; const git_oid *sm_head; - if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0 || - (error = git_submodule_status(&sm_status, sm)) < 0) { + if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0) { /* GIT_EEXISTS means a "submodule" that has not been git added */ - if (error == GIT_EEXISTS) + if (error == GIT_EEXISTS) { + giterr_clear(); error = 0; + } + return error; + } + + if ((error = git_submodule_status(&sm_status, sm)) < 0) { + git_submodule_free(sm); return error; } @@ -196,6 +202,8 @@ static int diff_file_content_commit_to_str( if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) status = "-dirty"; + + git_submodule_free(sm); } 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); - git_buf_free(&raw); + if (out.ptr != raw.ptr) + git_buf_free(&raw); if (!error) { fc->map.len = out.size; diff --git a/src/submodule.c b/src/submodule.c index 9eaf77dae..769b092a0 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -77,12 +77,12 @@ __KHASH_IMPL( str, static kh_inline, const char *, void *, 1, 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 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_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 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); @@ -99,27 +99,55 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix) return git_buf_puts(key, suffix); } +/* lookup submodule or return ENOTFOUND if it doesn't exist */ +static int submodule_lookup( + git_submodule **out, + git_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 */ 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, - const char *name) /* trailing slash is allowed */ + const char *name) /* trailing slash is allowed */ { int error; - khiter_t pos; assert(repo && name); - if ((error = load_submodule_config(repo)) < 0) + if ((error = load_submodule_config(repo, false)) < 0) return error; - pos = git_strmap_lookup_index(repo->submodules, name); - - if (!git_strmap_valid_index(repo->submodules, pos)) { - error = GIT_ENOTFOUND; + if ((error = submodule_lookup(out, repo->submodules, name, NULL)) < 0) { /* check if a plausible submodule exists at path */ if (git_repository_workdir(repo)) { @@ -137,14 +165,9 @@ int git_submodule_lookup( giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? "No submodule named '%s'" : "Submodule '%s' has not been added yet", name); - - return error; } - if (sm_ptr) - *sm_ptr = git_strmap_value_at(repo->submodules, pos); - - return 0; + return error; } int git_submodule_foreach( @@ -159,7 +182,7 @@ int git_submodule_foreach( assert(repo && callback); - if ((error = load_submodule_config(repo)) < 0) + if ((error = load_submodule_config(repo, true)) < 0) return error; git_strmap_foreach_value(repo->submodules, sm, { @@ -198,12 +221,15 @@ void git_submodule_config_free(git_repository *repo) if (smcfg == NULL) 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); } int git_submodule_add_setup( - git_submodule **submodule, + git_submodule **out, git_repository *repo, const char *url, const char *path, @@ -211,7 +237,7 @@ int git_submodule_add_setup( { int error = 0; git_config_backend *mods = NULL; - git_submodule *sm; + git_submodule *sm = NULL; git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT; git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; git_repository *subrepo = NULL; @@ -223,6 +249,7 @@ int git_submodule_add_setup( if (git_submodule_lookup(&sm, repo, path) < 0) giterr_clear(); else { + git_submodule_free(sm); giterr_set(GITERR_SUBMODULE, "Attempt to add a submodule that already exists"); return GIT_EEXISTS; @@ -307,12 +334,16 @@ int git_submodule_add_setup( /* add submodule to hash and "reload" it */ 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); cleanup: - if (submodule != NULL) - *submodule = !error ? sm : NULL; + if (error && sm) { + git_submodule_free(sm); + sm = NULL; + } + if (out != NULL) + *out = sm; if (mods != NULL) 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); } -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); - 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( @@ -855,11 +935,14 @@ static int submodule_update_head(git_submodule *submodule) return 0; } -int git_submodule_reload(git_submodule *submodule) + +int git_submodule_reload(git_submodule *submodule, int force) { int error = 0; git_config_backend *mods; + GIT_UNUSED(force); + assert(submodule); /* refresh index data */ @@ -870,6 +953,10 @@ int git_submodule_reload(git_submodule *submodule) if ((error = submodule_update_head(submodule)) < 0) return error; + /* done if bare */ + if (git_repository_is_bare(submodule->repo)) + return error; + /* refresh config data */ mods = open_gitmodules(submodule->repo, false, NULL); if (mods != NULL) { @@ -893,11 +980,9 @@ int git_submodule_reload(git_submodule *submodule) } /* refresh wd data */ - submodule->flags = submodule->flags & - ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID); + submodule->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; - return submodule_load_from_wd_lite( - submodule, submodule->path, submodule->repo); + return submodule_load_from_wd_lite(submodule); } static void submodule_copy_oid_maybe( @@ -1026,6 +1111,13 @@ static void submodule_release(git_submodule *sm) if (!sm) 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) git__free(sm->path); git__free(sm->name); @@ -1042,17 +1134,15 @@ void git_submodule_free(git_submodule *sm) } static int submodule_get( - git_submodule **sm_ptr, + git_submodule **out, git_repository *repo, const char *name, const char *alternate) { + int error = 0; git_strmap *smcfg = repo->submodules; khiter_t pos; git_submodule *sm; - int error; - - assert(repo && name); pos = git_strmap_lookup_index(smcfg, name); @@ -1068,22 +1158,28 @@ static int submodule_get( */ pos = kh_put(str, smcfg, sm->name, &error); - if (error < 0) { - git_submodule_free(sm); - sm = NULL; - } else if (error == 0) { + if (error < 0) + goto done; + else if (error == 0) { git_submodule_free(sm); sm = git_strmap_value_at(smcfg, pos); } else { + error = 0; git_strmap_set_value_at(smcfg, pos, sm); } } else { 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) @@ -1143,7 +1239,7 @@ static int submodule_load_from_config( const char *namestart, *property, *alternate = NULL; const char *key = entry->name, *value = entry->value, *path; git_buf name = GIT_BUF_INIT; - git_submodule *sm; + git_submodule *sm = NULL; int error = 0; if (git__prefixcmp(key, "submodule.") != 0) @@ -1230,8 +1326,8 @@ static int submodule_load_from_config( sm->update_default = sm->update; } else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) { - if (git_submodule_parse_recurse(&sm->fetch_recurse, value) < 0) - return -1; + if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0) + goto done; sm->fetch_recurse_default = sm->fetch_recurse; } else if (strcasecmp(property, "ignore") == 0) { @@ -1242,20 +1338,15 @@ static int submodule_load_from_config( /* ignore other unknown submodule properties */ done: + git_submodule_free(sm); /* offset refcount inc from submodule_get() */ git_buf_free(&name); return error; } -static int submodule_load_from_wd_lite( - git_submodule *sm, const char *name, void *payload) +static int submodule_load_from_wd_lite(git_submodule *sm) { 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) return -1; @@ -1293,8 +1384,10 @@ static int load_submodule_config_from_index( else sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; } 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); + git_submodule_free(sm); + } } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0) git_oid_cpy(gitmodules_oid, &entry->id); } @@ -1339,9 +1432,11 @@ static int load_submodule_config_from_head( else sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; } 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( sm, entry->mode, &entry->id); + git_submodule_free(sm); + } } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0 && git_oid_iszero(gitmodules_oid)) { git_oid_cpy(gitmodules_oid, &entry->id); @@ -1395,13 +1490,13 @@ static git_config_backend *open_gitmodules( return mods; } -static int load_submodule_config(git_repository *repo) +static int load_submodule_config(git_repository *repo, bool reload) { int error; git_oid gitmodules_oid; git_config_backend *mods = NULL; - if (repo->submodules) + if (!reload && repo->submodules) return 0; memset(&gitmodules_oid, 0, sizeof(gitmodules_oid)); @@ -1414,6 +1509,8 @@ static int load_submodule_config(git_repository *repo) GITERR_CHECK_ALLOC(repo->submodules); } + /* TODO: only do the following if the sources appear modified */ + /* add submodule information from index */ 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 */ - if (!git_repository_is_bare(repo)) - error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL); + if (!git_repository_is_bare(repo)) { + 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: if (mods != NULL) diff --git a/src/submodule.h b/src/submodule.h index 5e532e1ae..053cb61e0 100644 --- a/src/submodule.h +++ b/src/submodule.h @@ -111,6 +111,11 @@ enum { 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) \ ((S) & ~(0xFFFFFFFFu << 20)) @@ -128,9 +133,6 @@ extern int git_submodule_open_bare( git_repository **repo, 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( git_submodule_ignore_t *out, const char *value); extern int git_submodule_parse_update( |