From eeeb9654f064e1047ef8f3cb5a38636133426cdd Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sun, 30 Mar 2014 15:35:56 -0700 Subject: Reinstate efficient submodule reloading This makes it so that git_submodule_reload_all will actually only reload changed items unless the `force` flag is used. --- src/submodule.c | 234 +++++++++++++++++++++++++++----------------------------- 1 file changed, 111 insertions(+), 123 deletions(-) diff --git a/src/submodule.c b/src/submodule.c index 7b06c25c2..96b445ace 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -898,66 +898,9 @@ int git_submodule_open(git_repository **subrepo, git_submodule *sm) return git_submodule__open(subrepo, sm, false); } -static void submodule_cache_remove_item( - git_strmap *map, - const char *name, - git_submodule *expected, - bool free_after_remove) -{ - khiter_t pos; - git_submodule *found; - - if (!map) - return; - - pos = git_strmap_lookup_index(map, name); - - if (!git_strmap_valid_index(map, pos)) - return; - - found = git_strmap_value_at(map, pos); - - if (expected && found != expected) - return; - - git_strmap_set_value_at(map, pos, NULL); - git_strmap_delete_at(map, 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_strmap *map; - - GIT_UNUSED(force); - assert(repo); - - if (repo->_submodules) - submodule_cache_clear_flags(repo->_submodules, 0xFFFFFFFFu); - - if ((error = submodule_cache_init(repo, CACHE_FLUSH)) < 0) - return error; - - 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(map, sm->name, sm, true); - if (free_as_path) - submodule_cache_remove_item(map, sm->path, sm, true); - } - }); - - return error; + return submodule_cache_init(repo, force ? CACHE_FLUSH : CACHE_REFRESH); } static void submodule_update_from_index_entry( @@ -1210,20 +1153,49 @@ static int submodule_alloc( return 0; } +static void submodule_cache_remove_item( + git_submodule_cache *cache, + git_submodule *item, + bool free_after_remove) +{ + git_strmap *map; + const char *name, *alt; + + if (!cache || !(map = cache->submodules) || !item) + return; + + name = item->name; + alt = (item->path != item->name) ? item->path : NULL; + + for (; name; name = alt, alt = NULL) { + khiter_t pos = git_strmap_lookup_index(map, name); + git_submodule *found; + + if (!git_strmap_valid_index(map, pos)) + continue; + + found = git_strmap_value_at(map, pos); + + if (found != item) + continue; + + git_strmap_set_value_at(map, pos, NULL); + git_strmap_delete_at(map, pos); + + if (free_after_remove) + git_submodule_free(found); + } +} + static void submodule_release(git_submodule *sm) { if (!sm) return; - if (sm->repo && sm->repo->_submodules) { - git_strmap *map = sm->repo->_submodules->submodules; - bool free_as_path = (sm->path != sm->name); - + if (sm->repo) { + git_submodule_cache *cache = sm->repo->_submodules; 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); + submodule_cache_remove_item(cache, sm, false); } if (sm->path != sm->name) @@ -1619,99 +1591,115 @@ static int submodule_cache_alloc( static int submodule_cache_refresh(git_submodule_cache *cache, int refresh) { - int error = 0, updates = 0, flush, changed; - git_config_backend *mods = NULL; - const char *wd; + int error = 0, update_index, update_head, update_gitmod; git_index *idx = NULL; git_tree *head = NULL; + const char *wd = NULL; git_buf path = GIT_BUF_INIT; + git_submodule *sm; + git_config_backend *mods = NULL; + uint32_t mask; - if (!refresh) + if (!cache || !cache->repo || !refresh) return 0; - flush = (refresh == CACHE_FLUSH); 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 */ + /* get sources that we will need to check */ - /* add submodule information from index */ + if (git_repository_index(&idx, cache->repo) < 0) + giterr_clear(); + if (git_repository_head_tree(&head, cache->repo) < 0) + giterr_clear(); - if (!git_repository_index(&idx, cache->repo)) { - if (flush || git_index__changed_relative_to(idx, &cache->index_stamp)) { - if ((error = submodule_cache_refresh_from_index(cache, idx)) < 0) - goto cleanup; + wd = git_repository_workdir(cache->repo); + if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0) + goto cleanup; - updates += 1; - git_futils_filestamp_set( - &cache->index_stamp, git_index__filestamp(idx)); - } - } else { - giterr_clear(); + /* check for invalidation */ - submodule_cache_clear_flags( - cache, GIT_SUBMODULE_STATUS_IN_INDEX | - GIT_SUBMODULE_STATUS__INDEX_FLAGS | - GIT_SUBMODULE_STATUS__INDEX_OID_VALID); + if (refresh == CACHE_FLUSH) + update_index = update_head = update_gitmod = true; + else { + update_index = + !idx || git_index__changed_relative_to(idx, &cache->index_stamp); + update_head = + !head || !git_oid_equal(&cache->head_id, git_tree_id(head)); + + update_gitmod = (wd != NULL) ? + git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr) : + (cache->gitmodules_stamp.mtime != 0); + if (update_gitmod < 0) + giterr_clear(); } - /* add submodule information from HEAD */ + /* clear submodule flags that are to be refreshed */ - if (!git_repository_head_tree(&head, cache->repo)) { - if (flush || !git_oid_equal(&cache->head_id, git_tree_id(head))) { - if ((error = submodule_cache_refresh_from_head(cache, head)) < 0) - goto cleanup; + mask = 0; + if (!idx || update_index) + mask |= GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS__INDEX_FLAGS | + GIT_SUBMODULE_STATUS__INDEX_OID_VALID | + GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES; + if (!head || update_head) + mask |= GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS__HEAD_OID_VALID; + if (update_gitmod) + mask |= GIT_SUBMODULE_STATUS_IN_CONFIG; + if (mask != 0) + mask |= GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS__WD_SCANNED | + GIT_SUBMODULE_STATUS__WD_FLAGS | + GIT_SUBMODULE_STATUS__WD_OID_VALID; - updates += 1; - git_oid_cpy(&cache->head_id, git_tree_id(head)); - } - } else { - giterr_clear(); + submodule_cache_clear_flags(cache, mask); + + /* add back submodule information from index */ + + if (idx && update_index) { + if ((error = submodule_cache_refresh_from_index(cache, idx)) < 0) + goto cleanup; - submodule_cache_clear_flags( - cache, GIT_SUBMODULE_STATUS_IN_HEAD | - GIT_SUBMODULE_STATUS__HEAD_OID_VALID); + git_futils_filestamp_set( + &cache->index_stamp, git_index__filestamp(idx)); } - /* add submodule information from .gitmodules */ + /* add submodule information from HEAD */ - wd = git_repository_workdir(cache->repo); + if (head && update_head) { + if ((error = submodule_cache_refresh_from_head(cache, head)) < 0) + goto cleanup; - if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0) - goto cleanup; + git_oid_cpy(&cache->head_id, git_tree_id(head)); + } - changed = git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr); - if (flush && !changed) - changed = 1; + /* add submodule information from .gitmodules */ - 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( + if (wd && update_gitmod > 0) { + if ((mods = open_gitmodules(cache, false)) != NULL && + (error = git_config_file_foreach( mods, submodule_load_from_config, cache)) < 0) goto cleanup; - updates += 1; } - /* shallow scan submodules in work tree */ - - if (wd && updates > 0) { - git_submodule *sm; - - submodule_cache_clear_flags( - cache, GIT_SUBMODULE_STATUS_IN_WD | - GIT_SUBMODULE_STATUS__WD_SCANNED | - GIT_SUBMODULE_STATUS__WD_FLAGS | - GIT_SUBMODULE_STATUS__WD_OID_VALID); + /* shallow scan submodules in work tree as needed */ + if (wd && mask != 0) { git_strmap_foreach_value(cache->submodules, sm, { submodule_load_from_wd_lite(sm); }); } + /* remove submodules that no longer exist */ + + git_strmap_foreach_value(cache->submodules, sm, { + if (sm && (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS) == 0) + submodule_cache_remove_item(cache, sm, true); + }); + cleanup: git_config_file_free(mods); -- cgit v1.2.1