]> git.proxmox.com Git - libgit2.git/blobdiff - src/submodule.c
Merge branch 'master' into pr/3938
[libgit2.git] / src / submodule.c
index ac699860357c42a79e4be30b5014f3a8196fb07a..21e3d45e414b94e6068529f20f40d7d6b8f5ed84 100644 (file)
@@ -124,8 +124,8 @@ static void submodule_set_lookup_error(int error, const char *name)
                return;
 
        giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ?
-               "No submodule named '%s'" :
-               "Submodule '%s' has not been added yet", name);
+               "no submodule named '%s'" :
+               "submodule '%s' has not been added yet", name);
 }
 
 typedef struct {
@@ -149,40 +149,53 @@ static int find_by_path(const git_config_entry *entry, void *payload)
 }
 
 /**
- * Find out the name of a submodule from its path
+ * Release the name map returned by 'load_submodule_names'.
  */
-static int name_from_path(git_buf *out, git_config *cfg, const char *path)
+static void free_submodule_names(git_strmap *names)
+{
+       git_buf *name = 0;
+       if (names == NULL)
+               return;
+       git_strmap_foreach_value(names, name, {
+               git__free(name);
+       });
+       git_strmap_free(names);
+       return;
+}
+
+/**
+ * Map submodule paths to names.
+ * TODO: for some use-cases, this might need case-folding on a
+ * case-insensitive filesystem
+ */
+static int load_submodule_names(git_strmap *out, git_config *cfg)
 {
        const char *key = "submodule\\..*\\.path";
        git_config_iterator *iter;
        git_config_entry *entry;
-       int error;
+       git_buf buf = GIT_BUF_INIT;
+       int rval;
+       int error = 0;
 
        if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0)
                return error;
 
-       while ((error = git_config_next(&entry, iter)) == 0) {
+       while (git_config_next(&entry, iter) == 0) {
                const char *fdot, *ldot;
-               /* TODO: this should maybe be strcasecmp on a case-insensitive fs */
-               if (strcmp(path, entry->value) != 0)
-                       continue;
-
                fdot = strchr(entry->name, '.');
                ldot = strrchr(entry->name, '.');
 
-               git_buf_clear(out);
-               git_buf_put(out, fdot + 1, ldot - fdot - 1);
-               goto cleanup;
-       }
-
-       if (error == GIT_ITEROVER) {
-               giterr_set(GITERR_SUBMODULE, "could not find a submodule name for '%s'", path);
-               error = GIT_ENOTFOUND;
+               git_buf_put(&buf, fdot + 1, ldot - fdot - 1);
+               git_strmap_insert(out, entry->value, git_buf_detach(&buf), rval);
+               if (rval < 0) {
+                       giterr_set(GITERR_NOMEMORY, "Error inserting submodule into hash table");
+                       free_submodule_names(out);
+                       return -1;
+               }
        }
 
-cleanup:
        git_config_iterator_free(iter);
-       return error;
+       return 0;
 }
 
 int git_submodule_lookup(
@@ -196,6 +209,17 @@ int git_submodule_lookup(
 
        assert(repo && name);
 
+       if (repo->submodule_cache != NULL) {
+               khiter_t pos = git_strmap_lookup_index(repo->submodule_cache, name);
+               if (git_strmap_valid_index(repo->submodule_cache, pos)) {
+                       if (out) {
+                               *out = git_strmap_value_at(repo->submodule_cache, pos);
+                               GIT_REFCOUNT_INC(*out);
+                       }
+                       return 0;
+               }
+       }
+
        if ((error = submodule_alloc(&sm, repo, name)) < 0)
                return error;
 
@@ -324,89 +348,107 @@ done:
 
 static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg)
 {
-       int error;
-       git_iterator *i;
-       const git_index_entry *entry;
-       git_buf name = GIT_BUF_INIT;
-
-       if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
-               return error;
-
-       while (!(error = git_iterator_advance(&entry, i))) {
-               khiter_t pos = git_strmap_lookup_index(map, entry->path);
-               git_submodule *sm;
+       int error;
+       git_iterator *i;
+       const git_index_entry *entry;
+       git_strmap *names = 0;
+       git_strmap_alloc(&names);
+       if ((error = load_submodule_names(names, cfg)))
+               goto done;
 
-              git_buf_clear(&name);
-              if (!name_from_path(&name, cfg, entry->path)) {
-                      git_strmap_lookup_index(map, name.ptr);
-              }
+       if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
+               goto done;
 
-               if (git_strmap_valid_index(map, pos)) {
-                       sm = git_strmap_value_at(map, pos);
+       while (!(error = git_iterator_advance(&entry, i))) {
+               khiter_t pos = git_strmap_lookup_index(map, entry->path);
+               git_submodule *sm;
+
+               if (git_strmap_valid_index(map, pos)) {
+                       sm = git_strmap_value_at(map, 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)) {
+                       khiter_t name_pos;
+                       const char *name;
+
+                       name_pos = git_strmap_lookup_index(names, entry->path);
+                       if (git_strmap_valid_index(names, name_pos)) {
+                               name = git_strmap_value_at(names, name_pos);
+                       } else {
+                               name = entry->path;
+                       }
 
-                       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_or_create(&sm, git_index_owner(idx), map, name.ptr ? name.ptr : entry->path)) {
-                               submodule_update_from_index_entry(sm, entry);
-                               git_submodule_free(sm);
-                       }
-               }
-       }
+                       if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name)) {
+                               submodule_update_from_index_entry(sm, entry);
+                               git_submodule_free(sm);
+                       }
+               }
+       }
 
-       if (error == GIT_ITEROVER)
-               error = 0;
+       if (error == GIT_ITEROVER)
+               error = 0;
 
-       git_buf_free(&name);
-       git_iterator_free(i);
+done:
+       git_iterator_free(i);
+       free_submodule_names(names);
 
-       return error;
+       return error;
 }
 
 static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg)
 {
-       int error;
-       git_iterator *i;
-       const git_index_entry *entry;
-       git_buf name = GIT_BUF_INIT;
-
-       if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
-               return error;
-
-       while (!(error = git_iterator_advance(&entry, i))) {
-               khiter_t pos = git_strmap_lookup_index(map, entry->path);
-               git_submodule *sm;
+       int error;
+       git_iterator *i;
+       const git_index_entry *entry;
+       git_strmap *names = 0;
+       git_strmap_alloc(&names);
+       if ((error = load_submodule_names(names, cfg)))
+               goto done;
 
-              git_buf_clear(&name);
-              if (!name_from_path(&name, cfg, entry->path)) {
-                      git_strmap_lookup_index(map, name.ptr);
-              }
+       if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
+               goto done;
 
-               if (git_strmap_valid_index(map, pos)) {
-                       sm = git_strmap_value_at(map, pos);
+       while (!(error = git_iterator_advance(&entry, i))) {
+               khiter_t pos = git_strmap_lookup_index(map, entry->path);
+               git_submodule *sm;
+
+               if (git_strmap_valid_index(map, pos)) {
+                       sm = git_strmap_value_at(map, pos);
+
+                       if (S_ISGITLINK(entry->mode))
+                               submodule_update_from_head_data(sm, entry->mode, &entry->id);
+                       else
+                               sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
+               } else if (S_ISGITLINK(entry->mode)) {
+                       khiter_t name_pos;
+                       const char *name;
+
+                       name_pos = git_strmap_lookup_index(names, entry->path);
+                       if (git_strmap_valid_index(names, name_pos)) {
+                               name = git_strmap_value_at(names, name_pos);
+                       } else {
+                               name = entry->path;
+                       }
 
-                       if (S_ISGITLINK(entry->mode))
-                               submodule_update_from_head_data(sm, entry->mode, &entry->id);
-                       else
-                               sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
-               } else if (S_ISGITLINK(entry->mode)) {
-                       if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name.ptr ? name.ptr : entry->path)) {
-                               submodule_update_from_head_data(
-                                       sm, entry->mode, &entry->id);
-                               git_submodule_free(sm);
-                       }
-               }
-       }
+                       if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name)) {
+                               submodule_update_from_head_data(
+                                       sm, entry->mode, &entry->id);
+                               git_submodule_free(sm);
+                       }
+               }
+       }
 
-       if (error == GIT_ITEROVER)
-               error = 0;
+       if (error == GIT_ITEROVER)
+               error = 0;
 
-       git_buf_free(&name);
-       git_iterator_free(i);
+done:
+       git_iterator_free(i);
+       free_submodule_names(names);
 
-       return error;
+       return error;
 }
 
 /* If have_sm is true, sm is populated, otherwise map an repo are. */
@@ -416,7 +458,7 @@ typedef struct {
        git_repository *repo;
 } lfc_data;
 
-static int all_submodules(git_repository *repo, git_strmap *map)
+int git_submodule__map(git_repository *repo, git_strmap *map)
 {
        int error = 0;
        git_index *idx = NULL;
@@ -509,7 +551,7 @@ int git_submodule_foreach(
        if ((error = git_strmap_alloc(&submodules)) < 0)
                return error;
 
-       if ((error = all_submodules(repo, submodules)) < 0)
+       if ((error = git_submodule__map(repo, submodules)) < 0)
                goto done;
 
        if (!(error = git_vector_init(
@@ -618,7 +660,7 @@ int git_submodule_add_setup(
                giterr_clear();
        else {
                giterr_set(GITERR_SUBMODULE,
-                       "Attempt to add submodule '%s' that already exists", path);
+                       "attempt to add submodule '%s' that already exists", path);
                return GIT_EEXISTS;
        }
 
@@ -628,7 +670,7 @@ int git_submodule_add_setup(
                path += strlen(git_repository_workdir(repo));
 
        if (git_path_root(path) >= 0) {
-               giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path");
+               giterr_set(GITERR_SUBMODULE, "submodule path must be a relative path");
                error = -1;
                goto cleanup;
        }
@@ -637,7 +679,7 @@ int git_submodule_add_setup(
 
        if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
                giterr_set(GITERR_SUBMODULE,
-                       "Adding submodules to a bare repository is not supported");
+                       "adding submodules to a bare repository is not supported");
                return -1;
        }
 
@@ -758,7 +800,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
        /* read stat information for submodule working directory */
        if (p_stat(path.ptr, &st) < 0) {
                giterr_set(GITERR_SUBMODULE,
-                       "Cannot add submodule without working directory");
+                       "cannot add submodule without working directory");
                error = -1;
                goto cleanup;
        }
@@ -771,7 +813,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
        /* calling git_submodule_open will have set sm->wd_oid if possible */
        if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
                giterr_set(GITERR_SUBMODULE,
-                       "Cannot add submodule without HEAD to index");
+                       "cannot add submodule without HEAD to index");
                error = -1;
                goto cleanup;
        }
@@ -861,7 +903,7 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur
        } else if (strchr(url, ':') != NULL || url[0] == '/') {
                error = git_buf_sets(out, url);
        } else {
-               giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
+               giterr_set(GITERR_SUBMODULE, "invalid format for submodule URL");
                error = -1;
        }
 
@@ -1133,7 +1175,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio
                                goto done;
 
                        if (!init) {
-                               giterr_set(GITERR_SUBMODULE, "Submodule is not initialized.");
+                               giterr_set(GITERR_SUBMODULE, "submodule is not initialized");
                                error = GIT_ERROR;
                                goto done;
                        }
@@ -1214,7 +1256,7 @@ int git_submodule_init(git_submodule *sm, int overwrite)
 
        if (!sm->url) {
                giterr_set(GITERR_SUBMODULE,
-                       "No URL configured for submodule '%s'", sm->name);
+                       "no URL configured for submodule '%s'", sm->name);
                return -1;
        }
 
@@ -1258,7 +1300,7 @@ int git_submodule_sync(git_submodule *sm)
 
        if (!sm->url) {
                giterr_set(GITERR_SUBMODULE,
-                       "No URL configured for submodule '%s'", sm->name);
+                       "no URL configured for submodule '%s'", sm->name);
                return -1;
        }
 
@@ -1501,13 +1543,22 @@ int git_submodule__status(
                return 0;
        }
 
-       /* refresh the index OID */
-       if (submodule_update_index(sm) < 0)
-               return -1;
+       /* If the user has requested caching submodule state, performing these
+        * expensive operations (especially `submodule_update_head`, which is
+        * bottlenecked on `git_repository_head_tree`) eliminates much of the
+        * advantage.  We will, therefore, interpret the request for caching to
+        * apply here to and skip them.
+        */
 
-       /* refresh the HEAD OID */
-       if (submodule_update_head(sm) < 0)
-               return -1;
+       if (sm->repo->submodule_cache == NULL) {
+               /* refresh the index OID */
+               if (submodule_update_index(sm) < 0)
+                       return -1;
+
+               /* refresh the HEAD OID */
+               if (submodule_update_head(sm) < 0)
+                       return -1;
+       }
 
        /* for ignore == dirty, don't scan the working directory */
        if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
@@ -1565,7 +1616,6 @@ int git_submodule_location(unsigned int *location, git_submodule *sm)
                location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
 }
 
-
 /*
  * INTERNAL FUNCTIONS
  */
@@ -1577,7 +1627,7 @@ static int submodule_alloc(
        git_submodule *sm;
 
        if (!name || !(namelen = strlen(name))) {
-               giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
+               giterr_set(GITERR_SUBMODULE, "invalid submodule name");
                return -1;
        }
 
@@ -1629,7 +1679,7 @@ void git_submodule_free(git_submodule *sm)
 static int submodule_config_error(const char *property, const char *value)
 {
        giterr_set(GITERR_INVALID,
-               "Invalid value for submodule '%s' property: '%s'", property, value);
+               "invalid value for submodule '%s' property: '%s'", property, value);
        return -1;
 }
 
@@ -1967,7 +2017,7 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo)
        if (error == GIT_ENOTFOUND)
                giterr_set(
                        GITERR_SUBMODULE,
-                       "Cannot get default remote for submodule - no local tracking "
+                       "cannot get default remote for submodule - no local tracking "
                        "branch for HEAD and origin does not exist");
 
        return error;