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)
git_cache_clear(&repo->objects);
git_attr_cache_flush(repo);
git_submodule_cache_free(repo);
set_config(repo, NULL);
set_index(repo, NULL);
......@@ -108,7 +109,6 @@ void git_repository_free(git_repository *repo)
git_repository__cleanup(repo);
git_cache_free(&repo->objects);
git_submodule_config_free(repo);
git_diff_driver_registry_free(repo->diff_drivers);
repo->diff_drivers = NULL;
......
......@@ -19,7 +19,7 @@
#include "buffer.h"
#include "object.h"
#include "attrcache.h"
#include "strmap.h"
#include "submodule.h"
#include "diff_driver.h"
#define DOT_GIT ".git"
......@@ -105,10 +105,10 @@ struct git_repository {
git_refdb *_refdb;
git_config *_config;
git_index *_index;
git_submodule_cache *_submodules;
git_cache objects;
git_attr_cache attrcache;
git_strmap *submodules;
git_diff_driver_registry *diff_drivers;
char *path_repository;
......@@ -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);
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_repository *repo,
const char *operation_name)
......
......@@ -77,11 +77,13 @@ __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, bool reload);
static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *);
static int submodule_cache_init(git_repository *repo, bool refresh);
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(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_wd_lite(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)
/* lookup submodule or return ENOTFOUND if it doesn't exist */
static int submodule_lookup(
git_submodule **out,
git_strmap *cache,
git_submodule_cache *cache,
const char *name,
const char *alternate)
{
......@@ -110,18 +112,18 @@ static int submodule_lookup(
/* 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)
pos = git_strmap_lookup_index(cache, alternate);
if (!git_strmap_valid_index(cache->submodules, pos) && 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 */
return GIT_ENOTFOUND; /* don't set error - caller will cope */
}
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);
*out = sm;
}
......@@ -139,17 +141,45 @@ bool git_submodule__is_submodule(git_repository *repo, const char *name)
{
git_strmap *map;
if (load_submodule_config(repo, false) < 0) {
if (submodule_cache_init(repo, false) < 0) {
giterr_clear();
return false;
}
if (!(map = repo->submodules))
if (!repo->_submodules || !(map = repo->_submodules->submodules))
return false;
return git_strmap_valid_index(map, git_strmap_lookup_index(map, name));
}
static void submodule_set_lookup_error(int error, const char *name)
{
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(
git_submodule **out, /* NULL if user only wants to test existence */
git_repository *repo,
......@@ -159,10 +189,10 @@ int git_submodule_lookup(
assert(repo && name);
if ((error = load_submodule_config(repo, false)) < 0)
if ((error = submodule_cache_init(repo, true)) < 0)
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 */
if (git_repository_workdir(repo)) {
......@@ -178,9 +208,7 @@ int git_submodule_lookup(
git_buf_free(&path);
}
giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ?
"No submodule named '%s'" :
"Submodule '%s' has not been added yet", name);
submodule_set_lookup_error(error, name);
}
return error;
......@@ -198,10 +226,10 @@ int git_submodule_foreach(
assert(repo && callback);
if ((error = load_submodule_config(repo, true)) < 0)
if ((error = submodule_cache_init(repo, true)) < 0)
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
* us from issuing a callback twice for a submodule where the name
* and path are not the same.
......@@ -224,24 +252,14 @@ int git_submodule_foreach(
return error;
}
void git_submodule_config_free(git_repository *repo)
void git_submodule_cache_free(git_repository *repo)
{
git_strmap *smcfg;
git_submodule *sm;
git_submodule_cache *cache;
assert(repo);
smcfg = repo->submodules;
repo->submodules = NULL;
if (smcfg == NULL)
return;
git_strmap_foreach_value(smcfg, sm, {
sm->repo = NULL; /* disconnect from repo */;
git_submodule_free(sm);
});
git_strmap_free(smcfg);
if ((cache = git__swap(repo->_submodules, NULL)) != NULL)
submodule_cache_free(cache);
}
int git_submodule_add_setup(
......@@ -262,12 +280,11 @@ int git_submodule_add_setup(
/* 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();
else {
git_submodule_free(sm);
giterr_set(GITERR_SUBMODULE,
"Attempt to add a submodule that already exists");
"Attempt to add submodule '%s' that already exists", path);
return GIT_EEXISTS;
}
......@@ -288,7 +305,7 @@ int git_submodule_add_setup(
/* update .gitmodules */
if ((mods = open_gitmodules(repo, true, NULL)) == NULL) {
if ((mods = open_gitmodules(repo->_submodules, true)) == NULL) {
giterr_set(GITERR_SUBMODULE,
"Adding submodules to a bare repository is not supported (for now)");
return -1;
......@@ -348,7 +365,7 @@ int git_submodule_add_setup(
/* 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_init(sm, false);
......@@ -489,7 +506,7 @@ int git_submodule_save(git_submodule *submodule)
assert(submodule);
mods = open_gitmodules(submodule->repo, true, NULL);
mods = open_gitmodules(submodule->repo->_submodules, true);
if (!mods) {
giterr_set(GITERR_SUBMODULE,
"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)
}
static void submodule_cache_remove_item(
git_strmap *cache,
git_strmap *map,
const char *name,
git_submodule *expected,
bool free_after_remove)
......@@ -870,21 +887,21 @@ static void submodule_cache_remove_item(
khiter_t pos;
git_submodule *found;
if (!cache)
if (!map)
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;
found = git_strmap_value_at(cache, pos);
found = git_strmap_value_at(map, pos);
if (expected && found != expected)
return;
git_strmap_set_value_at(cache, pos, NULL);
git_strmap_delete_at(cache, pos);
git_strmap_set_value_at(map, pos, NULL);
git_strmap_delete_at(map, pos);
if (free_after_remove)
git_submodule_free(found);
......@@ -894,27 +911,31 @@ int git_submodule_reload_all(git_repository *repo, int force)
{
int error = 0;
git_submodule *sm;
git_strmap *map;
GIT_UNUSED(force);
assert(repo);
if (repo->submodules)
git_strmap_foreach_value(repo->submodules, sm, { sm->flags = 0; });
if (repo->_submodules) {
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;
git_strmap_foreach_value(repo->submodules, sm, {
git_strmap *cache = repo->submodules;
if (!repo->_submodules || !(map = repo->_submodules->submodules))
return error;
git_strmap_foreach_value(map, sm, {
if (sm && (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS) == 0) {
/* we must check path != name before first remove, in case
* that call frees the submodule */
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)
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)
}
int git_submodule_reload(git_submodule *submodule, int force)
int git_submodule_reload(git_submodule *sm, int force)
{
int error = 0;
git_config_backend *mods;
git_submodule_cache *cache;
GIT_UNUSED(force);
assert(submodule);
assert(sm);
cache = sm->repo->_submodules;
/* refresh index data */
if ((error = submodule_update_index(submodule)) < 0)
if ((error = submodule_update_index(sm)) < 0)
return error;
/* refresh HEAD tree data */
if ((error = submodule_update_head(submodule)) < 0)
if ((error = submodule_update_head(sm)) < 0)
return error;
/* done if bare */
if (git_repository_is_bare(submodule->repo))
if (git_repository_is_bare(sm->repo))
return error;
/* refresh config data */
mods = open_gitmodules(submodule->repo, false, NULL);
mods = open_gitmodules(cache, false);
if (mods != NULL) {
git_buf path = GIT_BUF_INIT;
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, ".*");
if (git_buf_oom(&path))
error = -1;
else
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_config_file_free(mods);
......@@ -1039,9 +1063,9 @@ int git_submodule_reload(git_submodule *submodule, int force)
}
/* 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(
......@@ -1135,34 +1159,35 @@ int git_submodule_location(unsigned int *location, git_submodule *sm)
* 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;
git_submodule *sm;
if (!name || !(namelen = strlen(name))) {
giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
return NULL;
return -1;
}
sm = git__calloc(1, sizeof(git_submodule));
if (sm == NULL)
return NULL;
GITERR_CHECK_ALLOC(sm);
sm->name = sm->path = git__strdup(name);
if (!sm->name) {
git__free(sm);
return NULL;
return -1;
}
GIT_REFCOUNT_INC(sm);
sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO;
sm->repo = repo;
sm->repo = cache->repo;
sm->branch = NULL;
return sm;
*out = sm;
return 0;
}
static void submodule_release(git_submodule *sm)
......@@ -1170,11 +1195,15 @@ 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->repo && sm->repo->_submodules) {
git_strmap *map = sm->repo->_submodules->submodules;
bool free_as_path = (sm->path != sm->name);
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)
......@@ -1195,40 +1224,39 @@ void git_submodule_free(git_submodule *sm)
static int submodule_get(
git_submodule **out,
git_repository *repo,
git_submodule_cache *cache,
const char *name,
const char *alternate)
{
int error = 0;
git_strmap *smcfg = repo->submodules;
khiter_t pos;
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)
pos = git_strmap_lookup_index(smcfg, alternate);
if (!git_strmap_valid_index(cache->submodules, pos) && alternate)
pos = git_strmap_lookup_index(cache->submodules, alternate);
if (!git_strmap_valid_index(smcfg, pos)) {
sm = submodule_alloc(repo, name);
GITERR_CHECK_ALLOC(sm);
if (!git_strmap_valid_index(cache->submodules, pos)) {
if ((error = submodule_alloc(&sm, cache, name)) < 0)
return error;
/* insert value at name - if another thread beats us to it, then use
* their record and release our own.
*/
pos = kh_put(str, smcfg, sm->name, &error);
pos = kh_put(str, cache->submodules, sm->name, &error);
if (error < 0)
goto done;
else if (error == 0) {
git_submodule_free(sm);
sm = git_strmap_value_at(smcfg, pos);
sm = git_strmap_value_at(cache->submodules, pos);
} else {
error = 0;
git_strmap_set_value_at(smcfg, pos, sm);
git_strmap_set_value_at(cache->submodules, pos, sm);
}
} else {
sm = git_strmap_value_at(smcfg, pos);
sm = git_strmap_value_at(cache->submodules, pos);
}
done:
......@@ -1294,8 +1322,7 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
static int submodule_load_from_config(
const git_config_entry *entry, void *payload)
{
git_repository *repo = payload;
git_strmap *smcfg = repo->submodules;
git_submodule_cache *cache = payload;
const char *namestart, *property, *alternate = NULL;
const char *key = entry->name, *value = entry->value, *path;
git_buf name = GIT_BUF_INIT;
......@@ -1315,7 +1342,7 @@ static int submodule_load_from_config(
path = !strcasecmp(property, "path") ? value : NULL;
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;
sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
......@@ -1341,7 +1368,7 @@ static int submodule_load_from_config(
/* Found a alternate key for the submodule */
if (alternate) {
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)
goto done;
......@@ -1423,36 +1450,35 @@ static int submodule_load_from_wd_lite(git_submodule *sm)
return 0;
}
static int load_submodule_config_from_index(
git_repository *repo, git_oid *gitmodules_oid)
static int submodule_cache_refresh_from_index(git_submodule_cache *cache)
{
int error;
git_index *index;
git_iterator *i;
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)
return error;
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;
if (git_strmap_valid_index(repo->submodules, pos)) {
sm = git_strmap_value_at(repo->submodules, pos);
if (git_strmap_valid_index(cache->submodules, pos)) {
sm = git_strmap_value_at(cache->submodules, pos);
if (S_ISGITLINK(entry->mode))
submodule_update_from_index_entry(sm, entry);
else
sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
} else if (S_ISGITLINK(entry->mode)) {
if (!submodule_get(&sm, repo, entry->path, NULL)) {
if (!submodule_get(&sm, cache, 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);
git_oid_cpy(&cache->gitmodules_id, &entry->id);
}
if (error == GIT_ITEROVER)
......@@ -1463,8 +1489,7 @@ static int load_submodule_config_from_index(
return error;
}
static int load_submodule_config_from_head(
git_repository *repo, git_oid *gitmodules_oid)
static int submodule_cache_refresh_from_head(git_submodule_cache *cache)
{
int error;
git_tree *head;
......@@ -1472,7 +1497,7 @@ static int load_submodule_config_from_head(
const git_index_entry *entry;
/* 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();
return 0;
}
......@@ -1483,11 +1508,11 @@ static int load_submodule_config_from_head(
}
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;
if (git_strmap_valid_index(repo->submodules, pos)) {
sm = git_strmap_value_at(repo->submodules, pos);
if (git_strmap_valid_index(cache->submodules, pos)) {
sm = git_strmap_value_at(cache->submodules, pos);
if (S_ISGITLINK(entry->mode))
submodule_update_from_head_data(
......@@ -1495,14 +1520,14 @@ 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, cache, 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);
git_oid_iszero(&cache->gitmodules_id)) {
git_oid_cpy(&cache->gitmodules_id, &entry->id);
}
}
......@@ -1516,11 +1541,10 @@ static int load_submodule_config_from_head(
}
static git_config_backend *open_gitmodules(
git_repository *repo,
bool okay_to_create,
const git_oid *gitmodules_oid)
git_submodule_cache *cache,
bool okay_to_create)
{
const char *workdir = git_repository_workdir(repo);
const char *workdir = git_repository_workdir(cache->repo);
git_buf path = GIT_BUF_INIT;
git_config_backend *mods = NULL;
......@@ -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 */
/* Should we actually do this? Core git does not, but it means you
......@@ -1553,53 +1577,86 @@ static git_config_backend *open_gitmodules(
return mods;
}
static int load_submodule_config(git_repository *repo, bool reload)
static void submodule_cache_free(git_submodule_cache *cache)
{
int error;
git_oid gitmodules_oid;
git_config_backend *mods = NULL;
git_submodule *sm;
if (!reload && repo->submodules)
return 0;
if (!cache)
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.
* These are usually the same, but that is not guaranteed.
*/
if (!repo->submodules) {
repo->submodules = git_strmap_alloc();
GITERR_CHECK_ALLOC(repo->submodules);
git_buf_free(&cache->gitmodules_path);
git_mutex_free(&cache->lock);
git__free(cache);
}
static int submodule_cache_alloc(
git_submodule_cache **out, git_repository *repo)
{
git_submodule_cache *cache = git__calloc(1, sizeof(git_submodule_cache));
GITERR_CHECK_ALLOC(cache);
if (git_mutex_init(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to initialize submodule cache lock");
git__free(cache);
return -1;
}
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 */
memset(&cache->gitmodules_id, 0, sizeof(cache->gitmodules_id));
/* 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;
/* 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;
/* 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(
mods, submodule_load_from_config, repo)) < 0)
mods, submodule_load_from_config, cache)) < 0)
goto cleanup;
/* shallow scan submodules in work tree */
if (!git_repository_is_bare(repo)) {
if (!git_repository_is_bare(cache->repo)) {
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;
});
git_strmap_foreach_value(repo->submodules, sm, {
git_strmap_foreach_value(cache->submodules, sm, {
submodule_load_from_wd_lite(sm);
});
}
......@@ -1608,8 +1665,28 @@ cleanup:
if (mods != NULL)
git_config_file_free(mods);
if (error)
git_submodule_config_free(repo);
/* TODO: if we got an error, mark submodule config as invalid? */
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;
}
......
......@@ -99,6 +99,30 @@ struct git_submodule {
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 */
enum {
GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20),
......@@ -122,6 +146,10 @@ enum {
/* Internal submodule check does not attempt to refresh cached data */
extern bool git_submodule__is_submodule(git_repository *repo, const char *name);
/* Internal lookup does not attempt to refresh cached data */
extern int git_submodule__lookup(
git_submodule **out, git_repository *repo, const char *path);
/* Internal status fn returns status and optionally the various OIDs */
extern int git_submodule__status(
unsigned int *out_status,
......@@ -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_update_to_str(git_submodule_update_t);
extern const char *git_submodule_recurse_to_str(git_submodule_recurse_t);
#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