]> git.proxmox.com Git - libgit2.git/blobdiff - src/submodule.c
Revert "Update d/ch for 0.28.5+dfsg.1-1 release -- from unstable branch"
[libgit2.git] / src / submodule.c
index 38db41529628f12b1f83a6ca334ffbb462a32aec..1690e08f81f3beffad5cd956d70c95a468dc1149 100644 (file)
@@ -5,7 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "submodule.h"
+
 #include "git2/config.h"
 #include "git2/sys/config.h"
 #include "git2/types.h"
 #include "buf_text.h"
 #include "vector.h"
 #include "posix.h"
-#include "config_file.h"
+#include "config_backend.h"
 #include "config.h"
 #include "repository.h"
-#include "submodule.h"
 #include "tree.h"
 #include "iterator.h"
 #include "path.h"
 #include "index.h"
+#include "worktree.h"
+#include "clone.h"
 
 #define GIT_MODULES_FILE ".gitmodules"
 
-static git_cvar_map _sm_update_map[] = {
-       {GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
-       {GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
-       {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
-       {GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
-       {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_UPDATE_NONE},
-       {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_UPDATE_CHECKOUT},
+static git_configmap _sm_update_map[] = {
+       {GIT_CONFIGMAP_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
+       {GIT_CONFIGMAP_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
+       {GIT_CONFIGMAP_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
+       {GIT_CONFIGMAP_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
+       {GIT_CONFIGMAP_FALSE, NULL, GIT_SUBMODULE_UPDATE_NONE},
+       {GIT_CONFIGMAP_TRUE, NULL, GIT_SUBMODULE_UPDATE_CHECKOUT},
 };
 
-static git_cvar_map _sm_ignore_map[] = {
-       {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE},
-       {GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
-       {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
-       {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
-       {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_IGNORE_NONE},
-       {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_IGNORE_ALL},
+static git_configmap _sm_ignore_map[] = {
+       {GIT_CONFIGMAP_STRING, "none", GIT_SUBMODULE_IGNORE_NONE},
+       {GIT_CONFIGMAP_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
+       {GIT_CONFIGMAP_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
+       {GIT_CONFIGMAP_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
+       {GIT_CONFIGMAP_FALSE, NULL, GIT_SUBMODULE_IGNORE_NONE},
+       {GIT_CONFIGMAP_TRUE, NULL, GIT_SUBMODULE_IGNORE_ALL},
 };
 
-static git_cvar_map _sm_recurse_map[] = {
-       {GIT_CVAR_STRING, "on-demand", GIT_SUBMODULE_RECURSE_ONDEMAND},
-       {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_RECURSE_NO},
-       {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_RECURSE_YES},
+static git_configmap _sm_recurse_map[] = {
+       {GIT_CONFIGMAP_STRING, "on-demand", GIT_SUBMODULE_RECURSE_ONDEMAND},
+       {GIT_CONFIGMAP_FALSE, NULL, GIT_SUBMODULE_RECURSE_NO},
+       {GIT_CONFIGMAP_TRUE, NULL, GIT_SUBMODULE_RECURSE_YES},
 };
 
 enum {
@@ -59,39 +61,12 @@ enum {
        GITMODULES_CREATE = 1,
 };
 
-static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
-{
-       khint_t h;
-
-       for (h = 0; *s; ++s)
-               if (s[1] != '\0' || *s != '/')
-                       h = (h << 5) - h + *s;
-
-       return h;
-}
-
-static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
-{
-       size_t alen = a ? strlen(a) : 0;
-       size_t blen = b ? strlen(b) : 0;
-
-       if (alen > 0 && a[alen - 1] == '/')
-               alen--;
-       if (blen > 0 && b[blen - 1] == '/')
-               blen--;
-
-       return (alen == blen && strncmp(a, b, alen) == 0);
-}
-
-__KHASH_IMPL(
-       str, static kh_inline, const char *, void *, 1,
-       str_hash_no_trailing_slash, str_equal_no_trailing_slash)
-
 static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name);
 static git_config_backend *open_gitmodules(git_repository *repo, int gitmod);
-static git_config *gitmodules_snapshot(git_repository *repo);
+static int gitmodules_snapshot(git_config **snap, git_repository *repo);
 static int get_url_base(git_buf *url, git_repository *repo);
 static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo);
+static int lookup_default_remote(git_remote **remote, git_repository *repo);
 static int submodule_load_each(const git_config_entry *entry, void *payload);
 static int submodule_read_config(git_submodule *sm, git_config *cfg);
 static int submodule_load_from_wd_lite(git_submodule *);
@@ -121,9 +96,9 @@ 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);
+       git_error_set(GIT_ERROR_SUBMODULE, (error == GIT_ENOTFOUND) ?
+               "no submodule named '%s'" :
+               "submodule '%s' has not been added yet", name);
 }
 
 typedef struct {
@@ -140,45 +115,136 @@ static int find_by_path(const git_config_entry *entry, void *payload)
                fdot = strchr(entry->name, '.');
                ldot = strrchr(entry->name, '.');
                data->name = git__strndup(fdot + 1, ldot - fdot - 1);
-               GITERR_CHECK_ALLOC(data->name);
+               GIT_ERROR_CHECK_ALLOC(data->name);
        }
 
        return 0;
 }
 
+/*
+ * Checks to see if the submodule shares its name with a file or directory that
+ * already exists on the index. If so, the submodule cannot be added.
+ */
+static int is_path_occupied(bool *occupied, git_repository *repo, const char *path)
+{
+       int error = 0;
+       git_index *index;
+       git_buf dir = GIT_BUF_INIT;
+       *occupied = false;
+
+       if ((error = git_repository_index__weakptr(&index, repo)) < 0)
+               goto out;
+
+       if ((error = git_index_find(NULL, index, path)) != GIT_ENOTFOUND) {
+               if (!error) {
+                       git_error_set(GIT_ERROR_SUBMODULE,
+                               "File '%s' already exists in the index", path);
+                       *occupied = true;
+               }
+               goto out;
+       }
+
+       if ((error = git_buf_sets(&dir, path)) < 0)
+               goto out;
+
+       if ((error = git_path_to_dir(&dir)) < 0)
+               goto out;
+
+       if ((error = git_index_find_prefix(NULL, index, dir.ptr)) != GIT_ENOTFOUND) {
+               if (!error) {
+                       git_error_set(GIT_ERROR_SUBMODULE,
+                               "Directory '%s' already exists in the index", path);
+                       *occupied = true;
+               }
+               goto out;
+       }
+
+       error = 0;
+
+out:
+       git_buf_dispose(&dir);
+       return error;
+}
+
+/**
+ * Release the name map returned by 'load_submodule_names'.
+ */
+static void free_submodule_names(git_strmap *names)
+{
+       const char *key;
+       char *value;
+
+       if (names == NULL)
+               return;
+
+       git_strmap_foreach(names, key, value, {
+               git__free((char *) key);
+               git__free(value);
+       });
+       git_strmap_free(names);
+
+       return;
+}
+
 /**
- * Find out the name of a submodule from its path
+ * Map submodule paths to names.
+ * TODO: for some use-cases, this might need case-folding on a
+ * case-insensitive filesystem
  */
-static int name_from_path(git_buf *out, git_config *cfg, const char *path)
+static int load_submodule_names(git_strmap **out, git_repository *repo, git_config *cfg)
 {
        const char *key = "submodule\\..*\\.path";
-       git_config_iterator *iter;
+       git_config_iterator *iter = NULL;
        git_config_entry *entry;
-       int error;
+       git_buf buf = GIT_BUF_INIT;
+       git_strmap *names;
+       int isvalid, error;
+
+       *out = NULL;
+
+       if ((error = git_strmap_new(&names)) < 0)
+               goto out;
 
        if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0)
-               return error;
+               goto out;
 
        while ((error = 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 (git_strmap_exists(names, entry->value)) {
+                       git_error_set(GIT_ERROR_SUBMODULE,
+                                  "duplicated submodule path '%s'", entry->value);
+                       error = -1;
+                       goto out;
+               }
 
-       if (error == GIT_ITEROVER) {
-               giterr_set(GITERR_SUBMODULE, "could not find a submodule name for '%s'", path);
-               error = GIT_ENOTFOUND;
+               git_buf_clear(&buf);
+               git_buf_put(&buf, fdot + 1, ldot - fdot - 1);
+               isvalid = git_submodule_name_is_valid(repo, buf.ptr, 0);
+               if (isvalid < 0) {
+                       error = isvalid;
+                       goto out;
+               }
+               if (!isvalid)
+                       continue;
+
+               if ((error = git_strmap_set(names, git__strdup(entry->value), git_buf_detach(&buf))) < 0) {
+                       git_error_set(GIT_ERROR_NOMEMORY, "error inserting submodule into hash table");
+                       error = -1;
+                       goto out;
+               }
        }
+       if (error == GIT_ITEROVER)
+               error = 0;
 
-cleanup:
+       *out = names;
+       names = NULL;
+
+out:
+       free_submodule_names(names);
+       git_buf_dispose(&buf);
        git_config_iterator_free(iter);
        return error;
 }
@@ -194,6 +260,21 @@ int git_submodule_lookup(
 
        assert(repo && name);
 
+       if (repo->is_bare) {
+               git_error_set(GIT_ERROR_SUBMODULE, "cannot get submodules without a working tree");
+               return -1;
+       }
+
+       if (repo->submodule_cache != NULL) {
+               if ((sm = git_strmap_get(repo->submodule_cache, name)) != NULL) {
+                       if (out) {
+                               *out = sm;
+                               GIT_REFCOUNT_INC(*out);
+                       }
+                       return 0;
+               }
+       }
+
        if ((error = submodule_alloc(&sm, repo, name)) < 0)
                return error;
 
@@ -223,13 +304,13 @@ int git_submodule_lookup(
                mods = open_gitmodules(repo, GITMODULES_EXISTING);
 
                if (mods)
-                       error = git_config_file_foreach_match(mods, pattern, find_by_path, &data);
+                       error = git_config_backend_foreach_match(mods, pattern, find_by_path, &data);
 
-               git_config_file_free(mods);
+               git_config_backend_free(mods);
 
                if (error < 0) {
                        git_submodule_free(sm);
-                       git_buf_free(&path);
+                       git_buf_dispose(&path);
                        return error;
                }
 
@@ -245,7 +326,7 @@ int git_submodule_lookup(
                        }
                }
 
-               git_buf_free(&path);
+               git_buf_dispose(&path);
        }
 
        if ((error = git_submodule_location(&location, sm)) < 0) {
@@ -268,7 +349,7 @@ int git_submodule_lookup(
                        if (git_path_exists(path.ptr))
                                error = GIT_EEXISTS;
 
-                       git_buf_free(&path);
+                       git_buf_dispose(&path);
                }
 
                submodule_set_lookup_error(error, name);
@@ -283,6 +364,28 @@ int git_submodule_lookup(
        return 0;
 }
 
+int git_submodule_name_is_valid(git_repository *repo, const char *name, int flags)
+{
+       git_buf buf = GIT_BUF_INIT;
+       int error, isvalid;
+
+       if (flags == 0)
+               flags = GIT_PATH_REJECT_FILESYSTEM_DEFAULTS;
+
+       /* Avoid allocating a new string if we can avoid it */
+       if (strchr(name, '\\') != NULL) {
+               if ((error = git_path_normalize_slashes(&buf, name)) < 0)
+                       return error;
+       } else {
+               git_buf_attach_notowned(&buf, name, strlen(name));
+       }
+
+       isvalid =  git_path_isvalid(repo, buf.ptr, 0, flags);
+       git_buf_dispose(&buf);
+
+       return isvalid;
+}
+
 static void submodule_free_dup(void *sm)
 {
        git_submodule_free(sm);
@@ -290,30 +393,21 @@ static void submodule_free_dup(void *sm)
 
 static int submodule_get_or_create(git_submodule **out, git_repository *repo, git_strmap *map, const char *name)
 {
-       int error = 0;
-       khiter_t pos;
        git_submodule *sm = NULL;
+       int error;
 
-       pos = git_strmap_lookup_index(map, name);
-       if (git_strmap_valid_index(map, pos)) {
-               sm = git_strmap_value_at(map, pos);
+       if ((sm = git_strmap_get(map, name)) != NULL)
                goto done;
-       }
 
        /* if the submodule doesn't exist yet in the map, create it */
        if ((error = submodule_alloc(&sm, repo, name)) < 0)
                return error;
 
-       pos = kh_put(str, map, sm->name, &error);
-       /* nobody can beat us to adding it */
-       assert(error != 0);
-       if (error < 0) {
+       if ((error = git_strmap_set(map, sm->name, sm)) < 0) {
                git_submodule_free(sm);
                return error;
        }
 
-       git_strmap_set_value_at(map, pos, sm);
-
 done:
        GIT_REFCOUNT_INC(sm);
        *out = sm;
@@ -322,89 +416,91 @@ 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;
+       int error;
+       git_iterator *i = NULL;
+       const git_index_entry *entry;
+       git_strmap *names;
 
-       if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
-               return error;
+       if ((error = load_submodule_names(&names, git_index_owner(idx), cfg)))
+               goto done;
 
-       while (!(error = git_iterator_advance(&entry, i))) {
-               khiter_t pos = git_strmap_lookup_index(map, entry->path);
-               git_submodule *sm;
+       if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
+               goto done;
 
-              git_buf_clear(&name);
-              if (!name_from_path(&name, cfg, entry->path)) {
-                      git_strmap_lookup_index(map, name.ptr);
-              }
+       while (!(error = git_iterator_advance(&entry, i))) {
+               git_submodule *sm;
 
-               if (git_strmap_valid_index(map, pos)) {
-                       sm = git_strmap_value_at(map, pos);
+               if ((sm = git_strmap_get(map, entry->path)) != NULL) {
+                       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)) {
+                       const char *name;
 
-                       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 ((name = git_strmap_get(names, entry->path)) == NULL)
+                               name = entry->path;
 
-       if (error == GIT_ITEROVER)
-               error = 0;
+                       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;
 
-       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;
+       int error;
+       git_iterator *i = NULL;
+       const git_index_entry *entry;
+       git_strmap *names;
 
-       if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
-               return error;
+       if ((error = load_submodule_names(&names, git_tree_owner(head), cfg)))
+               goto done;
 
-       while (!(error = git_iterator_advance(&entry, i))) {
-               khiter_t pos = git_strmap_lookup_index(map, entry->path);
-               git_submodule *sm;
+       if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
+               goto done;
+
+       while (!(error = git_iterator_advance(&entry, i))) {
+               git_submodule *sm;
 
-              git_buf_clear(&name);
-              if (!name_from_path(&name, cfg, entry->path)) {
-                      git_strmap_lookup_index(map, name.ptr);
-              }
+               if ((sm = git_strmap_get(map, entry->path)) != NULL) {
+                       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)) {
+                       const char *name;
 
-               if (git_strmap_valid_index(map, pos)) {
-                       sm = git_strmap_value_at(map, pos);
+                       if ((name = git_strmap_get(names, entry->path)) == NULL)
+                               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. */
@@ -414,7 +510,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;
@@ -423,44 +519,30 @@ static int all_submodules(git_repository *repo, git_strmap *map)
        git_buf path = GIT_BUF_INIT;
        git_submodule *sm;
        git_config *mods = NULL;
-       uint32_t mask;
 
        assert(repo && map);
 
        /* get sources that we will need to check */
        if (git_repository_index(&idx, repo) < 0)
-               giterr_clear();
+               git_error_clear();
        if (git_repository_head_tree(&head, repo) < 0)
-               giterr_clear();
+               git_error_clear();
 
        wd = git_repository_workdir(repo);
        if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0)
                goto cleanup;
 
-       /* clear submodule flags that are to be refreshed */
-       mask = 0;
-       mask |= GIT_SUBMODULE_STATUS_IN_INDEX |
-               GIT_SUBMODULE_STATUS__INDEX_FLAGS |
-               GIT_SUBMODULE_STATUS__INDEX_OID_VALID |
-               GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
-
-       mask |= GIT_SUBMODULE_STATUS_IN_HEAD |
-               GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
-       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;
-
        /* add submodule information from .gitmodules */
        if (wd) {
                lfc_data data = { 0 };
                data.map = map;
                data.repo = repo;
 
-               if ((mods = gitmodules_snapshot(repo)) == NULL)
+               if ((error = gitmodules_snapshot(&mods, repo)) < 0) {
+                       if (error == GIT_ENOTFOUND)
+                               error = 0;
                        goto cleanup;
+               }
 
                data.mods = mods;
                if ((error = git_config_foreach(
@@ -468,17 +550,17 @@ static int all_submodules(git_repository *repo, git_strmap *map)
                        goto cleanup;
        }
        /* add back submodule information from index */
-       if (idx) {
+       if (mods && idx) {
                if ((error = submodules_from_index(map, idx, mods)) < 0)
                        goto cleanup;
        }
        /* add submodule information from HEAD */
-       if (head) {
+       if (mods && head) {
                if ((error = submodules_from_head(map, head, mods)) < 0)
                        goto cleanup;
        }
        /* shallow scan submodules in work tree as needed */
-       if (wd && mask != 0) {
+       if (wd) {
                git_strmap_foreach_value(map, sm, {
                                submodule_load_from_wd_lite(sm);
                        });
@@ -489,7 +571,7 @@ cleanup:
        /* TODO: if we got an error, mark submodule config as invalid? */
        git_index_free(idx);
        git_tree_free(head);
-       git_buf_free(&path);
+       git_buf_dispose(&path);
        return error;
 }
 
@@ -504,14 +586,19 @@ int git_submodule_foreach(
        int error;
        size_t i;
 
-       if ((error = git_strmap_alloc(&submodules)) < 0)
+       if (repo->is_bare) {
+               git_error_set(GIT_ERROR_SUBMODULE, "cannot get submodules without a working tree");
+               return -1;
+       }
+
+       if ((error = git_strmap_new(&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(
-                       &snapshot, kh_size(submodules), submodule_cmp))) {
+                       &snapshot, git_strmap_size(submodules), submodule_cmp))) {
 
                git_strmap_foreach_value(submodules, sm, {
                        if ((error = git_vector_insert(&snapshot, sm)) < 0)
@@ -527,7 +614,7 @@ int git_submodule_foreach(
 
        git_vector_foreach(&snapshot, i, sm) {
                if ((error = callback(sm, sm->name, payload)) != 0) {
-                       giterr_set_after_callback(error);
+                       git_error_set_after_callback(error);
                        break;
                }
        }
@@ -572,8 +659,10 @@ static int submodule_repo_init(
         * Old style: sub-repo goes directly into repo/<name>/.git/
         */
         if (use_gitlink) {
-               error = git_buf_join3(
-                       &repodir, '/', git_repository_path(parent_repo), "modules", path);
+               error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
+               if (error < 0)
+                       goto cleanup;
+               error = git_buf_joinpath(&repodir, repodir.ptr, path);
                if (error < 0)
                        goto cleanup;
 
@@ -587,8 +676,8 @@ static int submodule_repo_init(
                error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt);
 
 cleanup:
-       git_buf_free(&workdir);
-       git_buf_free(&repodir);
+       git_buf_dispose(&workdir);
+       git_buf_dispose(&repodir);
 
        *out = subrepo;
 
@@ -607,16 +696,17 @@ int git_submodule_add_setup(
        git_submodule *sm = NULL;
        git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
        git_repository *subrepo = NULL;
+       bool path_occupied;
 
        assert(repo && url && path);
 
        /* see if there is already an entry for this submodule */
 
        if (git_submodule_lookup(NULL, repo, path) < 0)
-               giterr_clear();
+               git_error_clear();
        else {
-               giterr_set(GITERR_SUBMODULE,
-                       "Attempt to add submodule '%s' that already exists", path);
+               git_error_set(GIT_ERROR_SUBMODULE,
+                       "attempt to add submodule '%s' that already exists", path);
                return GIT_EEXISTS;
        }
 
@@ -626,25 +716,33 @@ 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");
+               git_error_set(GIT_ERROR_SUBMODULE, "submodule path must be a relative path");
                error = -1;
                goto cleanup;
        }
 
+       if ((error = is_path_occupied(&path_occupied, repo, path)) < 0)
+               goto cleanup;
+
+       if (path_occupied) {
+               error = GIT_EEXISTS;
+               goto cleanup;
+       }
+
        /* update .gitmodules */
 
        if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
-               giterr_set(GITERR_SUBMODULE,
-                       "Adding submodules to a bare repository is not supported");
+               git_error_set(GIT_ERROR_SUBMODULE,
+                       "adding submodules to a bare repository is not supported");
                return -1;
        }
 
        if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
-               (error = git_config_file_set_string(mods, name.ptr, path)) < 0)
+               (error = git_config_backend_set_string(mods, name.ptr, path)) < 0)
                goto cleanup;
 
        if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
-               (error = git_config_file_set_string(mods, name.ptr, url)) < 0)
+               (error = git_config_backend_set_string(mods, name.ptr, url)) < 0)
                goto cleanup;
 
        git_buf_clear(&name);
@@ -682,10 +780,10 @@ cleanup:
        if (out != NULL)
                *out = sm;
 
-       git_config_file_free(mods);
+       git_config_backend_free(mods);
        git_repository_free(subrepo);
-       git_buf_free(&real_url);
-       git_buf_free(&name);
+       git_buf_dispose(&real_url);
+       git_buf_dispose(&name);
 
        return error;
 }
@@ -714,7 +812,65 @@ int git_submodule_repo_init(
 
 done:
        git_config_free(cfg);
-       git_buf_free(&buf);
+       git_buf_dispose(&buf);
+       return error;
+}
+
+static int clone_return_origin(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
+{
+       GIT_UNUSED(url);
+       GIT_UNUSED(payload);
+       return git_remote_lookup(out, repo, name);
+}
+
+static int clone_return_repo(git_repository **out, const char *path, int bare, void *payload)
+{
+       git_submodule *sm = payload;
+
+       GIT_UNUSED(path);
+       GIT_UNUSED(bare);
+       return git_submodule_open(out, sm);
+}
+
+int git_submodule_clone(git_repository **out, git_submodule *submodule, const git_submodule_update_options *given_opts)
+{
+       int error;
+       git_repository *clone;
+       git_buf rel_path = GIT_BUF_INIT;
+       git_submodule_update_options sub_opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+       git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+
+       assert(submodule);
+
+       if (given_opts)
+               memcpy(&sub_opts, given_opts, sizeof(sub_opts));
+
+       GIT_ERROR_CHECK_VERSION(&sub_opts, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
+
+       memcpy(&opts.checkout_opts, &sub_opts.checkout_opts, sizeof(sub_opts.checkout_opts));
+       memcpy(&opts.fetch_opts, &sub_opts.fetch_opts, sizeof(sub_opts.fetch_opts));
+       opts.repository_cb = clone_return_repo;
+       opts.repository_cb_payload = submodule;
+       opts.remote_cb = clone_return_origin;
+       opts.remote_cb_payload = submodule;
+
+       git_buf_puts(&rel_path, git_repository_workdir(git_submodule_owner(submodule)));
+       git_buf_joinpath(&rel_path, git_buf_cstr(&rel_path), git_submodule_path(submodule));
+
+       GIT_ERROR_CHECK_ALLOC_BUF(&rel_path);
+
+       error = git_clone__submodule(&clone, git_submodule_url(submodule), git_buf_cstr(&rel_path), &opts);
+       if (error < 0)
+               goto cleanup;
+
+       if (!out)
+               git_repository_free(clone);
+       else
+               *out = clone;
+
+cleanup:
+       git_buf_dispose(&rel_path);
+
        return error;
 }
 
@@ -755,8 +911,8 @@ 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");
+               git_error_set(GIT_ERROR_SUBMODULE,
+                       "cannot add submodule without working directory");
                error = -1;
                goto cleanup;
        }
@@ -764,12 +920,12 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
        memset(&entry, 0, sizeof(entry));
        entry.path = sm->path;
        git_index_entry__init_from_stat(
-               &entry, &st, !(git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE));
+               &entry, &st, !(git_index_caps(index) & GIT_INDEX_CAPABILITY_NO_FILEMODE));
 
        /* 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");
+               git_error_set(GIT_ERROR_SUBMODULE,
+                       "cannot add submodule without HEAD to index");
                error = -1;
                goto cleanup;
        }
@@ -798,7 +954,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
 
 cleanup:
        git_repository_free(sm_repo);
-       git_buf_free(&path);
+       git_buf_dispose(&path);
        return error;
 }
 
@@ -859,11 +1015,11 @@ 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");
+               git_error_set(GIT_ERROR_SUBMODULE, "invalid format for submodule URL");
                error = -1;
        }
 
-       git_buf_free(&normalized);
+       git_buf_dispose(&normalized);
        return error;
 }
 
@@ -881,28 +1037,28 @@ static int write_var(git_repository *repo, const char *name, const char *var, co
                goto cleanup;
 
        if (val)
-               error = git_config_file_set_string(mods, key.ptr, val);
+               error = git_config_backend_set_string(mods, key.ptr, val);
        else
-               error = git_config_file_delete(mods, key.ptr);
+               error = git_config_backend_delete(mods, key.ptr);
 
-       git_buf_free(&key);
+       git_buf_dispose(&key);
 
 cleanup:
-       git_config_file_free(mods);
+       git_config_backend_free(mods);
        return error;
 }
 
-static int write_mapped_var(git_repository *repo, const char *name, git_cvar_map *maps, size_t nmaps, const char *var, int ival)
+static int write_mapped_var(git_repository *repo, const char *name, git_configmap *maps, size_t nmaps, const char *var, int ival)
 {
-       git_cvar_t type;
+       git_configmap_t type;
        const char *val;
 
        if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) {
-               giterr_set(GITERR_SUBMODULE, "invalid value for %s", var);
+               git_error_set(GIT_ERROR_SUBMODULE, "invalid value for %s", var);
                return -1;
        }
 
-       if (type == GIT_CVAR_TRUE)
+       if (type == GIT_CONFIGMAP_TRUE)
                val = "true";
 
        return write_var(repo, name, var, val);
@@ -961,7 +1117,7 @@ const git_oid *git_submodule_wd_id(git_submodule *submodule)
                if (!git_submodule_open_bare(&subrepo, submodule))
                        git_repository_free(subrepo);
                else
-                       giterr_clear();
+                       git_error_clear();
        }
 
        if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
@@ -1040,16 +1196,18 @@ static int submodule_repo_create(
         * <repo-dir>/modules/<name>/ with a gitlink in the
         * sub-repo workdir directory to that repository.
         */
-       error = git_buf_join3(
-               &repodir, '/', git_repository_path(parent_repo), "modules", path);
+       error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
+       if (error < 0)
+               goto cleanup;
+       error = git_buf_joinpath(&repodir, repodir.ptr, path);
        if (error < 0)
                goto cleanup;
 
        error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
 
 cleanup:
-       git_buf_free(&workdir);
-       git_buf_free(&repodir);
+       git_buf_dispose(&workdir);
+       git_buf_dispose(&repodir);
 
        *out = subrepo;
 
@@ -1075,13 +1233,18 @@ static int git_submodule_update_repo_init_cb(
        return submodule_repo_create(out, sm->repo, path);
 }
 
-int git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version)
+int git_submodule_update_options_init(git_submodule_update_options *opts, unsigned int version)
 {
        GIT_INIT_STRUCTURE_FROM_TEMPLATE(
                opts, version, git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_INIT);
        return 0;
 }
 
+int git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version)
+{
+       return git_submodule_update_options_init(opts, version);
+}
+
 int git_submodule_update(git_submodule *sm, int init, git_submodule_update_options *_update_options)
 {
        int error;
@@ -1100,7 +1263,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio
        if (_update_options)
                memcpy(&update_options, _update_options, sizeof(git_submodule_update_options));
 
-       GITERR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
+       GIT_ERROR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
 
        /* Copy over the remote callbacks */
        memcpy(&clone_options.fetch_opts, &update_options.fetch_opts, sizeof(git_fetch_options));
@@ -1130,8 +1293,8 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio
                        if (error != GIT_ENOTFOUND)
                                goto done;
 
-                       if (error == GIT_ENOTFOUND && !init) {
-                               giterr_set(GITERR_SUBMODULE, "Submodule is not initialized.");
+                       if (!init) {
+                               git_error_set(GIT_ERROR_SUBMODULE, "submodule is not initialized");
                                error = GIT_ERROR;
                                goto done;
                        }
@@ -1158,21 +1321,39 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio
                 * will checkout the specific commit manually.
                 */
                clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
-               update_options.checkout_opts.checkout_strategy = update_options.clone_checkout_strategy;
 
                if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
                        (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 ||
                        (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0)
                        goto done;
        } else {
+               const git_oid *oid;
+
                /**
                 * Work dir is initialized - look up the commit in the parent repository's index,
                 * update the workdir contents of the subrepository, and set the subrepository's
                 * head to the new commit.
                 */
-               if ((error = git_submodule_open(&sub_repo, sm)) < 0 ||
-                       (error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0 ||
-                       (error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 ||
+               if ((error = git_submodule_open(&sub_repo, sm)) < 0)
+                       goto done;
+
+               if ((oid = git_submodule_index_id(sm)) == NULL) {
+                       git_error_set(GIT_ERROR_SUBMODULE, "could not get ID of submodule in index");
+                       error = -1;
+                       goto done;
+               }
+
+               /* Look up the target commit in the submodule. */
+               if ((error = git_object_lookup(&target_commit, sub_repo, oid, GIT_OBJECT_COMMIT)) < 0) {
+                       /* If it isn't found then fetch and try again. */
+                       if (error != GIT_ENOTFOUND || !update_options.allow_fetch ||
+                               (error = lookup_default_remote(&remote, sub_repo)) < 0 ||
+                               (error = git_remote_fetch(remote, NULL, &update_options.fetch_opts, NULL)) < 0 ||
+                               (error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJECT_COMMIT)) < 0)
+                               goto done;
+               }
+
+               if ((error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 ||
                        (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0)
                        goto done;
 
@@ -1184,7 +1365,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio
        }
 
 done:
-       git_buf_free(&buf);
+       git_buf_dispose(&buf);
        git_config_free(config);
        git_object_free(target_commit);
        git_remote_free(remote);
@@ -1201,8 +1382,8 @@ int git_submodule_init(git_submodule *sm, int overwrite)
        git_config *cfg = NULL;
 
        if (!sm->url) {
-               giterr_set(GITERR_SUBMODULE,
-                       "No URL configured for submodule '%s'", sm->name);
+               git_error_set(GIT_ERROR_SUBMODULE,
+                       "no URL configured for submodule '%s'", sm->name);
                return -1;
        }
 
@@ -1231,58 +1412,54 @@ int git_submodule_init(git_submodule *sm, int overwrite)
 
 cleanup:
        git_config_free(cfg);
-       git_buf_free(&key);
-       git_buf_free(&effective_submodule_url);
+       git_buf_dispose(&key);
+       git_buf_dispose(&effective_submodule_url);
 
        return error;
 }
 
 int git_submodule_sync(git_submodule *sm)
 {
-       int error = 0;
-       git_config *cfg = NULL;
-       git_buf key = GIT_BUF_INIT;
+       git_buf key = GIT_BUF_INIT, url = GIT_BUF_INIT, remote_name = GIT_BUF_INIT;
        git_repository *smrepo = NULL;
+       git_config *cfg = NULL;
+       int error = 0;
 
        if (!sm->url) {
-               giterr_set(GITERR_SUBMODULE,
-                       "No URL configured for submodule '%s'", sm->name);
+               git_error_set(GIT_ERROR_SUBMODULE, "no URL configured for submodule '%s'", sm->name);
                return -1;
        }
 
        /* copy URL over to config only if it already exists */
+       if ((error = git_repository_config__weakptr(&cfg, sm->repo)) < 0 ||
+           (error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 ||
+           (error = git_submodule_resolve_url(&url, sm->repo, sm->url)) < 0 ||
+           (error = git_config__update_entry(cfg, key.ptr, url.ptr, true, true)) < 0)
+               goto out;
 
-       if (!(error = git_repository_config__weakptr(&cfg, sm->repo)) &&
-               !(error = git_buf_printf(&key, "submodule.%s.url", sm->name)))
-               error = git_config__update_entry(cfg, key.ptr, sm->url, true, true);
+       if (!(sm->flags & GIT_SUBMODULE_STATUS_IN_WD))
+               goto out;
 
        /* if submodule exists in the working directory, update remote url */
+       if ((error = git_submodule_open(&smrepo, sm)) < 0 ||
+           (error = git_repository_config__weakptr(&cfg, smrepo)) < 0)
+               goto out;
 
-       if (!error &&
-               (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
-               !(error = git_submodule_open(&smrepo, sm)))
-       {
-               git_buf remote_name = GIT_BUF_INIT;
-
-               if ((error = git_repository_config__weakptr(&cfg, smrepo)) < 0)
-                       /* return error from reading submodule config */;
-               else if ((error = lookup_head_remote_key(&remote_name, smrepo)) < 0) {
-                       giterr_clear();
-                       error = git_buf_sets(&key, "remote.origin.url");
-               } else {
-                       error = git_buf_join3(
-                               &key, '.', "remote", remote_name.ptr, "url");
-                       git_buf_free(&remote_name);
-               }
-
-               if (!error)
-                       error = git_config__update_entry(cfg, key.ptr, sm->url, true, false);
-
-               git_repository_free(smrepo);
+       if (lookup_head_remote_key(&remote_name, smrepo) == 0) {
+               if ((error = git_buf_join3(&key, '.', "remote", remote_name.ptr, "url")) < 0)
+                       goto out;
+       } else if ((error = git_buf_sets(&key, "remote.origin.url")) < 0) {
+               goto out;
        }
 
-       git_buf_free(&key);
+       if ((error = git_config__update_entry(cfg, key.ptr, url.ptr, true, false)) < 0)
+               goto out;
 
+out:
+       git_repository_free(smrepo);
+       git_buf_dispose(&remote_name);
+       git_buf_dispose(&key);
+       git_buf_dispose(&url);
        return error;
 }
 
@@ -1324,7 +1501,7 @@ static int git_submodule__open(
                if (!git_reference_name_to_id(&sm->wd_oid, *subrepo, GIT_HEAD_FILE))
                        sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
                else
-                       giterr_clear();
+                       git_error_clear();
        } else if (git_path_exists(path.ptr)) {
                sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED |
                        GIT_SUBMODULE_STATUS_IN_WD;
@@ -1335,7 +1512,7 @@ static int git_submodule__open(
                        sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
        }
 
-       git_buf_free(&path);
+       git_buf_dispose(&path);
 
        return error;
 }
@@ -1414,9 +1591,9 @@ static int submodule_update_head(git_submodule *submodule)
        /* if we can't look up file in current head, then done */
        if (git_repository_head_tree(&head, submodule->repo) < 0 ||
                git_tree_entry_bypath(&te, head, submodule->path) < 0)
-               giterr_clear();
+               git_error_clear();
        else
-               submodule_update_from_head_data(submodule, te->attr, &te->oid);
+               submodule_update_from_head_data(submodule, te->attr, git_tree_entry_id(te));
 
        git_tree_entry_free(te);
        git_tree_free(head);
@@ -1425,36 +1602,40 @@ static int submodule_update_head(git_submodule *submodule)
 
 int git_submodule_reload(git_submodule *sm, int force)
 {
-       int error = 0;
-       git_config *mods;
+       git_config *mods = NULL;
+       int error;
 
        GIT_UNUSED(force);
 
        assert(sm);
 
-       if (!git_repository_is_bare(sm->repo)) {
-               /* refresh config data */
-               mods = gitmodules_snapshot(sm->repo);
-               if (mods != NULL) {
-                       error = submodule_read_config(sm, mods);
-                       git_config_free(mods);
+       if ((error = git_submodule_name_is_valid(sm->repo, sm->name, 0)) <= 0)
+               /* This should come with a warning, but we've no API for that */
+               goto out;
 
-                       if (error < 0)
-                               return error;
-               }
+       if (git_repository_is_bare(sm->repo))
+               goto out;
 
-               /* refresh wd data */
-               sm->flags &=
-                       ~(GIT_SUBMODULE_STATUS_IN_WD |
-                         GIT_SUBMODULE_STATUS__WD_OID_VALID |
-                         GIT_SUBMODULE_STATUS__WD_FLAGS);
+       /* refresh config data */
+       if ((error = gitmodules_snapshot(&mods, sm->repo)) < 0 && error != GIT_ENOTFOUND)
+               goto out;
 
-               error = submodule_load_from_wd_lite(sm);
-       }
+       if (mods != NULL && (error = submodule_read_config(sm, mods)) < 0)
+               goto out;
 
-       if (error == 0 && (error = submodule_update_index(sm)) == 0)
-               error = submodule_update_head(sm);
+       /* refresh wd data */
+       sm->flags &=
+               ~(GIT_SUBMODULE_STATUS_IN_WD |
+                 GIT_SUBMODULE_STATUS__WD_OID_VALID |
+                 GIT_SUBMODULE_STATUS__WD_FLAGS);
+
+       if ((error = submodule_load_from_wd_lite(sm)) < 0 ||
+           (error = submodule_update_index(sm)) < 0 ||
+           (error = submodule_update_head(sm)) < 0)
+               goto out;
 
+out:
+       git_config_free(mods);
        return error;
 }
 
@@ -1489,24 +1670,33 @@ 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) {
                /* git_submodule_open_bare will load WD OID data */
                if (git_submodule_open_bare(&smrepo, sm) < 0)
-                       giterr_clear();
+                       git_error_clear();
                else
                        git_repository_free(smrepo);
                smrepo = NULL;
        } else if (git_submodule_open(&smrepo, sm) < 0) {
-               giterr_clear();
+               git_error_clear();
                smrepo = NULL;
        }
 
@@ -1553,7 +1743,6 @@ int git_submodule_location(unsigned int *location, git_submodule *sm)
                location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
 }
 
-
 /*
  * INTERNAL FUNCTIONS
  */
@@ -1565,12 +1754,12 @@ static int submodule_alloc(
        git_submodule *sm;
 
        if (!name || !(namelen = strlen(name))) {
-               giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
+               git_error_set(GIT_ERROR_SUBMODULE, "invalid submodule name");
                return -1;
        }
 
        sm = git__calloc(1, sizeof(git_submodule));
-       GITERR_CHECK_ALLOC(sm);
+       GIT_ERROR_CHECK_ALLOC(sm);
 
        sm->name = sm->path = git__strdup(name);
        if (!sm->name) {
@@ -1616,8 +1805,8 @@ 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);
+       git_error_set(GIT_ERROR_INVALID,
+               "invalid value for submodule '%s' property: '%s'", property, value);
        return -1;
 }
 
@@ -1676,6 +1865,14 @@ static int get_value(const char **out, git_config *cfg, git_buf *buf, const char
        return error;
 }
 
+static bool looks_like_command_line_option(const char *s)
+{
+       if (s && s[0] == '-')
+               return true;
+
+       return false;
+}
+
 static int submodule_read_config(git_submodule *sm, git_config *cfg)
 {
        git_buf key = GIT_BUF_INIT;
@@ -1689,24 +1886,31 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg)
 
        if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) {
                in_config = 1;
+               /* We would warn here if we had that API */
+               if (!looks_like_command_line_option(value)) {
        /*
         * TODO: if case insensitive filesystem, then the following strcmp
         * should be strcasecmp
         */
-               if (strcmp(sm->name, value) != 0) {
-                       if (sm->path != sm->name)
-                               git__free(sm->path);
-                       sm->path = git__strdup(value);
-                       GITERR_CHECK_ALLOC(sm->path);
+                       if (strcmp(sm->name, value) != 0) {
+                               if (sm->path != sm->name)
+                                       git__free(sm->path);
+                               sm->path = git__strdup(value);
+                               GIT_ERROR_CHECK_ALLOC(sm->path);
+                       }
+
                }
        } else if (error != GIT_ENOTFOUND) {
                goto cleanup;
        }
 
        if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) {
-               in_config = 1;
-               sm->url = git__strdup(value);
-               GITERR_CHECK_ALLOC(sm->url);
+               /* We would warn here if we had that API */
+               if (!looks_like_command_line_option(value)) {
+                       in_config = 1;
+                       sm->url = git__strdup(value);
+                       GIT_ERROR_CHECK_ALLOC(sm->url);
+               }
        } else if (error != GIT_ENOTFOUND) {
                goto cleanup;
        }
@@ -1714,7 +1918,7 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg)
        if ((error = get_value(&value, cfg, &key, sm->name, "branch")) == 0) {
                in_config = 1;
                sm->branch = git__strdup(value);
-               GITERR_CHECK_ALLOC(sm->branch);
+               GIT_ERROR_CHECK_ALLOC(sm->branch);
        } else if (error != GIT_ENOTFOUND) {
                goto cleanup;
        }
@@ -1752,7 +1956,7 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg)
        error = 0;
 
 cleanup:
-       git_buf_free(&key);
+       git_buf_dispose(&key);
        return error;
 }
 
@@ -1760,11 +1964,10 @@ static int submodule_load_each(const git_config_entry *entry, void *payload)
 {
        lfc_data *data = payload;
        const char *namestart, *property;
-       git_strmap_iter pos;
        git_strmap *map = data->map;
        git_buf name = GIT_BUF_INIT;
        git_submodule *sm;
-       int error;
+       int error, isvalid;
 
        if (git__prefixcmp(entry->name, "submodule.") != 0)
                return 0;
@@ -1780,14 +1983,19 @@ static int submodule_load_each(const git_config_entry *entry, void *payload)
        if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0)
                return error;
 
+       isvalid = git_submodule_name_is_valid(data->repo, name.ptr, 0);
+       if (isvalid <= 0) {
+               error = isvalid;
+               goto done;
+       }
+
        /*
         * Now that we have the submodule's name, we can use that to
         * figure out whether it's in the map. If it's not, we create
         * a new submodule, load the config and insert it. If it's
         * already inserted, we've already loaded it, so we skip.
         */
-       pos = git_strmap_lookup_index(map, name.ptr);
-       if (git_strmap_valid_index(map, pos)) {
+       if (git_strmap_exists(map, name.ptr)) {
                error = 0;
                goto done;
        }
@@ -1800,15 +2008,13 @@ static int submodule_load_each(const git_config_entry *entry, void *payload)
                goto done;
        }
 
-       git_strmap_insert(map, sm->name, sm, error);
-       assert(error != 0);
-       if (error < 0)
+       if ((error = git_strmap_set(map, sm->name, sm)) < 0)
                goto done;
 
        error = 0;
 
 done:
-       git_buf_free(&name);
+       git_buf_dispose(&name);
        return error;
 }
 
@@ -1825,37 +2031,43 @@ static int submodule_load_from_wd_lite(git_submodule *sm)
        if (git_path_contains(&path, DOT_GIT))
                sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
 
-       git_buf_free(&path);
+       git_buf_dispose(&path);
        return 0;
 }
 
 /**
- * Returns a snapshot of $WORK_TREE/.gitmodules.
+ * Requests a snapshot of $WORK_TREE/.gitmodules.
  *
- * We ignore any errors and just pretend the file isn't there.
+ * Returns GIT_ENOTFOUND in case no .gitmodules file exist
  */
-static git_config *gitmodules_snapshot(git_repository *repo)
+static int gitmodules_snapshot(git_config **snap, git_repository *repo)
 {
        const char *workdir = git_repository_workdir(repo);
-       git_config *mods = NULL, *snap = NULL;
+       git_config *mods = NULL;
        git_buf path = GIT_BUF_INIT;
+       int error;
 
-       if (workdir != NULL) {
-               if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
-                       return NULL;
+       if (!workdir)
+               return GIT_ENOTFOUND;
 
-               if (git_config_open_ondisk(&mods, path.ptr) < 0)
-                       mods = NULL;
-       }
+       if ((error = git_buf_joinpath(&path, workdir, GIT_MODULES_FILE)) < 0)
+               return error;
+
+       if ((error = git_config_open_ondisk(&mods, path.ptr)) < 0)
+               goto cleanup;
+       git_buf_dispose(&path);
 
-       git_buf_free(&path);
+       if ((error = git_config_snapshot(snap, mods)) < 0)
+               goto cleanup;
+
+       error = 0;
 
-       if (mods) {
-               git_config_snapshot(&snap, mods);
+cleanup:
+       if (mods)
                git_config_free(mods);
-       }
+       git_buf_dispose(&path);
 
-       return snap;
+       return error;
 }
 
 static git_config_backend *open_gitmodules(
@@ -1871,18 +2083,18 @@ static git_config_backend *open_gitmodules(
                        return NULL;
 
                if (okay_to_create || git_path_isfile(path.ptr)) {
-                       /* git_config_file__ondisk should only fail if OOM */
-                       if (git_config_file__ondisk(&mods, path.ptr) < 0)
+                       /* git_config_backend_from_file should only fail if OOM */
+                       if (git_config_backend_from_file(&mods, path.ptr) < 0)
                                mods = NULL;
                        /* open should only fail here if the file is malformed */
-                       else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
-                               git_config_file_free(mods);
+                       else if (git_config_backend_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) {
+                               git_config_backend_free(mods);
                                mods = NULL;
                        }
                }
        }
 
-       git_buf_free(&path);
+       git_buf_dispose(&path);
 
        return mods;
 }
@@ -1904,7 +2116,7 @@ static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo)
         * a remote key for the local tracking branch HEAD points to.
         **/
        if (!git_reference_is_branch(head)) {
-               giterr_set(GITERR_INVALID,
+               git_error_set(GIT_ERROR_INVALID,
                        "HEAD does not refer to a branch.");
                error = GIT_ENOTFOUND;
                goto done;
@@ -1922,7 +2134,7 @@ static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo)
                goto done;
 
 done:
-       git_buf_free(&upstream_name);
+       git_buf_dispose(&upstream_name);
        git_reference_free(head);
 
        return error;
@@ -1938,7 +2150,7 @@ static int lookup_head_remote(git_remote **remote, git_repository *repo)
        if (!(error = lookup_head_remote_key(&remote_name, repo)))
                error = git_remote_lookup(remote, repo, remote_name.ptr);
 
-       git_buf_free(&remote_name);
+       git_buf_dispose(&remote_name);
 
        return error;
 }
@@ -1949,13 +2161,13 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo)
        int error = lookup_head_remote(remote, repo);
 
        /* if that failed, use 'origin' instead */
-       if (error == GIT_ENOTFOUND)
+       if (error == GIT_ENOTFOUND || error == GIT_EUNBORNBRANCH)
                error = git_remote_lookup(remote, repo, "origin");
 
        if (error == GIT_ENOTFOUND)
-               giterr_set(
-                       GITERR_SUBMODULE,
-                       "Cannot get default remote for submodule - no local tracking "
+               git_error_set(
+                       GIT_ERROR_SUBMODULE,
+                       "cannot get default remote for submodule - no local tracking "
                        "branch for HEAD and origin does not exist");
 
        return error;
@@ -1964,17 +2176,28 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo)
 static int get_url_base(git_buf *url, git_repository *repo)
 {
        int error;
+       git_worktree *wt = NULL;
        git_remote *remote = NULL;
 
-       if (!(error = lookup_default_remote(&remote, repo))) {
+       if ((error = lookup_default_remote(&remote, repo)) == 0) {
                error = git_buf_sets(url, git_remote_url(remote));
-               git_remote_free(remote);
-       }
-       else if (error == GIT_ENOTFOUND) {
-               /* if repository does not have a default remote, use workdir instead */
-               giterr_clear();
+               goto out;
+       } else if (error != GIT_ENOTFOUND)
+               goto out;
+       else
+               git_error_clear();
+
+       /* if repository does not have a default remote, use workdir instead */
+       if (git_repository_is_worktree(repo)) {
+               if ((error = git_worktree_open_from_repository(&wt, repo)) < 0)
+                       goto out;
+               error = git_buf_sets(url, wt->parent_path);
+       } else
                error = git_buf_sets(url, git_repository_workdir(repo));
-       }
+
+out:
+       git_remote_free(remote);
+       git_worktree_free(wt);
 
        return error;
 }
@@ -2043,11 +2266,11 @@ static void submodule_get_wd_status(
 
        /* if we don't have an unborn head, check diff with index */
        if (git_repository_head_tree(&sm_head, sm_repo) < 0)
-               giterr_clear();
+               git_error_clear();
        else {
                /* perform head to index diff on submodule */
                if (git_diff_tree_to_index(&diff, sm_repo, sm_head, index, &opt) < 0)
-                       giterr_clear();
+                       git_error_clear();
                else {
                        if (git_diff_num_deltas(diff) > 0)
                                *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
@@ -2060,7 +2283,7 @@ static void submodule_get_wd_status(
 
        /* perform index-to-workdir diff on submodule */
        if (git_diff_index_to_workdir(&diff, sm_repo, index, &opt) < 0)
-               giterr_clear();
+               git_error_clear();
        else {
                size_t untracked =
                        git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);