]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/submodule--helper.c
Sync with maint
[thirdparty/git.git] / builtin / submodule--helper.c
index cd75c7f61c874809661a51f138becb1d2a53baaa..c72931ecd74a15fe659c66d4795fe1a191ab90e0 100644 (file)
@@ -1,3 +1,4 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "builtin.h"
 #include "repository.h"
 #include "cache.h"
 #include "run-command.h"
 #include "remote.h"
 #include "refs.h"
+#include "refspec.h"
 #include "connect.h"
 #include "revision.h"
 #include "diffcore.h"
 #include "diff.h"
+#include "object-store.h"
 #include "dir.h"
+#include "advice.h"
 
 #define OPT_QUIET (1 << 0)
 #define OPT_CACHED (1 << 1)
@@ -54,7 +58,7 @@ static char *get_default_remote(void)
 
 static int print_default_remote(int argc, const char **argv, const char *prefix)
 {
-       const char *remote;
+       char *remote;
 
        if (argc != 1)
                die(_("submodule--helper print-default-remote takes no arguments"));
@@ -63,6 +67,7 @@ static int print_default_remote(int argc, const char **argv, const char *prefix)
        if (remote)
                printf("%s\n", remote);
 
+       free(remote);
        return 0;
 }
 
@@ -329,7 +334,7 @@ static int module_list_compute(int argc, const char **argv,
        for (i = 0; i < active_nr; i++) {
                const struct cache_entry *ce = active_cache[i];
 
-               if (!match_pathspec(pathspec, ce->name, ce_namelen(ce),
+               if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
                                    0, ps_matched, 1) ||
                    !S_ISGITLINK(ce->ce_mode))
                        continue;
@@ -345,7 +350,7 @@ static int module_list_compute(int argc, const char **argv,
                        i++;
        }
 
-       if (ps_matched && report_path_error(ps_matched, pathspec, prefix))
+       if (ps_matched && report_path_error(ps_matched, pathspec))
                result = -1;
 
        free(ps_matched);
@@ -421,7 +426,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
                const struct cache_entry *ce = list.entries[i];
 
                if (ce_stage(ce))
-                       printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
+                       printf("%06o %s U\t", ce->ce_mode, oid_to_hex(&null_oid));
                else
                        printf("%06o %s %d\t", ce->ce_mode,
                               oid_to_hex(&ce->oid), ce_stage(ce));
@@ -439,6 +444,170 @@ static void for_each_listed_submodule(const struct module_list *list,
                fn(list->entries[i], cb_data);
 }
 
+struct cb_foreach {
+       int argc;
+       const char **argv;
+       const char *prefix;
+       int quiet;
+       int recursive;
+};
+#define CB_FOREACH_INIT { 0 }
+
+static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
+                                      void *cb_data)
+{
+       struct cb_foreach *info = cb_data;
+       const char *path = list_item->name;
+       const struct object_id *ce_oid = &list_item->oid;
+
+       const struct submodule *sub;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       char *displaypath;
+
+       displaypath = get_submodule_displaypath(path, info->prefix);
+
+       sub = submodule_from_path(the_repository, &null_oid, path);
+
+       if (!sub)
+               die(_("No url found for submodule path '%s' in .gitmodules"),
+                       displaypath);
+
+       if (!is_submodule_populated_gently(path, NULL))
+               goto cleanup;
+
+       prepare_submodule_repo_env(&cp.env_array);
+
+       /*
+        * For the purpose of executing <command> in the submodule,
+        * separate shell is used for the purpose of running the
+        * child process.
+        */
+       cp.use_shell = 1;
+       cp.dir = path;
+
+       /*
+        * NEEDSWORK: the command currently has access to the variables $name,
+        * $sm_path, $displaypath, $sha1 and $toplevel only when the command
+        * contains a single argument. This is done for maintaining a faithful
+        * translation from shell script.
+        */
+       if (info->argc == 1) {
+               char *toplevel = xgetcwd();
+               struct strbuf sb = STRBUF_INIT;
+
+               argv_array_pushf(&cp.env_array, "name=%s", sub->name);
+               argv_array_pushf(&cp.env_array, "sm_path=%s", path);
+               argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath);
+               argv_array_pushf(&cp.env_array, "sha1=%s",
+                               oid_to_hex(ce_oid));
+               argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel);
+
+               /*
+                * Since the path variable was accessible from the script
+                * before porting, it is also made available after porting.
+                * The environment variable "PATH" has a very special purpose
+                * on windows. And since environment variables are
+                * case-insensitive in windows, it interferes with the
+                * existing PATH variable. Hence, to avoid that, we expose
+                * path via the args argv_array and not via env_array.
+                */
+               sq_quote_buf(&sb, path);
+               argv_array_pushf(&cp.args, "path=%s; %s",
+                                sb.buf, info->argv[0]);
+               strbuf_release(&sb);
+               free(toplevel);
+       } else {
+               argv_array_pushv(&cp.args, info->argv);
+       }
+
+       if (!info->quiet)
+               printf(_("Entering '%s'\n"), displaypath);
+
+       if (info->argv[0] && run_command(&cp))
+               die(_("run_command returned non-zero status for %s\n."),
+                       displaypath);
+
+       if (info->recursive) {
+               struct child_process cpr = CHILD_PROCESS_INIT;
+
+               cpr.git_cmd = 1;
+               cpr.dir = path;
+               prepare_submodule_repo_env(&cpr.env_array);
+
+               argv_array_pushl(&cpr.args, "--super-prefix", NULL);
+               argv_array_pushf(&cpr.args, "%s/", displaypath);
+               argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
+                               NULL);
+
+               if (info->quiet)
+                       argv_array_push(&cpr.args, "--quiet");
+
+               argv_array_push(&cpr.args, "--");
+               argv_array_pushv(&cpr.args, info->argv);
+
+               if (run_command(&cpr))
+                       die(_("run_command returned non-zero status while "
+                               "recursing in the nested submodules of %s\n."),
+                               displaypath);
+       }
+
+cleanup:
+       free(displaypath);
+}
+
+static int module_foreach(int argc, const char **argv, const char *prefix)
+{
+       struct cb_foreach info = CB_FOREACH_INIT;
+       struct pathspec pathspec;
+       struct module_list list = MODULE_LIST_INIT;
+
+       struct option module_foreach_options[] = {
+               OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")),
+               OPT_BOOL(0, "recursive", &info.recursive,
+                        N_("Recurse into nested submodules")),
+               OPT_END()
+       };
+
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule--helper foreach [--quiet] [--recursive] [--] <command>"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, prefix, module_foreach_options,
+                            git_submodule_helper_usage, 0);
+
+       if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
+               return 1;
+
+       info.argc = argc;
+       info.argv = argv;
+       info.prefix = prefix;
+
+       for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
+
+       return 0;
+}
+
+static char *compute_submodule_clone_url(const char *rel_url)
+{
+       char *remoteurl, *relurl;
+       char *remote = get_default_remote();
+       struct strbuf remotesb = STRBUF_INIT;
+
+       strbuf_addf(&remotesb, "remote.%s.url", remote);
+       if (git_config_get_string(remotesb.buf, &remoteurl)) {
+               warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
+               remoteurl = xgetcwd();
+       }
+       relurl = relative_url(remoteurl, rel_url, NULL);
+
+       free(remote);
+       free(remoteurl);
+       strbuf_release(&remotesb);
+
+       return relurl;
+}
+
 struct init_cb {
        const char *prefix;
        unsigned int flags;
@@ -455,7 +624,7 @@ static void init_submodule(const char *path, const char *prefix,
 
        displaypath = get_submodule_displaypath(path, prefix);
 
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
 
        if (!sub)
                die(_("No url found for submodule path '%s' in .gitmodules"),
@@ -489,21 +658,9 @@ static void init_submodule(const char *path, const char *prefix,
                /* Possibly a url relative to parent */
                if (starts_with_dot_dot_slash(url) ||
                    starts_with_dot_slash(url)) {
-                       char *remoteurl, *relurl;
-                       char *remote = get_default_remote();
-                       struct strbuf remotesb = STRBUF_INIT;
-                       strbuf_addf(&remotesb, "remote.%s.url", remote);
-                       free(remote);
-
-                       if (git_config_get_string(remotesb.buf, &remoteurl)) {
-                               warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
-                               remoteurl = xgetcwd();
-                       }
-                       relurl = relative_url(remoteurl, url, NULL);
-                       strbuf_release(&remotesb);
-                       free(remoteurl);
-                       free(url);
-                       url = relurl;
+                       char *oldurl = url;
+                       url = compute_submodule_clone_url(oldurl);
+                       free(oldurl);
                }
 
                if (git_config_set_gently(sb.buf, url))
@@ -555,7 +712,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
        };
 
        const char *const git_submodule_helper_usage[] = {
-               N_("git submodule--helper init [<path>]"),
+               N_("git submodule--helper init [<options>] [<path>]"),
                NULL
        };
 
@@ -596,8 +753,12 @@ static void print_status(unsigned int flags, char state, const char *path,
 
        printf("%c%s %s", state, oid_to_hex(oid), displaypath);
 
-       if (state == ' ' || state == '+')
-               printf(" (%s)", compute_rev_name(path, oid_to_hex(oid)));
+       if (state == ' ' || state == '+') {
+               const char *name = compute_rev_name(path, oid_to_hex(oid));
+
+               if (name)
+                       printf(" (%s)", name);
+       }
 
        printf("\n");
 }
@@ -622,7 +783,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
        struct rev_info rev;
        int diff_files_result;
 
-       if (!submodule_from_path(&null_oid, path))
+       if (!submodule_from_path(the_repository, &null_oid, path))
                die(_("no submodule mapping found in .gitmodules for path '%s'"),
                      path);
 
@@ -643,7 +804,8 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
                         path, NULL);
 
        git_config(git_diff_basic_config, NULL);
-       init_revisions(&rev, prefix);
+
+       repo_init_revisions(the_repository, &rev, NULL);
        rev.abbrev = 0;
        diff_files_args.argc = setup_revisions(diff_files_args.argc,
                                               diff_files_args.argv,
@@ -655,9 +817,13 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
                             displaypath);
        } else if (!(flags & OPT_CACHED)) {
                struct object_id oid;
+               struct ref_store *refs = get_submodule_ref_store(path);
 
-               if (refs_head_ref(get_submodule_ref_store(path),
-                                 handle_submodule_head_ref, &oid))
+               if (!refs) {
+                       print_status(flags, '-', path, ce_oid, displaypath);
+                       goto cleanup;
+               }
+               if (refs_head_ref(refs, handle_submodule_head_ref, &oid))
                        die(_("could not resolve HEAD ref inside the "
                              "submodule '%s'"), path);
 
@@ -742,7 +908,7 @@ static int module_name(int argc, const char **argv, const char *prefix)
        if (argc != 2)
                usage(_("git submodule--helper name <path>"));
 
-       sub = submodule_from_path(&null_oid, argv[1]);
+       sub = submodule_from_path(the_repository, &null_oid, argv[1]);
 
        if (!sub)
                die(_("no submodule mapping found in .gitmodules for path '%s'"),
@@ -773,7 +939,7 @@ static void sync_submodule(const char *path, const char *prefix,
        if (!is_submodule_active(the_repository, path))
                return;
 
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
 
        if (sub && sub->url) {
                if (starts_with_dot_dot_slash(sub->url) ||
@@ -871,7 +1037,6 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 {
        struct sync_cb *info = cb_data;
        sync_submodule(list_item->name, info->prefix, info->flags);
-
 }
 
 static int module_sync(int argc, const char **argv, const char *prefix)
@@ -926,7 +1091,7 @@ static void deinit_submodule(const char *path, const char *prefix,
        struct strbuf sb_config = STRBUF_INIT;
        char *sub_git_dir = xstrfmt("%s/.git", path);
 
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
 
        if (!sub || !sub->name)
                goto cleanup;
@@ -971,6 +1136,8 @@ static void deinit_submodule(const char *path, const char *prefix,
                if (!(flags & OPT_QUIET))
                        printf(format, displaypath);
 
+               submodule_unset_core_worktree(sub);
+
                strbuf_release(&sb_rm);
        }
 
@@ -1057,7 +1224,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
 }
 
 static int clone_submodule(const char *path, const char *gitdir, const char *url,
-                          const char *depth, struct string_list *reference,
+                          const char *depth, struct string_list *reference, int dissociate,
                           int quiet, int progress)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -1076,6 +1243,8 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
                        argv_array_pushl(&cp.args, "--reference",
                                         item->string, NULL);
        }
+       if (dissociate)
+               argv_array_push(&cp.args, "--dissociate");
        if (gitdir && *gitdir)
                argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
 
@@ -1102,20 +1271,28 @@ struct submodule_alternate_setup {
 #define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
        SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
 
+static const char alternate_error_advice[] = N_(
+"An alternate computed from a superproject's alternate is invalid.\n"
+"To allow Git to clone without an alternate in such a case, set\n"
+"submodule.alternateErrorStrategy to 'info' or, equivalently, clone with\n"
+"'--reference-if-able' instead of '--reference'."
+);
+
 static int add_possible_reference_from_superproject(
-               struct alternate_object_database *alt, void *sas_cb)
+               struct object_directory *odb, void *sas_cb)
 {
        struct submodule_alternate_setup *sas = sas_cb;
+       size_t len;
 
        /*
         * If the alternate object store is another repository, try the
         * standard layout with .git/(modules/<name>)+/objects
         */
-       if (ends_with(alt->path, "/objects")) {
+       if (strip_suffix(odb->path, "/objects", &len)) {
                char *sm_alternate;
                struct strbuf sb = STRBUF_INIT;
                struct strbuf err = STRBUF_INIT;
-               strbuf_add(&sb, alt->path, strlen(alt->path) - strlen("objects"));
+               strbuf_add(&sb, odb->path, len);
 
                /*
                 * We need to end the new path with '/' to mark it as a dir,
@@ -1123,7 +1300,7 @@ static int add_possible_reference_from_superproject(
                 * as the last part of a missing submodule reference would
                 * be taken as a file name.
                 */
-               strbuf_addf(&sb, "modules/%s/", sas->submodule_name);
+               strbuf_addf(&sb, "/modules/%s/", sas->submodule_name);
 
                sm_alternate = compute_alternate_path(sb.buf, &err);
                if (sm_alternate) {
@@ -1132,10 +1309,12 @@ static int add_possible_reference_from_superproject(
                } else {
                        switch (sas->error_mode) {
                        case SUBMODULE_ALTERNATE_ERROR_DIE:
+                               if (advice_submodule_alternate_error_strategy_die)
+                                       advise(_(alternate_error_advice));
                                die(_("submodule '%s' cannot add alternate: %s"),
                                    sas->submodule_name, err.buf);
                        case SUBMODULE_ALTERNATE_ERROR_INFO:
-                               fprintf(stderr, _("submodule '%s' cannot add alternate: %s"),
+                               fprintf_ln(stderr, _("submodule '%s' cannot add alternate: %s"),
                                        sas->submodule_name, err.buf);
                        case SUBMODULE_ALTERNATE_ERROR_IGNORE:
                                ; /* nothing */
@@ -1192,7 +1371,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
        char *p, *path = NULL, *sm_gitdir;
        struct strbuf sb = STRBUF_INIT;
        struct string_list reference = STRING_LIST_INIT_NODUP;
-       int require_init = 0;
+       int dissociate = 0, require_init = 0;
        char *sm_alternate = NULL, *error_strategy = NULL;
 
        struct option module_clone_options[] = {
@@ -1211,6 +1390,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
                OPT_STRING_LIST(0, "reference", &reference,
                           N_("repo"),
                           N_("reference repository")),
+               OPT_BOOL(0, "dissociate", &dissociate,
+                          N_("use --reference only while cloning")),
                OPT_STRING(0, "depth", &depth,
                           N_("string"),
                           N_("depth for shallow clones")),
@@ -1256,7 +1437,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
                prepare_possible_alternates(name, &reference);
 
-               if (clone_submodule(path, sm_gitdir, url, depth, &reference,
+               if (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate,
                                    quiet, progress))
                        die(_("clone of '%s' into submodule path '%s' failed"),
                            url, path);
@@ -1270,8 +1451,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
                strbuf_reset(&sb);
        }
 
-       /* Connect module worktree and git dir */
-       connect_work_tree_and_git_dir(path, sm_gitdir);
+       connect_work_tree_and_git_dir(path, sm_gitdir, 0);
 
        p = git_pathdup_submodule(path, "config");
        if (!p)
@@ -1297,6 +1477,72 @@ static int module_clone(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static void determine_submodule_update_strategy(struct repository *r,
+                                               int just_cloned,
+                                               const char *path,
+                                               const char *update,
+                                               struct submodule_update_strategy *out)
+{
+       const struct submodule *sub = submodule_from_path(r, &null_oid, path);
+       char *key;
+       const char *val;
+
+       key = xstrfmt("submodule.%s.update", sub->name);
+
+       if (update) {
+               if (parse_submodule_update_strategy(update, out) < 0)
+                       die(_("Invalid update mode '%s' for submodule path '%s'"),
+                               update, path);
+       } else if (!repo_config_get_string_const(r, key, &val)) {
+               if (parse_submodule_update_strategy(val, out) < 0)
+                       die(_("Invalid update mode '%s' configured for submodule path '%s'"),
+                               val, path);
+       } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
+               if (sub->update_strategy.type == SM_UPDATE_COMMAND)
+                       BUG("how did we read update = !command from .gitmodules?");
+               out->type = sub->update_strategy.type;
+               out->command = sub->update_strategy.command;
+       } else
+               out->type = SM_UPDATE_CHECKOUT;
+
+       if (just_cloned &&
+           (out->type == SM_UPDATE_MERGE ||
+            out->type == SM_UPDATE_REBASE ||
+            out->type == SM_UPDATE_NONE))
+               out->type = SM_UPDATE_CHECKOUT;
+
+       free(key);
+}
+
+static int module_update_module_mode(int argc, const char **argv, const char *prefix)
+{
+       const char *path, *update = NULL;
+       int just_cloned;
+       struct submodule_update_strategy update_strategy = { .type = SM_UPDATE_CHECKOUT };
+
+       if (argc < 3 || argc > 4)
+               die("submodule--helper update-module-clone expects <just-cloned> <path> [<update>]");
+
+       just_cloned = git_config_int("just_cloned", argv[1]);
+       path = argv[2];
+
+       if (argc == 4)
+               update = argv[3];
+
+       determine_submodule_update_strategy(the_repository,
+                                           just_cloned, path, update,
+                                           &update_strategy);
+       fputs(submodule_strategy_to_string(&update_strategy), stdout);
+
+       return 0;
+}
+
+struct update_clone_data {
+       const struct submodule *sub;
+       struct object_id oid;
+       unsigned just_cloned;
+};
+
 struct submodule_update_clone {
        /* index into 'list', the list of submodules to look into for cloning */
        int current;
@@ -1311,13 +1557,15 @@ struct submodule_update_clone {
        int quiet;
        int recommend_shallow;
        struct string_list references;
+       int dissociate;
        unsigned require_init;
        const char *depth;
        const char *recursive_prefix;
        const char *prefix;
 
-       /* Machine-readable status lines to be consumed by git-submodule.sh */
-       struct string_list projectlines;
+       /* to be consumed by git-submodule.sh */
+       struct update_clone_data *update_clone;
+       int update_clone_nr; int update_clone_alloc;
 
        /* If we want to stop as fast as possible and return an error */
        unsigned quickstop : 1;
@@ -1325,11 +1573,13 @@ struct submodule_update_clone {
        /* failed clones to be retried again */
        const struct cache_entry **failed_clones;
        int failed_clones_nr, failed_clones_alloc;
+
+       int max_jobs;
 };
 #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
-       SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
+       SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, 0, \
        NULL, NULL, NULL, \
-       STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
+       NULL, 0, 0, 0, NULL, 0, 0, 1}
 
 
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@ -1368,6 +1618,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
        struct strbuf sb = STRBUF_INIT;
        const char *displaypath = NULL;
        int needs_cloning = 0;
+       int need_free_url = 0;
 
        if (ce_stage(ce)) {
                if (suc->recursive_prefix)
@@ -1379,7 +1630,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                goto cleanup;
        }
 
-       sub = submodule_from_path(&null_oid, ce->name);
+       sub = submodule_from_path(the_repository, &null_oid, ce->name);
 
        if (suc->recursive_prefix)
                displaypath = relative_path(suc->recursive_prefix,
@@ -1416,18 +1667,25 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
 
        strbuf_reset(&sb);
        strbuf_addf(&sb, "submodule.%s.url", sub->name);
-       if (repo_config_get_string_const(the_repository, sb.buf, &url))
-               url = sub->url;
+       if (repo_config_get_string_const(the_repository, sb.buf, &url)) {
+               if (starts_with_dot_slash(sub->url) ||
+                   starts_with_dot_dot_slash(sub->url)) {
+                       url = compute_submodule_clone_url(sub->url);
+                       need_free_url = 1;
+               } else
+                       url = sub->url;
+       }
 
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/.git", ce->name);
        needs_cloning = !file_exists(sb.buf);
 
-       strbuf_reset(&sb);
-       strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
-                       oid_to_hex(&ce->oid), ce_stage(ce),
-                       needs_cloning, ce->name);
-       string_list_append(&suc->projectlines, sb.buf);
+       ALLOC_GROW(suc->update_clone, suc->update_clone_nr + 1,
+                  suc->update_clone_alloc);
+       oidcpy(&suc->update_clone[suc->update_clone_nr].oid, &ce->oid);
+       suc->update_clone[suc->update_clone_nr].just_cloned = needs_cloning;
+       suc->update_clone[suc->update_clone_nr].sub = sub;
+       suc->update_clone_nr++;
 
        if (!needs_cloning)
                goto cleanup;
@@ -1456,12 +1714,16 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                for_each_string_list_item(item, &suc->references)
                        argv_array_pushl(&child->args, "--reference", item->string, NULL);
        }
+       if (suc->dissociate)
+               argv_array_push(&child->args, "--dissociate");
        if (suc->depth)
                argv_array_push(&child->args, suc->depth);
 
 cleanup:
        strbuf_reset(&displaypath_sb);
        strbuf_reset(&sb);
+       if (need_free_url)
+               free((void*)url);
 
        return needs_cloning;
 }
@@ -1559,8 +1821,8 @@ static int update_clone_task_finished(int result,
        return 0;
 }
 
-static int gitmodules_update_clone_config(const char *var, const char *value,
-                                         void *cb)
+static int git_update_clone_config(const char *var, const char *value,
+                                  void *cb)
 {
        int *max_jobs = cb;
        if (!strcmp(var, "submodule.fetchjobs"))
@@ -1568,11 +1830,43 @@ static int gitmodules_update_clone_config(const char *var, const char *value,
        return 0;
 }
 
+static void update_submodule(struct update_clone_data *ucd)
+{
+       fprintf(stdout, "dummy %s %d\t%s\n",
+               oid_to_hex(&ucd->oid),
+               ucd->just_cloned,
+               ucd->sub->path);
+}
+
+static int update_submodules(struct submodule_update_clone *suc)
+{
+       int i;
+
+       run_processes_parallel_tr2(suc->max_jobs, update_clone_get_next_task,
+                                  update_clone_start_failure,
+                                  update_clone_task_finished, suc, "submodule",
+                                  "parallel/update");
+
+       /*
+        * We saved the output and put it out all at once now.
+        * That means:
+        * - the listener does not have to interleave their (checkout)
+        *   work with our fetching.  The writes involved in a
+        *   checkout involve more straightforward sequential I/O.
+        * - the listener can avoid doing any work if fetching failed.
+        */
+       if (suc->quickstop)
+               return 1;
+
+       for (i = 0; i < suc->update_clone_nr; i++)
+               update_submodule(&suc->update_clone[i]);
+
+       return 0;
+}
+
 static int update_clone(int argc, const char **argv, const char *prefix)
 {
        const char *update = NULL;
-       int max_jobs = 1;
-       struct string_list_item *item;
        struct pathspec pathspec;
        struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
 
@@ -1589,10 +1883,12 @@ static int update_clone(int argc, const char **argv, const char *prefix)
                           N_("rebase, merge, checkout or none")),
                OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"),
                           N_("reference repository")),
+               OPT_BOOL(0, "dissociate", &suc.dissociate,
+                          N_("use --reference only while cloning")),
                OPT_STRING(0, "depth", &suc.depth, "<depth>",
                           N_("Create a shallow clone truncated to the "
                              "specified number of revisions")),
-               OPT_INTEGER('j', "jobs", &max_jobs,
+               OPT_INTEGER('j', "jobs", &suc.max_jobs,
                            N_("parallel jobs")),
                OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
                            N_("whether the initial clone should follow the shallow recommendation")),
@@ -1605,13 +1901,13 @@ static int update_clone(int argc, const char **argv, const char *prefix)
        };
 
        const char *const git_submodule_helper_usage[] = {
-               N_("git submodule--helper update_clone [--prefix=<path>] [<path>...]"),
+               N_("git submodule--helper update-clone [--prefix=<path>] [<path>...]"),
                NULL
        };
        suc.prefix = prefix;
 
-       config_from_gitmodules(gitmodules_update_clone_config, &max_jobs);
-       git_config(gitmodules_update_clone_config, &max_jobs);
+       update_clone_config_from_gitmodules(&suc.max_jobs);
+       git_config(git_update_clone_config, &suc.max_jobs);
 
        argc = parse_options(argc, argv, prefix, module_update_clone_options,
                             git_submodule_helper_usage, 0);
@@ -1626,27 +1922,7 @@ static int update_clone(int argc, const char **argv, const char *prefix)
        if (pathspec.nr)
                suc.warn_if_uninitialized = 1;
 
-       run_processes_parallel(max_jobs,
-                              update_clone_get_next_task,
-                              update_clone_start_failure,
-                              update_clone_task_finished,
-                              &suc);
-
-       /*
-        * We saved the output and put it out all at once now.
-        * That means:
-        * - the listener does not have to interleave their (checkout)
-        *   work with our fetching.  The writes involved in a
-        *   checkout involve more straightforward sequential I/O.
-        * - the listener can avoid doing any work if fetching failed.
-        */
-       if (suc.quickstop)
-               return 1;
-
-       for_each_string_list_item(item, &suc.projectlines)
-               fprintf(stdout, "%s", item->string);
-
-       return 0;
+       return update_submodules(&suc);
 }
 
 static int resolve_relative_path(int argc, const char **argv, const char *prefix)
@@ -1666,7 +1942,7 @@ static const char *remote_submodule_branch(const char *path)
        const char *branch = NULL;
        char *key;
 
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
        if (!sub)
                return NULL;
 
@@ -1751,13 +2027,14 @@ static int push_check(int argc, const char **argv, const char *prefix)
 
        /* Check the refspec */
        if (argc > 2) {
-               int i, refspec_nr = argc - 2;
+               int i;
                struct ref *local_refs = get_local_heads();
-               struct refspec *refspec = parse_push_refspec(refspec_nr,
-                                                            argv + 2);
+               struct refspec refspec = REFSPEC_INIT_PUSH;
 
-               for (i = 0; i < refspec_nr; i++) {
-                       struct refspec *rs = refspec + i;
+               refspec_appendn(&refspec, argv + 2, argc - 2);
+
+               for (i = 0; i < refspec.nr; i++) {
+                       const struct refspec_item *rs = &refspec.items[i];
 
                        if (rs->pattern || rs->matching)
                                continue;
@@ -1784,13 +2061,52 @@ static int push_check(int argc, const char **argv, const char *prefix)
                                    rs->src);
                        }
                }
-               free_refspec(refspec_nr, refspec);
+               refspec_clear(&refspec);
        }
        free(head);
 
        return 0;
 }
 
+static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
+{
+       const struct submodule *sub;
+       const char *path;
+       char *cw;
+       struct repository subrepo;
+
+       if (argc != 2)
+               BUG("submodule--helper ensure-core-worktree <path>");
+
+       path = argv[1];
+
+       sub = submodule_from_path(the_repository, &null_oid, path);
+       if (!sub)
+               BUG("We could get the submodule handle before?");
+
+       if (repo_submodule_init(&subrepo, the_repository, sub))
+               die(_("could not get a repository handle for submodule '%s'"), path);
+
+       if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
+               char *cfg_file, *abs_path;
+               const char *rel_path;
+               struct strbuf sb = STRBUF_INIT;
+
+               cfg_file = repo_git_path(&subrepo, "config");
+
+               abs_path = absolute_pathdup(path);
+               rel_path = relative_path(abs_path, subrepo.gitdir, &sb);
+
+               git_config_set_in_file(cfg_file, "core.worktree", rel_path);
+
+               free(cfg_file);
+               free(abs_path);
+               strbuf_release(&sb);
+       }
+
+       return 0;
+}
+
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -1808,7 +2124,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
        };
 
        const char *const git_submodule_helper_usage[] = {
-               N_("git submodule--helper embed-git-dir [<path>...]"),
+               N_("git submodule--helper absorb-git-dirs [<options>] [<path>...]"),
                NULL
        };
 
@@ -1819,8 +2135,7 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
                return 1;
 
        for (i = 0; i < list.nr; i++)
-               absorb_git_dir_into_superproject(prefix,
-                               list.entries[i]->name, flags);
+               absorb_git_dir_into_superproject(list.entries[i]->name, flags);
 
        return 0;
 }
@@ -1856,6 +2171,52 @@ static int check_name(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static int module_config(int argc, const char **argv, const char *prefix)
+{
+       enum {
+               CHECK_WRITEABLE = 1,
+               DO_UNSET = 2
+       } command = 0;
+
+       struct option module_config_options[] = {
+               OPT_CMDMODE(0, "check-writeable", &command,
+                           N_("check if it is safe to write to the .gitmodules file"),
+                           CHECK_WRITEABLE),
+               OPT_CMDMODE(0, "unset", &command,
+                           N_("unset the config in the .gitmodules file"),
+                           DO_UNSET),
+               OPT_END()
+       };
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule--helper config <name> [<value>]"),
+               N_("git submodule--helper config --unset <name>"),
+               N_("git submodule--helper config --check-writeable"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, prefix, module_config_options,
+                            git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);
+
+       if (argc == 1 && command == CHECK_WRITEABLE)
+               return is_writing_gitmodules_ok() ? 0 : -1;
+
+       /* Equivalent to ACTION_GET in builtin/config.c */
+       if (argc == 2 && command != DO_UNSET)
+               return print_config_from_gitmodules(the_repository, argv[1]);
+
+       /* Equivalent to ACTION_SET in builtin/config.c */
+       if (argc == 3 || (argc == 2 && command == DO_UNSET)) {
+               const char *value = (argc == 3) ? argv[2] : NULL;
+
+               if (!is_writing_gitmodules_ok())
+                       die(_("please make sure that the .gitmodules file is in the working tree"));
+
+               return config_set_in_gitmodules_file_gently(argv[1], value);
+       }
+
+       usage_with_options(git_submodule_helper_usage, module_config_options);
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -1868,10 +2229,13 @@ static struct cmd_struct commands[] = {
        {"list", module_list, 0},
        {"name", module_name, 0},
        {"clone", module_clone, 0},
+       {"update-module-mode", module_update_module_mode, 0},
        {"update-clone", update_clone, 0},
+       {"ensure-core-worktree", ensure_core_worktree, 0},
        {"relative-path", resolve_relative_path, 0},
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
+       {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
        {"init", module_init, SUPPORT_SUPER_PREFIX},
        {"status", module_status, SUPPORT_SUPER_PREFIX},
        {"print-default-remote", print_default_remote, 0},
@@ -1882,6 +2246,7 @@ static struct cmd_struct commands[] = {
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
        {"is-active", is_active, 0},
        {"check-name", check_name, 0},
+       {"config", module_config, 0},
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)