Commit db0e7878 by Russell Belfer

Make submodule refresh a bit smarter

This makes submodule cache refresh actually look at the timestamps
from the data sources for submodules and reload as needed if they
have changed since the last refresh.
parent 69b6ffc4
...@@ -16,6 +16,7 @@ GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level ...@@ -16,6 +16,7 @@ GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level
GIT_INLINE(void) git_config_file_free(git_config_backend *cfg) GIT_INLINE(void) git_config_file_free(git_config_backend *cfg)
{ {
if (cfg)
cfg->free(cfg); cfg->free(cfg);
} }
......
...@@ -517,6 +517,16 @@ int git_index_read(git_index *index, int force) ...@@ -517,6 +517,16 @@ int git_index_read(git_index *index, int force)
return error; return error;
} }
int git_index__changed_relative_to(
git_index *index, const git_futils_filestamp *fs)
{
/* attempt to update index (ignoring errors) */
if (git_index_read(index, false) < 0)
giterr_clear();
return (memcmp(&index->stamp, fs, sizeof(index->stamp)) == 0);
}
int git_index_write(git_index *index) int git_index_write(git_index *index)
{ {
git_filebuf file = GIT_FILEBUF_INIT; git_filebuf file = GIT_FILEBUF_INIT;
......
...@@ -62,4 +62,11 @@ extern void git_index__set_ignore_case(git_index *index, bool ignore_case); ...@@ -62,4 +62,11 @@ extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
extern unsigned int git_index__create_mode(unsigned int mode); extern unsigned int git_index__create_mode(unsigned int mode);
GIT_INLINE(const git_futils_filestamp *) git_index__filestamp(git_index *index)
{
return &index->stamp;
}
extern int git_index__changed_relative_to(git_index *index, const git_futils_filestamp *fs);
#endif #endif
...@@ -133,6 +133,18 @@ static int submodule_lookup( ...@@ -133,6 +133,18 @@ static int submodule_lookup(
return 0; return 0;
} }
/* clear a set of flags on all submodules */
static void submodule_cache_clear_flags(
git_submodule_cache *cache, uint32_t mask)
{
git_submodule *sm;
uint32_t inverted_mask = ~mask;
git_strmap_foreach_value(cache->submodules, sm, {
sm->flags &= inverted_mask;
});
}
/* /*
* PUBLIC APIS * PUBLIC APIS
*/ */
...@@ -377,7 +389,6 @@ cleanup: ...@@ -377,7 +389,6 @@ cleanup:
if (out != NULL) if (out != NULL)
*out = sm; *out = sm;
if (mods != NULL)
git_config_file_free(mods); git_config_file_free(mods);
git_repository_free(subrepo); git_repository_free(subrepo);
git_buf_free(&real_url); git_buf_free(&real_url);
...@@ -556,7 +567,6 @@ int git_submodule_save(git_submodule *submodule) ...@@ -556,7 +567,6 @@ int git_submodule_save(git_submodule *submodule)
submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
cleanup: cleanup:
if (mods != NULL)
git_config_file_free(mods); git_config_file_free(mods);
git_buf_free(&key); git_buf_free(&key);
...@@ -916,10 +926,8 @@ int git_submodule_reload_all(git_repository *repo, int force) ...@@ -916,10 +926,8 @@ int git_submodule_reload_all(git_repository *repo, int force)
GIT_UNUSED(force); GIT_UNUSED(force);
assert(repo); assert(repo);
if (repo->_submodules) { if (repo->_submodules)
map = repo->_submodules->submodules; submodule_cache_clear_flags(repo->_submodules, 0xFFFFFFFFu);
git_strmap_foreach_value(map, sm, { sm->flags = 0; });
}
if ((error = submodule_cache_init(repo, true)) < 0) if ((error = submodule_cache_init(repo, true)) < 0)
return error; return error;
...@@ -1063,7 +1071,9 @@ int git_submodule_reload(git_submodule *sm, int force) ...@@ -1063,7 +1071,9 @@ int git_submodule_reload(git_submodule *sm, int force)
} }
/* refresh wd data */ /* refresh wd data */
sm->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; sm->flags &=
~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID |
GIT_SUBMODULE_STATUS__WD_FLAGS);
return submodule_load_from_wd_lite(sm); return submodule_load_from_wd_lite(sm);
} }
...@@ -1450,15 +1460,14 @@ static int submodule_load_from_wd_lite(git_submodule *sm) ...@@ -1450,15 +1460,14 @@ static int submodule_load_from_wd_lite(git_submodule *sm)
return 0; return 0;
} }
static int submodule_cache_refresh_from_index(git_submodule_cache *cache) static int submodule_cache_refresh_from_index(
git_submodule_cache *cache, git_index *idx)
{ {
int error; int error;
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, cache->repo)) < 0 || if ((error = git_iterator_for_index(&i, idx, 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))) {
...@@ -1477,8 +1486,7 @@ static int submodule_cache_refresh_from_index(git_submodule_cache *cache) ...@@ -1477,8 +1486,7 @@ static int submodule_cache_refresh_from_index(git_submodule_cache *cache)
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) }
git_oid_cpy(&cache->gitmodules_id, &entry->id);
} }
if (error == GIT_ITEROVER) if (error == GIT_ITEROVER)
...@@ -1489,23 +1497,15 @@ static int submodule_cache_refresh_from_index(git_submodule_cache *cache) ...@@ -1489,23 +1497,15 @@ static int submodule_cache_refresh_from_index(git_submodule_cache *cache)
return error; return error;
} }
static int submodule_cache_refresh_from_head(git_submodule_cache *cache) static int submodule_cache_refresh_from_head(
git_submodule_cache *cache, git_tree *head)
{ {
int error; int error;
git_tree *head;
git_iterator *i; git_iterator *i;
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 ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0)
if (git_repository_head_tree(&head, cache->repo) < 0) {
giterr_clear();
return 0;
}
if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) {
git_tree_free(head);
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(cache->submodules, entry->path); khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path);
...@@ -1515,8 +1515,7 @@ static int submodule_cache_refresh_from_head(git_submodule_cache *cache) ...@@ -1515,8 +1515,7 @@ static int submodule_cache_refresh_from_head(git_submodule_cache *cache)
sm = git_strmap_value_at(cache->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(sm, entry->mode, &entry->id);
sm, entry->mode, &entry->id);
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)) {
...@@ -1525,9 +1524,6 @@ static int submodule_cache_refresh_from_head(git_submodule_cache *cache) ...@@ -1525,9 +1524,6 @@ static int submodule_cache_refresh_from_head(git_submodule_cache *cache)
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 &&
git_oid_iszero(&cache->gitmodules_id)) {
git_oid_cpy(&cache->gitmodules_id, &entry->id);
} }
} }
...@@ -1535,7 +1531,6 @@ static int submodule_cache_refresh_from_head(git_submodule_cache *cache) ...@@ -1535,7 +1531,6 @@ static int submodule_cache_refresh_from_head(git_submodule_cache *cache)
error = 0; error = 0;
git_iterator_free(i); git_iterator_free(i);
git_tree_free(head);
return error; return error;
} }
...@@ -1564,14 +1559,6 @@ static git_config_backend *open_gitmodules( ...@@ -1564,14 +1559,6 @@ static git_config_backend *open_gitmodules(
} }
} }
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
* can't really get much information about submodules on bare repos.
*/
}
git_buf_free(&path); git_buf_free(&path);
return mods; return mods;
...@@ -1622,51 +1609,103 @@ static int submodule_cache_alloc( ...@@ -1622,51 +1609,103 @@ static int submodule_cache_alloc(
static int submodule_cache_refresh(git_submodule_cache *cache, bool force) static int submodule_cache_refresh(git_submodule_cache *cache, bool force)
{ {
int error; int error = 0, updates = 0, changed;
git_config_backend *mods = NULL; git_config_backend *mods = NULL;
const char *wd;
git_index *idx = NULL;
git_tree *head = NULL;
git_buf path = GIT_BUF_INIT;
GIT_UNUSED(force); if (git_mutex_lock(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache");
return -1;
}
/* 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 = submodule_cache_refresh_from_index(cache)) < 0) if (!git_repository_index(&idx, cache->repo)) {
if (force || git_index__changed_relative_to(idx, &cache->index_stamp)) {
if ((error = submodule_cache_refresh_from_index(cache, idx)) < 0)
goto cleanup; goto cleanup;
updates += 1;
git_futils_filestamp_set(
&cache->index_stamp, git_index__filestamp(idx));
}
} else {
giterr_clear();
submodule_cache_clear_flags(
cache, GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS__INDEX_FLAGS |
GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
}
/* add submodule information from HEAD */ /* add submodule information from HEAD */
if ((error = submodule_cache_refresh_from_head(cache)) < 0) if (!git_repository_head_tree(&head, cache->repo)) {
if (force || !git_oid_equal(&cache->head_id, git_tree_id(head))) {
if ((error = submodule_cache_refresh_from_head(cache, head)) < 0)
goto cleanup; goto cleanup;
updates += 1;
git_oid_cpy(&cache->head_id, git_tree_id(head));
}
} else {
giterr_clear();
submodule_cache_clear_flags(
cache, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
}
/* add submodule information from .gitmodules */ /* add submodule information from .gitmodules */
if ((mods = open_gitmodules(cache, false)) != NULL && wd = git_repository_workdir(cache->repo);
(error = git_config_file_foreach(
if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0)
goto cleanup;
changed = git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr);
if (changed < 0) {
giterr_clear();
submodule_cache_clear_flags(cache, GIT_SUBMODULE_STATUS_IN_CONFIG);
} else if (changed > 0 && (mods = open_gitmodules(cache, false)) != NULL) {
if ((error = git_config_file_foreach(
mods, submodule_load_from_config, cache)) < 0) mods, submodule_load_from_config, cache)) < 0)
goto cleanup; goto cleanup;
updates += 1;
}
/* shallow scan submodules in work tree */ /* shallow scan submodules in work tree */
if (!git_repository_is_bare(cache->repo)) { if (wd && updates > 0) {
git_submodule *sm; git_submodule *sm;
git_strmap_foreach_value(cache->submodules, sm, { submodule_cache_clear_flags(
sm->flags &= ~GIT_SUBMODULE_STATUS__ALL_WD_FLAGS; cache, GIT_SUBMODULE_STATUS_IN_WD |
}); GIT_SUBMODULE_STATUS__WD_SCANNED |
GIT_SUBMODULE_STATUS__WD_FLAGS |
GIT_SUBMODULE_STATUS__WD_OID_VALID);
git_strmap_foreach_value(cache->submodules, sm, { git_strmap_foreach_value(cache->submodules, sm, {
submodule_load_from_wd_lite(sm); submodule_load_from_wd_lite(sm);
}); });
} }
cleanup: cleanup:
if (mods != NULL)
git_config_file_free(mods); git_config_file_free(mods);
/* TODO: if we got an error, mark submodule config as invalid? */ /* TODO: if we got an error, mark submodule config as invalid? */
git_mutex_unlock(&cache->lock);
git_index_free(idx);
git_tree_free(head);
git_buf_free(&path);
return error; return error;
} }
......
...@@ -113,7 +113,6 @@ typedef struct { ...@@ -113,7 +113,6 @@ typedef struct {
git_futils_filestamp index_stamp; git_futils_filestamp index_stamp;
git_buf gitmodules_path; git_buf gitmodules_path;
git_futils_filestamp gitmodules_stamp; git_futils_filestamp gitmodules_stamp;
git_oid gitmodules_id;
git_futils_filestamp config_stamp; git_futils_filestamp config_stamp;
} git_submodule_cache; } git_submodule_cache;
...@@ -135,11 +134,6 @@ enum { ...@@ -135,11 +134,6 @@ enum {
GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27), GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27),
}; };
#define GIT_SUBMODULE_STATUS__ALL_WD_FLAGS \
(GIT_SUBMODULE_STATUS_IN_WD | \
GIT_SUBMODULE_STATUS__WD_OID_VALID | \
GIT_SUBMODULE_STATUS__WD_FLAGS)
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \ #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
((S) & ~(0xFFFFFFFFu << 20)) ((S) & ~(0xFFFFFFFFu << 20))
......
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