Merge branch 'sb/submodule-update-in-c'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Sep 2018 20:53:51 +0000 (13:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Sep 2018 20:53:51 +0000 (13:53 -0700)
"git submodule update" is getting rewritten piece-by-piece into C.

* sb/submodule-update-in-c:
submodule--helper: introduce new update-module-mode helper
submodule--helper: replace connect-gitdir-workingtree by ensure-core-worktree
builtin/submodule--helper: factor out method to update a single submodule
builtin/submodule--helper: store update_clone information in a struct
builtin/submodule--helper: factor out submodule updating
git-submodule.sh: rename unused variables
git-submodule.sh: align error reporting for update mode to use path

1  2 
builtin/submodule--helper.c
git-submodule.sh
index f6fb8991f3a81b0b2dd895c1e8550b5bda5ea99a,5c9d1fb496d6be8dd4bfa183dde84b01c77f6a51..40844870cfb158b014ee5ea10517c27349e09133
@@@ -331,7 -331,7 +331,7 @@@ static int module_list_compute(int argc
        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;
@@@ -542,7 -542,7 +542,7 @@@ static void runcommand_in_submodule_cb(
                argv_array_pushv(&cpr.args, info->argv);
  
                if (run_command(&cpr))
 -                      die(_("run_command returned non-zero status while"
 +                      die(_("run_command returned non-zero status while "
                                "recursing in the nested submodules of %s\n."),
                                displaypath);
        }
@@@ -1024,6 -1024,7 +1024,6 @@@ static void sync_submodule_cb(const str
  {
        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)
@@@ -1123,6 -1124,8 +1123,6 @@@ static void deinit_submodule(const cha
                if (!(flags & OPT_QUIET))
                        printf(format, displaypath);
  
 -              submodule_unset_core_worktree(sub);
 -
                strbuf_release(&sb_rm);
        }
  
@@@ -1443,6 -1446,72 +1443,72 @@@ static int module_clone(int argc, cons
        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) {
+               trace_printf("parsing 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) {
+               trace_printf("loaded thing");
+               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;
        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;
        /* 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, \
        NULL, NULL, NULL, \
-       STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
+       NULL, 0, 0, 0, NULL, 0, 0, 0}
  
  
  static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@@ -1569,11 -1641,12 +1638,12 @@@ static int prepare_to_clone_next_submod
        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;
@@@ -1714,11 -1787,44 +1784,44 @@@ static int git_update_clone_config(cons
        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(suc->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 (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;
  
                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")),
        };
        suc.prefix = prefix;
  
-       update_clone_config_from_gitmodules(&max_jobs);
-       git_config(git_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);
        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)
@@@ -1938,6 -2024,45 +2021,45 @@@ static int push_check(int argc, const c
        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 connect-gitdir-workingtree <name> <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, path))
+               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;
@@@ -2015,7 -2140,9 +2137,9 @@@ 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},
diff --combined git-submodule.sh
index 1cb2c0a31b416f9db979cb86badac5f958792453,19c9f1215e13826b7d24d4a06662c2060e9ca594..1b568e29b9a09af9fdecf19ddb75ffd2bdc6e094
@@@ -438,9 -438,6 +438,9 @@@ cmd_update(
                -q|--quiet)
                        GIT_QUIET=1
                        ;;
 +              -v)
 +                      GIT_QUIET=0
 +                      ;;
                --progress)
                        progress=1
                        ;;
                "$@" || echo "#unmatched" $?
        } | {
        err=
-       while read -r mode sha1 stage just_cloned sm_path
+       while read -r quickabort sha1 just_cloned sm_path
        do
-               die_if_unmatched "$mode" "$sha1"
+               die_if_unmatched "$quickabort" "$sha1"
  
-               name=$(git submodule--helper name "$sm_path") || exit
-               if ! test -z "$update"
-               then
-                       update_module=$update
-               else
-                       update_module=$(git config submodule."$name".update)
-                       if test -z "$update_module"
-                       then
-                               update_module="checkout"
-                       fi
-               fi
+               git submodule--helper ensure-core-worktree "$sm_path"
+               update_module=$(git submodule--helper update-module-mode $just_cloned "$sm_path" $update)
  
                displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
  
                if test $just_cloned -eq 1
                then
                        subsha1=
-                       case "$update_module" in
-                       merge | rebase | none)
-                               update_module=checkout ;;
-                       esac
                else
                        subsha1=$(sanitize_submodule_env; cd "$sm_path" &&
                                git rev-parse --verify HEAD) ||
                                must_die_on_failure=yes
                                ;;
                        *)
-                               die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
+                               die "$(eval_gettext "Invalid update mode '$update_module' for submodule path '$path'")"
                        esac
  
                        if (sanitize_submodule_env; cd "$sm_path" && $command "$sha1")