Commit 69b6ffc4 by Russell Belfer

Make a real submodule cache object

This takes the old submodule cache which was just a git_strmap
and makes a real git_submodule_cache object that can contain other
things like a lock and timestamp-ish data to control refreshing of
submodule info.
parent d543d59c
...@@ -93,6 +93,7 @@ void git_repository__cleanup(git_repository *repo) ...@@ -93,6 +93,7 @@ void git_repository__cleanup(git_repository *repo)
git_cache_clear(&repo->objects); git_cache_clear(&repo->objects);
git_attr_cache_flush(repo); git_attr_cache_flush(repo);
git_submodule_cache_free(repo);
set_config(repo, NULL); set_config(repo, NULL);
set_index(repo, NULL); set_index(repo, NULL);
...@@ -108,7 +109,6 @@ void git_repository_free(git_repository *repo) ...@@ -108,7 +109,6 @@ void git_repository_free(git_repository *repo)
git_repository__cleanup(repo); git_repository__cleanup(repo);
git_cache_free(&repo->objects); git_cache_free(&repo->objects);
git_submodule_config_free(repo);
git_diff_driver_registry_free(repo->diff_drivers); git_diff_driver_registry_free(repo->diff_drivers);
repo->diff_drivers = NULL; repo->diff_drivers = NULL;
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
#include "buffer.h" #include "buffer.h"
#include "object.h" #include "object.h"
#include "attrcache.h" #include "attrcache.h"
#include "strmap.h" #include "submodule.h"
#include "diff_driver.h" #include "diff_driver.h"
#define DOT_GIT ".git" #define DOT_GIT ".git"
...@@ -105,10 +105,10 @@ struct git_repository { ...@@ -105,10 +105,10 @@ struct git_repository {
git_refdb *_refdb; git_refdb *_refdb;
git_config *_config; git_config *_config;
git_index *_index; git_index *_index;
git_submodule_cache *_submodules;
git_cache objects; git_cache objects;
git_attr_cache attrcache; git_attr_cache attrcache;
git_strmap *submodules;
git_diff_driver_registry *diff_drivers; git_diff_driver_registry *diff_drivers;
char *path_repository; char *path_repository;
...@@ -149,11 +149,6 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo); ...@@ -149,11 +149,6 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo);
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar); int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);
void git_repository__cvar_cache_clear(git_repository *repo); void git_repository__cvar_cache_clear(git_repository *repo);
/*
* Submodule cache
*/
extern void git_submodule_config_free(git_repository *repo);
GIT_INLINE(int) git_repository__ensure_not_bare( GIT_INLINE(int) git_repository__ensure_not_bare(
git_repository *repo, git_repository *repo,
const char *operation_name) const char *operation_name)
......
...@@ -77,11 +77,13 @@ __KHASH_IMPL( ...@@ -77,11 +77,13 @@ __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, bool reload); static int submodule_cache_init(git_repository *repo, bool refresh);
static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *); static void submodule_cache_free(git_submodule_cache *cache);
static git_config_backend *open_gitmodules(git_submodule_cache *, bool);
static int lookup_head_remote_key(git_buf *key, git_repository *repo); static int lookup_head_remote_key(git_buf *key, git_repository *repo);
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_submodule_cache *, const char *, const char *);
static int submodule_load_from_config(const git_config_entry *, void *); static int submodule_load_from_config(const git_config_entry *, void *);
static int submodule_load_from_wd_lite(git_submodule *); static int submodule_load_from_wd_lite(git_submodule *);
static void submodule_get_index_status(unsigned int *, git_submodule *); static void submodule_get_index_status(unsigned int *, git_submodule *);
...@@ -102,7 +104,7 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix) ...@@ -102,7 +104,7 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
/* lookup submodule or return ENOTFOUND if it doesn't exist */ /* lookup submodule or return ENOTFOUND if it doesn't exist */
static int submodule_lookup( static int submodule_lookup(
git_submodule **out, git_submodule **out,
git_strmap *cache, git_submodule_cache *cache,
const char *name, const char *name,
const char *alternate) const char *alternate)
{ {
...@@ -110,18 +112,18 @@ static int submodule_lookup( ...@@ -110,18 +112,18 @@ static int submodule_lookup(
/* lock cache */ /* lock cache */
pos = git_strmap_lookup_index(cache, name); pos = git_strmap_lookup_index(cache->submodules, name);
if (!git_strmap_valid_index(cache, pos) && alternate) if (!git_strmap_valid_index(cache->submodules, pos) && alternate)
pos = git_strmap_lookup_index(cache, alternate); pos = git_strmap_lookup_index(cache->submodules, alternate);
if (!git_strmap_valid_index(cache, pos)) { if (!git_strmap_valid_index(cache->submodules, pos)) {
/* unlock cache */ /* unlock cache */
return GIT_ENOTFOUND; /* don't set error - caller will cope */ return GIT_ENOTFOUND; /* don't set error - caller will cope */
} }
if (out != NULL) { if (out != NULL) {
git_submodule *sm = git_strmap_value_at(cache, pos); git_submodule *sm = git_strmap_value_at(cache->submodules, pos);
GIT_REFCOUNT_INC(sm); GIT_REFCOUNT_INC(sm);
*out = sm; *out = sm;
} }
...@@ -139,17 +141,45 @@ bool git_submodule__is_submodule(git_repository *repo, const char *name) ...@@ -139,17 +141,45 @@ bool git_submodule__is_submodule(git_repository *repo, const char *name)
{ {
git_strmap *map; git_strmap *map;
if (load_submodule_config(repo, false) < 0) { if (submodule_cache_init(repo, false) < 0) {
giterr_clear(); giterr_clear();
return false; return false;
} }
if (!(map = repo->submodules)) if (!repo->_submodules || !(map = repo->_submodules->submodules))
return false; return false;
return git_strmap_valid_index(map, git_strmap_lookup_index(map, name)); return git_strmap_valid_index(map, git_strmap_lookup_index(map, name));
} }
static void submodule_set_lookup_error(int error, const char *name)
{
if (!error)
return;
giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ?
"No submodule named '%s'" :
"Submodule '%s' has not been added yet", name);
}
int git_submodule__lookup(
git_submodule **out, /* NULL if user only wants to test existence */
git_repository *repo,
const char *name) /* trailing slash is allowed */
{
int error;
assert(repo && name);
if ((error = submodule_cache_init(repo, false)) < 0)
return error;
if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0)
submodule_set_lookup_error(error, name);
return error;
}
int git_submodule_lookup( int git_submodule_lookup(
git_submodule **out, /* NULL if user only wants to test existence */ git_submodule **out, /* NULL if user only wants to test existence */
git_repository *repo, git_repository *repo,
...@@ -159,10 +189,10 @@ int git_submodule_lookup( ...@@ -159,10 +189,10 @@ int git_submodule_lookup(
assert(repo && name); assert(repo && name);
if ((error = load_submodule_config(repo, false)) < 0) if ((error = submodule_cache_init(repo, true)) < 0)
return error; return error;
if ((error = submodule_lookup(out, repo->submodules, name, NULL)) < 0) { if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) {
/* 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)) {
...@@ -178,9 +208,7 @@ int git_submodule_lookup( ...@@ -178,9 +208,7 @@ int git_submodule_lookup(
git_buf_free(&path); git_buf_free(&path);
} }
giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? submodule_set_lookup_error(error, name);
"No submodule named '%s'" :
"Submodule '%s' has not been added yet", name);
} }
return error; return error;
...@@ -198,10 +226,10 @@ int git_submodule_foreach( ...@@ -198,10 +226,10 @@ int git_submodule_foreach(
assert(repo && callback); assert(repo && callback);
if ((error = load_submodule_config(repo, true)) < 0) if ((error = submodule_cache_init(repo, true)) < 0)
return error; return error;
git_strmap_foreach_value(repo->submodules, sm, { git_strmap_foreach_value(repo->_submodules->submodules, sm, {
/* Usually the following will not come into play - it just prevents /* Usually the following will not come into play - it just prevents
* us from issuing a callback twice for a submodule where the name * us from issuing a callback twice for a submodule where the name
* and path are not the same. * and path are not the same.
...@@ -224,24 +252,14 @@ int git_submodule_foreach( ...@@ -224,24 +252,14 @@ int git_submodule_foreach(
return error; return error;
} }
void git_submodule_config_free(git_repository *repo) void git_submodule_cache_free(git_repository *repo)
{ {
git_strmap *smcfg; git_submodule_cache *cache;
git_submodule *sm;
assert(repo); assert(repo);
smcfg = repo->submodules; if ((cache = git__swap(repo->_submodules, NULL)) != NULL)
repo->submodules = NULL; submodule_cache_free(cache);
if (smcfg == NULL)
return;
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( int git_submodule_add_setup(
...@@ -262,12 +280,11 @@ int git_submodule_add_setup( ...@@ -262,12 +280,11 @@ int git_submodule_add_setup(
/* see if there is already an entry for this submodule */ /* see if there is already an entry for this submodule */
if (git_submodule_lookup(&sm, repo, path) < 0) if (git_submodule_lookup(NULL, 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 submodule '%s' that already exists", path);
return GIT_EEXISTS; return GIT_EEXISTS;
} }
...@@ -288,7 +305,7 @@ int git_submodule_add_setup( ...@@ -288,7 +305,7 @@ int git_submodule_add_setup(
/* update .gitmodules */ /* update .gitmodules */
if ((mods = open_gitmodules(repo, true, NULL)) == NULL) { if ((mods = open_gitmodules(repo->_submodules, true)) == NULL) {
giterr_set(GITERR_SUBMODULE, giterr_set(GITERR_SUBMODULE,
"Adding submodules to a bare repository is not supported (for now)"); "Adding submodules to a bare repository is not supported (for now)");
return -1; return -1;
...@@ -348,7 +365,7 @@ int git_submodule_add_setup( ...@@ -348,7 +365,7 @@ 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->_submodules, path, NULL)) &&
!(error = git_submodule_reload(sm, false))) !(error = git_submodule_reload(sm, false)))
error = git_submodule_init(sm, false); error = git_submodule_init(sm, false);
...@@ -489,7 +506,7 @@ int git_submodule_save(git_submodule *submodule) ...@@ -489,7 +506,7 @@ int git_submodule_save(git_submodule *submodule)
assert(submodule); assert(submodule);
mods = open_gitmodules(submodule->repo, true, NULL); mods = open_gitmodules(submodule->repo->_submodules, true);
if (!mods) { if (!mods) {
giterr_set(GITERR_SUBMODULE, giterr_set(GITERR_SUBMODULE,
"Adding submodules to a bare repository is not supported (for now)"); "Adding submodules to a bare repository is not supported (for now)");
...@@ -862,7 +879,7 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm) ...@@ -862,7 +879,7 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm)
} }
static void submodule_cache_remove_item( static void submodule_cache_remove_item(
git_strmap *cache, git_strmap *map,
const char *name, const char *name,
git_submodule *expected, git_submodule *expected,
bool free_after_remove) bool free_after_remove)
...@@ -870,21 +887,21 @@ static void submodule_cache_remove_item( ...@@ -870,21 +887,21 @@ static void submodule_cache_remove_item(
khiter_t pos; khiter_t pos;
git_submodule *found; git_submodule *found;
if (!cache) if (!map)
return; return;
pos = git_strmap_lookup_index(cache, name); pos = git_strmap_lookup_index(map, name);
if (!git_strmap_valid_index(cache, pos)) if (!git_strmap_valid_index(map, pos))
return; return;
found = git_strmap_value_at(cache, pos); found = git_strmap_value_at(map, pos);
if (expected && found != expected) if (expected && found != expected)
return; return;
git_strmap_set_value_at(cache, pos, NULL); git_strmap_set_value_at(map, pos, NULL);
git_strmap_delete_at(cache, pos); git_strmap_delete_at(map, pos);
if (free_after_remove) if (free_after_remove)
git_submodule_free(found); git_submodule_free(found);
...@@ -894,27 +911,31 @@ int git_submodule_reload_all(git_repository *repo, int force) ...@@ -894,27 +911,31 @@ int git_submodule_reload_all(git_repository *repo, int force)
{ {
int error = 0; int error = 0;
git_submodule *sm; git_submodule *sm;
git_strmap *map;
GIT_UNUSED(force); GIT_UNUSED(force);
assert(repo); assert(repo);
if (repo->submodules) if (repo->_submodules) {
git_strmap_foreach_value(repo->submodules, sm, { sm->flags = 0; }); map = repo->_submodules->submodules;
git_strmap_foreach_value(map, sm, { sm->flags = 0; });
}
if ((error = load_submodule_config(repo, true)) < 0) if ((error = submodule_cache_init(repo, true)) < 0)
return error; return error;
git_strmap_foreach_value(repo->submodules, sm, { if (!repo->_submodules || !(map = repo->_submodules->submodules))
git_strmap *cache = repo->submodules; return error;
git_strmap_foreach_value(map, sm, {
if (sm && (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS) == 0) { if (sm && (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS) == 0) {
/* we must check path != name before first remove, in case /* we must check path != name before first remove, in case
* that call frees the submodule */ * that call frees the submodule */
bool free_as_path = (sm->path != sm->name); bool free_as_path = (sm->path != sm->name);
submodule_cache_remove_item(cache, sm->name, sm, true); submodule_cache_remove_item(map, sm->name, sm, true);
if (free_as_path) if (free_as_path)
submodule_cache_remove_item(cache, sm->path, sm, true); submodule_cache_remove_item(map, sm->path, sm, true);
} }
}); });
...@@ -995,41 +1016,44 @@ static int submodule_update_head(git_submodule *submodule) ...@@ -995,41 +1016,44 @@ static int submodule_update_head(git_submodule *submodule)
} }
int git_submodule_reload(git_submodule *submodule, int force) int git_submodule_reload(git_submodule *sm, int force)
{ {
int error = 0; int error = 0;
git_config_backend *mods; git_config_backend *mods;
git_submodule_cache *cache;
GIT_UNUSED(force); GIT_UNUSED(force);
assert(submodule); assert(sm);
cache = sm->repo->_submodules;
/* refresh index data */ /* refresh index data */
if ((error = submodule_update_index(submodule)) < 0) if ((error = submodule_update_index(sm)) < 0)
return error; return error;
/* refresh HEAD tree data */ /* refresh HEAD tree data */
if ((error = submodule_update_head(submodule)) < 0) if ((error = submodule_update_head(sm)) < 0)
return error; return error;
/* done if bare */ /* done if bare */
if (git_repository_is_bare(submodule->repo)) if (git_repository_is_bare(sm->repo))
return error; return error;
/* refresh config data */ /* refresh config data */
mods = open_gitmodules(submodule->repo, false, NULL); mods = open_gitmodules(cache, false);
if (mods != NULL) { if (mods != NULL) {
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
git_buf_sets(&path, "submodule\\."); git_buf_sets(&path, "submodule\\.");
git_buf_text_puts_escape_regex(&path, submodule->name); git_buf_text_puts_escape_regex(&path, sm->name);
git_buf_puts(&path, ".*"); git_buf_puts(&path, ".*");
if (git_buf_oom(&path)) if (git_buf_oom(&path))
error = -1; error = -1;
else else
error = git_config_file_foreach_match( error = git_config_file_foreach_match(
mods, path.ptr, submodule_load_from_config, submodule->repo); mods, path.ptr, submodule_load_from_config, cache);
git_buf_free(&path); git_buf_free(&path);
git_config_file_free(mods); git_config_file_free(mods);
...@@ -1039,9 +1063,9 @@ int git_submodule_reload(git_submodule *submodule, int force) ...@@ -1039,9 +1063,9 @@ int git_submodule_reload(git_submodule *submodule, int force)
} }
/* refresh wd data */ /* refresh wd data */
submodule->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; sm->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS;
return submodule_load_from_wd_lite(submodule); return submodule_load_from_wd_lite(sm);
} }
static void submodule_copy_oid_maybe( static void submodule_copy_oid_maybe(
...@@ -1135,34 +1159,35 @@ int git_submodule_location(unsigned int *location, git_submodule *sm) ...@@ -1135,34 +1159,35 @@ int git_submodule_location(unsigned int *location, git_submodule *sm)
* INTERNAL FUNCTIONS * INTERNAL FUNCTIONS
*/ */
static git_submodule *submodule_alloc(git_repository *repo, const char *name) static int submodule_alloc(
git_submodule **out, git_submodule_cache *cache, const char *name)
{ {
size_t namelen; size_t namelen;
git_submodule *sm; git_submodule *sm;
if (!name || !(namelen = strlen(name))) { if (!name || !(namelen = strlen(name))) {
giterr_set(GITERR_SUBMODULE, "Invalid submodule name"); giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
return NULL; return -1;
} }
sm = git__calloc(1, sizeof(git_submodule)); sm = git__calloc(1, sizeof(git_submodule));
if (sm == NULL) GITERR_CHECK_ALLOC(sm);
return NULL;
sm->name = sm->path = git__strdup(name); sm->name = sm->path = git__strdup(name);
if (!sm->name) { if (!sm->name) {
git__free(sm); git__free(sm);
return NULL; return -1;
} }
GIT_REFCOUNT_INC(sm); GIT_REFCOUNT_INC(sm);
sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE; sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT; sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO; sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO;
sm->repo = repo; sm->repo = cache->repo;
sm->branch = NULL; sm->branch = NULL;
return sm; *out = sm;
return 0;
} }
static void submodule_release(git_submodule *sm) static void submodule_release(git_submodule *sm)
...@@ -1170,11 +1195,15 @@ static void submodule_release(git_submodule *sm) ...@@ -1170,11 +1195,15 @@ static void submodule_release(git_submodule *sm)
if (!sm) if (!sm)
return; return;
if (sm->repo) { if (sm->repo && sm->repo->_submodules) {
git_strmap *cache = sm->repo->submodules; git_strmap *map = sm->repo->_submodules->submodules;
submodule_cache_remove_item(cache, sm->name, sm, false); bool free_as_path = (sm->path != sm->name);
if (sm->path != sm->name)
submodule_cache_remove_item(cache, sm->path, sm, false); sm->repo = NULL;
submodule_cache_remove_item(map, sm->name, sm, false);
if (free_as_path)
submodule_cache_remove_item(map, sm->path, sm, false);
} }
if (sm->path != sm->name) if (sm->path != sm->name)
...@@ -1195,40 +1224,39 @@ void git_submodule_free(git_submodule *sm) ...@@ -1195,40 +1224,39 @@ void git_submodule_free(git_submodule *sm)
static int submodule_get( static int submodule_get(
git_submodule **out, git_submodule **out,
git_repository *repo, git_submodule_cache *cache,
const char *name, const char *name,
const char *alternate) const char *alternate)
{ {
int error = 0; int error = 0;
git_strmap *smcfg = repo->submodules;
khiter_t pos; khiter_t pos;
git_submodule *sm; git_submodule *sm;
pos = git_strmap_lookup_index(smcfg, name); pos = git_strmap_lookup_index(cache->submodules, name);
if (!git_strmap_valid_index(smcfg, pos) && alternate) if (!git_strmap_valid_index(cache->submodules, pos) && alternate)
pos = git_strmap_lookup_index(smcfg, alternate); pos = git_strmap_lookup_index(cache->submodules, alternate);
if (!git_strmap_valid_index(smcfg, pos)) { if (!git_strmap_valid_index(cache->submodules, pos)) {
sm = submodule_alloc(repo, name); if ((error = submodule_alloc(&sm, cache, name)) < 0)
GITERR_CHECK_ALLOC(sm); return error;
/* insert value at name - if another thread beats us to it, then use /* insert value at name - if another thread beats us to it, then use
* their record and release our own. * their record and release our own.
*/ */
pos = kh_put(str, smcfg, sm->name, &error); pos = kh_put(str, cache->submodules, sm->name, &error);
if (error < 0) if (error < 0)
goto done; goto done;
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(cache->submodules, pos);
} else { } else {
error = 0; error = 0;
git_strmap_set_value_at(smcfg, pos, sm); git_strmap_set_value_at(cache->submodules, pos, sm);
} }
} else { } else {
sm = git_strmap_value_at(smcfg, pos); sm = git_strmap_value_at(cache->submodules, pos);
} }
done: done:
...@@ -1294,8 +1322,7 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value) ...@@ -1294,8 +1322,7 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
static int submodule_load_from_config( static int submodule_load_from_config(
const git_config_entry *entry, void *payload) const git_config_entry *entry, void *payload)
{ {
git_repository *repo = payload; git_submodule_cache *cache = payload;
git_strmap *smcfg = repo->submodules;
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;
...@@ -1315,7 +1342,7 @@ static int submodule_load_from_config( ...@@ -1315,7 +1342,7 @@ static int submodule_load_from_config(
path = !strcasecmp(property, "path") ? value : NULL; path = !strcasecmp(property, "path") ? value : NULL;
if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 || if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 ||
(error = submodule_get(&sm, repo, name.ptr, path)) < 0) (error = submodule_get(&sm, cache, name.ptr, path)) < 0)
goto done; goto done;
sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
...@@ -1341,7 +1368,7 @@ static int submodule_load_from_config( ...@@ -1341,7 +1368,7 @@ static int submodule_load_from_config(
/* Found a alternate key for the submodule */ /* Found a alternate key for the submodule */
if (alternate) { if (alternate) {
void *old_sm = NULL; void *old_sm = NULL;
git_strmap_insert2(smcfg, alternate, sm, old_sm, error); git_strmap_insert2(cache->submodules, alternate, sm, old_sm, error);
if (error < 0) if (error < 0)
goto done; goto done;
...@@ -1423,36 +1450,35 @@ static int submodule_load_from_wd_lite(git_submodule *sm) ...@@ -1423,36 +1450,35 @@ static int submodule_load_from_wd_lite(git_submodule *sm)
return 0; return 0;
} }
static int load_submodule_config_from_index( static int submodule_cache_refresh_from_index(git_submodule_cache *cache)
git_repository *repo, git_oid *gitmodules_oid)
{ {
int error; int error;
git_index *index; git_index *index;
git_iterator *i; git_iterator *i;
const git_index_entry *entry; const git_index_entry *entry;
if ((error = git_repository_index__weakptr(&index, repo)) < 0 || if ((error = git_repository_index__weakptr(&index, cache->repo)) < 0 ||
(error = git_iterator_for_index(&i, index, 0, NULL, NULL)) < 0) (error = git_iterator_for_index(&i, index, 0, NULL, NULL)) < 0)
return error; return error;
while (!(error = git_iterator_advance(&entry, i))) { while (!(error = git_iterator_advance(&entry, i))) {
khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path); khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path);
git_submodule *sm; git_submodule *sm;
if (git_strmap_valid_index(repo->submodules, pos)) { if (git_strmap_valid_index(cache->submodules, pos)) {
sm = git_strmap_value_at(repo->submodules, pos); sm = git_strmap_value_at(cache->submodules, pos);
if (S_ISGITLINK(entry->mode)) if (S_ISGITLINK(entry->mode))
submodule_update_from_index_entry(sm, entry); submodule_update_from_index_entry(sm, entry);
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, cache, entry->path, NULL)) {
submodule_update_from_index_entry(sm, entry); submodule_update_from_index_entry(sm, entry);
git_submodule_free(sm); 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(&cache->gitmodules_id, &entry->id);
} }
if (error == GIT_ITEROVER) if (error == GIT_ITEROVER)
...@@ -1463,8 +1489,7 @@ static int load_submodule_config_from_index( ...@@ -1463,8 +1489,7 @@ static int load_submodule_config_from_index(
return error; return error;
} }
static int load_submodule_config_from_head( static int submodule_cache_refresh_from_head(git_submodule_cache *cache)
git_repository *repo, git_oid *gitmodules_oid)
{ {
int error; int error;
git_tree *head; git_tree *head;
...@@ -1472,7 +1497,7 @@ static int load_submodule_config_from_head( ...@@ -1472,7 +1497,7 @@ static int load_submodule_config_from_head(
const git_index_entry *entry; const git_index_entry *entry;
/* if we can't look up current head, then there's no submodule in it */ /* if we can't look up current head, then there's no submodule in it */
if (git_repository_head_tree(&head, repo) < 0) { if (git_repository_head_tree(&head, cache->repo) < 0) {
giterr_clear(); giterr_clear();
return 0; return 0;
} }
...@@ -1483,11 +1508,11 @@ static int load_submodule_config_from_head( ...@@ -1483,11 +1508,11 @@ static int load_submodule_config_from_head(
} }
while (!(error = git_iterator_advance(&entry, i))) { while (!(error = git_iterator_advance(&entry, i))) {
khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path); khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path);
git_submodule *sm; git_submodule *sm;
if (git_strmap_valid_index(repo->submodules, pos)) { if (git_strmap_valid_index(cache->submodules, pos)) {
sm = git_strmap_value_at(repo->submodules, pos); sm = git_strmap_value_at(cache->submodules, pos);
if (S_ISGITLINK(entry->mode)) if (S_ISGITLINK(entry->mode))
submodule_update_from_head_data( submodule_update_from_head_data(
...@@ -1495,14 +1520,14 @@ static int load_submodule_config_from_head( ...@@ -1495,14 +1520,14 @@ 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, cache, 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); 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(&cache->gitmodules_id)) {
git_oid_cpy(gitmodules_oid, &entry->id); git_oid_cpy(&cache->gitmodules_id, &entry->id);
} }
} }
...@@ -1516,11 +1541,10 @@ static int load_submodule_config_from_head( ...@@ -1516,11 +1541,10 @@ static int load_submodule_config_from_head(
} }
static git_config_backend *open_gitmodules( static git_config_backend *open_gitmodules(
git_repository *repo, git_submodule_cache *cache,
bool okay_to_create, bool okay_to_create)
const git_oid *gitmodules_oid)
{ {
const char *workdir = git_repository_workdir(repo); const char *workdir = git_repository_workdir(cache->repo);
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
git_config_backend *mods = NULL; git_config_backend *mods = NULL;
...@@ -1540,7 +1564,7 @@ static git_config_backend *open_gitmodules( ...@@ -1540,7 +1564,7 @@ static git_config_backend *open_gitmodules(
} }
} }
if (!mods && gitmodules_oid && !git_oid_iszero(gitmodules_oid)) { if (!mods && !git_oid_iszero(&cache->gitmodules_id)) {
/* TODO: Retrieve .gitmodules content from ODB */ /* TODO: Retrieve .gitmodules content from ODB */
/* Should we actually do this? Core git does not, but it means you /* Should we actually do this? Core git does not, but it means you
...@@ -1553,53 +1577,86 @@ static git_config_backend *open_gitmodules( ...@@ -1553,53 +1577,86 @@ static git_config_backend *open_gitmodules(
return mods; return mods;
} }
static int load_submodule_config(git_repository *repo, bool reload) static void submodule_cache_free(git_submodule_cache *cache)
{ {
int error; git_submodule *sm;
git_oid gitmodules_oid;
git_config_backend *mods = NULL;
if (!reload && repo->submodules) if (!cache)
return 0; return;
memset(&gitmodules_oid, 0, sizeof(gitmodules_oid)); git_strmap_foreach_value(cache->submodules, sm, {
sm->repo = NULL; /* disconnect from repo */
git_submodule_free(sm);
});
git_strmap_free(cache->submodules);
/* Submodule data is kept in a hashtable keyed by both name and path. git_buf_free(&cache->gitmodules_path);
* These are usually the same, but that is not guaranteed. git_mutex_free(&cache->lock);
*/ git__free(cache);
if (!repo->submodules) { }
repo->submodules = git_strmap_alloc();
GITERR_CHECK_ALLOC(repo->submodules); static int submodule_cache_alloc(
git_submodule_cache **out, git_repository *repo)
{
git_submodule_cache *cache = git__calloc(1, sizeof(git_submodule_cache));
GITERR_CHECK_ALLOC(cache);
if (git_mutex_init(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to initialize submodule cache lock");
git__free(cache);
return -1;
}
cache->submodules = git_strmap_alloc();
if (!cache->submodules) {
submodule_cache_free(cache);
return -1;
} }
cache->repo = repo;
git_buf_init(&cache->gitmodules_path, 0);
*out = cache;
return 0;
}
static int submodule_cache_refresh(git_submodule_cache *cache, bool force)
{
int error;
git_config_backend *mods = NULL;
GIT_UNUSED(force);
/* TODO: only do the following if the sources appear modified */ /* TODO: only do the following if the sources appear modified */
memset(&cache->gitmodules_id, 0, sizeof(cache->gitmodules_id));
/* add submodule information from index */ /* add submodule information from index */
if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0) if ((error = submodule_cache_refresh_from_index(cache)) < 0)
goto cleanup; goto cleanup;
/* add submodule information from HEAD */ /* add submodule information from HEAD */
if ((error = load_submodule_config_from_head(repo, &gitmodules_oid)) < 0) if ((error = submodule_cache_refresh_from_head(cache)) < 0)
goto cleanup; goto cleanup;
/* add submodule information from .gitmodules */ /* add submodule information from .gitmodules */
if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL && if ((mods = open_gitmodules(cache, false)) != NULL &&
(error = git_config_file_foreach( (error = git_config_file_foreach(
mods, submodule_load_from_config, repo)) < 0) mods, submodule_load_from_config, cache)) < 0)
goto cleanup; goto cleanup;
/* shallow scan submodules in work tree */ /* shallow scan submodules in work tree */
if (!git_repository_is_bare(repo)) { if (!git_repository_is_bare(cache->repo)) {
git_submodule *sm; git_submodule *sm;
git_strmap_foreach_value(repo->submodules, sm, { git_strmap_foreach_value(cache->submodules, sm, {
sm->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; sm->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS;
}); });
git_strmap_foreach_value(repo->submodules, sm, { git_strmap_foreach_value(cache->submodules, sm, {
submodule_load_from_wd_lite(sm); submodule_load_from_wd_lite(sm);
}); });
} }
...@@ -1608,8 +1665,28 @@ cleanup: ...@@ -1608,8 +1665,28 @@ cleanup:
if (mods != NULL) if (mods != NULL)
git_config_file_free(mods); git_config_file_free(mods);
if (error) /* TODO: if we got an error, mark submodule config as invalid? */
git_submodule_config_free(repo);
return error;
}
static int submodule_cache_init(git_repository *repo, bool refresh)
{
int error = 0;
git_submodule_cache *cache = NULL;
/* if submodules already exist, just refresh as requested */
if (repo->_submodules)
return refresh ? submodule_cache_refresh(repo->_submodules, false) : 0;
/* otherwise create a new cache, load it, and atomically swap it in */
if (!(error = submodule_cache_alloc(&cache, repo)) &&
!(error = submodule_cache_refresh(cache, true)))
cache = git__compare_and_swap(&repo->_submodules, NULL, cache);
/* might have raced with another thread to set cache, so free if needed */
if (cache)
submodule_cache_free(cache);
return error; return error;
} }
......
...@@ -99,6 +99,30 @@ struct git_submodule { ...@@ -99,6 +99,30 @@ struct git_submodule {
git_oid wd_oid; git_oid wd_oid;
}; };
/**
* The git_submodule_cache stores known submodules along with timestamps,
* etc. about when they were loaded
*/
typedef struct {
git_repository *repo;
git_strmap *submodules;
git_mutex lock;
/* cache invalidation data */
git_oid head_id;
git_futils_filestamp index_stamp;
git_buf gitmodules_path;
git_futils_filestamp gitmodules_stamp;
git_oid gitmodules_id;
git_futils_filestamp config_stamp;
} git_submodule_cache;
/* Force revalidation of submodule data cache (alloc as needed) */
extern int git_submodule_cache_refresh(git_repository *repo);
/* Release all submodules */
extern void git_submodule_cache_free(git_repository *repo);
/* Additional flags on top of public GIT_SUBMODULE_STATUS values */ /* Additional flags on top of public GIT_SUBMODULE_STATUS values */
enum { enum {
GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20), GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20),
...@@ -122,6 +146,10 @@ enum { ...@@ -122,6 +146,10 @@ enum {
/* Internal submodule check does not attempt to refresh cached data */ /* Internal submodule check does not attempt to refresh cached data */
extern bool git_submodule__is_submodule(git_repository *repo, const char *name); extern bool git_submodule__is_submodule(git_repository *repo, const char *name);
/* Internal lookup does not attempt to refresh cached data */
extern int git_submodule__lookup(
git_submodule **out, git_repository *repo, const char *path);
/* Internal status fn returns status and optionally the various OIDs */ /* Internal status fn returns status and optionally the various OIDs */
extern int git_submodule__status( extern int git_submodule__status(
unsigned int *out_status, unsigned int *out_status,
...@@ -143,5 +171,6 @@ extern int git_submodule_parse_update( ...@@ -143,5 +171,6 @@ extern int git_submodule_parse_update(
extern const char *git_submodule_ignore_to_str(git_submodule_ignore_t); extern const char *git_submodule_ignore_to_str(git_submodule_ignore_t);
extern const char *git_submodule_update_to_str(git_submodule_update_t); extern const char *git_submodule_update_to_str(git_submodule_update_t);
extern const char *git_submodule_recurse_to_str(git_submodule_recurse_t);
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment