summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVicent Marti <vicent@github.com>2014-03-26 18:29:34 +0100
committerVicent Marti <vicent@github.com>2014-03-26 18:29:34 +0100
commitdc7efa1aef2d3694e7a1955d746d26013402a21d (patch)
tree53fe478631e02e372508d981093c2abace29ce42 /src
parent77b699e0da3d4a4fed742893bd172f3ee7b936d5 (diff)
parent591e82952a2835c3d411ee5abec78be3b0816861 (diff)
downloadlibgit2-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.c40
-rw-r--r--src/diff.c31
-rw-r--r--src/diff_file.c17
-rw-r--r--src/submodule.c219
-rw-r--r--src/submodule.h8
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(