mbox series

[v6,00/16] submodule: convert the rest of 'update' to C

Message ID 20220208083952.35036-1-chooglen@google.com (mailing list archive)
Headers show
Series submodule: convert the rest of 'update' to C | expand

Message

Glen Choo Feb. 8, 2022, 8:39 a.m. UTC
Here is a split-up of the last patch as I promised :) The intent was to
minimize the diff vs v5 instead of getting to a perfect end state; this
will almost certainly need a reroll.

This split-up isn't as complete as I had hoped - the last patch is still
rather large, but breaking that up already took more time than
everything else combined, and I think this organization is already
easier to review than v5. I'd appreciate any suggestions on how this
could be done [1] :) If inspiration strikes, I'll break it up further in
a reroll, but as of right now, I'm out of ideas.

I did some minimal review of the implementation, but I plan to do more
after sending this to the list e.g. I have not revisited the earlier
preparatory patches to see if my patches make sense with them. Where I
found issues, I left NEEDSWORK comments instead of fixing them because
the result would be easier to diff with v5 (the only differences between
this tree and v5's is the NEEDSWORK comments). I plan to fix
implementation issues (once we've spotted them in reivew) and the
NEEDSWORKs in the next reroll.

Let me know if the trailers make sense - this is my first time reworking
someone else's changes, so I don't think I did a perfect job at
attributing work. I also had to fiddle with the Signed-off-by to make
them resemble the v5 patches' Signed-off-by (ar/submodule-update seemed
to order them differently from v5, I'm not sure why).

[1] The difficulty in the last patch comes from the fact that we have
the --recursive flag handling in shell, and we are piping update-clone
into {run-update-procedure + --recursive flag handling}. Because of how
"git submodule" parses args, it is tricky to implement --recursive
without bypassing "git submodule" with "git submodule--helper update"
(the final patch does this bypass). On the other hand, it's difficult to
leave --recursive for last, because we would need to mimic the piping
and exiting behavior around update-clone and run-update-procedure.

Changes in v6:
- Split up last patch of v5 into patches 9-16.
- Patches 9-10 are preparatory work on submodule--helper.c.
- Patches 11-14 contain sh -> c conversions of the git-submodule.sh code
  surrounding update-clone and run-update-procedure.
- Patch 15 moves functions around in order to shrink patch 16's diff
  - If reviewers prefer, we could choose _not_ to move the functions
    around. If so, we can drop patch 15.
- Patch 16 introduces "submodule--helper update". Change the commit
  message to drop references to work done in previous patches.
- Add NEEDSWORK comments for potential issues. These will be cleaned up
  in the next version.

Atharva Raykar (6):
  submodule--helper: get remote names from any repository
  submodule--helper: refactor get_submodule_displaypath()
  submodule--helper: allow setting superprefix for init_submodule()
  submodule--helper: run update using child process struct
  builtin/submodule--helper.c: reformat designated initializers
  submodule: move core cmd_update() logic to C

Glen Choo (7):
  submodule--helper: remove update-module-mode
  submodule--helper: reorganize code for sh to C conversion
  submodule--helper run-update-procedure: remove --suboid
  submodule--helper run-update-procedure: learn --remote
  submodule--helper: remove ensure-core-worktree
  submodule--helper update-clone: learn --init
  submodule--helper: move functions around

Ævar Arnfjörð Bjarmason (3):
  builtin/submodule--helper.c: rename option variables to "opt"
  submodule--helper: don't use bitfield indirection for parse_options()
  submodule tests: test for init and update failure output

 builtin/submodule--helper.c    | 709 +++++++++++++++++++--------------
 git-submodule.sh               | 145 +------
 t/t7406-submodule-update.sh    |  14 +-
 t/t7408-submodule-reference.sh |  14 +-
 4 files changed, 447 insertions(+), 435 deletions(-)

Range-diff against v5:
 1:  b45e49810a =  1:  86ffb53742 submodule--helper: get remote names from any repository
 2:  8974329fc5 =  2:  2a40266b7a submodule--helper: refactor get_submodule_displaypath()
 3:  bd92e00c05 =  3:  cd851c8eb5 submodule--helper: allow setting superprefix for init_submodule()
 4:  909cb60cd4 =  4:  bfe5cad136 submodule--helper: run update using child process struct
 5:  36575b2f25 =  5:  72c257fdbf builtin/submodule--helper.c: reformat designated initializers
 6:  a743003e09 =  6:  4b5f703fde builtin/submodule--helper.c: rename option variables to "opt"
 7:  3a449d00b9 =  7:  f1d21f5b1c submodule--helper: don't use bitfield indirection for parse_options()
 8:  7da45dde67 =  8:  9d32a73fc3 submodule tests: test for init and update failure output
 -:  ---------- >  9:  087bf43aba submodule--helper: remove update-module-mode
 -:  ---------- > 10:  4eb2893a19 submodule--helper: reorganize code for sh to C conversion
 -:  ---------- > 11:  c08e7781e3 submodule--helper run-update-procedure: remove --suboid
 -:  ---------- > 12:  2419c37184 submodule--helper run-update-procedure: learn --remote
 -:  ---------- > 13:  6691fd3648 submodule--helper: remove ensure-core-worktree
 -:  ---------- > 14:  d2c9c356e9 submodule--helper update-clone: learn --init
 -:  ---------- > 15:  a5cde5e084 submodule--helper: move functions around
 9:  251a0b4241 ! 16:  f0551a37e5 submodule: move core cmd_update() logic to C
    @@ Commit message
         `submodule--helper update`, with a modified `--recursive-prefix` and
         `--prefix` parameter.
     
    -    We also introduce `update_submodules2()` and `update_submodule2()`
    -    which will supersede `update_submodules()` and `update_submodule()`.
    -
    -    When the `--init` flag is passed to the subcommand, we do not spawn a
    -    new subprocess and call `submodule--helper init` on the submodule paths,
    -    because the Git machinery is not able to pick up the configuration
    -    changes introduced by that init call[1]. So we instead run the
    -    `init_submodule_cb()` callback over each submodule in the same process.
    -
    -    While we are at it, we also remove the fetch_in_submodule() shell
    -    function since it is no longer used anywhere.
    -
    -    [1] https://lore.kernel.org/git/CAP8UFD0NCQ5w_3GtT_xHr35i7h8BuLX4UcHNY6VHPGREmDVObA@mail.gmail.com/
    -
         Mentored-by: Christian Couder <christian.couder@gmail.com>
         Mentored-by: Shourya Shukla <periperidip@gmail.com>
         Signed-off-by: Atharva Raykar <raykar.ath@gmail.com>
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
         Signed-off-by: Junio C Hamano <gitster@pobox.com>
    +    Signed-off-by: Glen Choo <chooglen@google.com>
     
      ## builtin/submodule--helper.c ##
    -@@ builtin/submodule--helper.c: static char *get_default_remote(void)
    - 	return repo_get_default_remote(the_repository);
    - }
    - 
    --static int print_default_remote(int argc, const char **argv, const char *prefix)
    --{
    --	char *remote;
    --
    --	if (argc != 1)
    --		die(_("submodule--helper print-default-remote takes no arguments"));
    --
    --	remote = get_default_remote();
    --	if (remote)
    --		printf("%s\n", remote);
    --
    --	free(remote);
    --	return 0;
    --}
    --
    - static int starts_with_dot_slash(const char *str)
    - {
    - 	return str[0] == '.' && is_dir_sep(str[1]);
    -@@ builtin/submodule--helper.c: static void determine_submodule_update_strategy(struct repository *r,
    - 	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;
     @@ builtin/submodule--helper.c: struct submodule_update_clone {
      	const char *prefix;
      	int single_branch;
    @@ builtin/submodule--helper.c: struct submodule_update_clone {
      	struct update_clone_data *update_clone;
      	int update_clone_nr; int update_clone_alloc;
      
    +@@ builtin/submodule--helper.c: struct submodule_update_clone {
    + 	int failed_clones_nr, failed_clones_alloc;
    + 
    + 	int max_jobs;
    +-	unsigned int init;
    + };
    + #define SUBMODULE_UPDATE_CLONE_INIT { \
    + 	.list = MODULE_LIST_INIT, \
     @@ builtin/submodule--helper.c: struct submodule_update_clone {
      }
      
    @@ builtin/submodule--helper.c: struct update_data {
      	unsigned int quiet;
      	unsigned int nofetch;
      	unsigned int just_cloned;
    -+	unsigned int remote;
    + 	unsigned int remote;
     +	unsigned int recursive;
     +	unsigned int progress;
     +	unsigned int dissociate;
    @@ builtin/submodule--helper.c: static int run_update_command(struct update_data *u
      	char *oid = oid_to_hex(&ud->oid);
      	int must_die_on_failure = 0;
     +	struct submodule_update_strategy strategy = SUBMODULE_UPDATE_STRATEGY_INIT;
    - 
    --	switch (ud->update_strategy.type) {
    ++
     +	if (ud->update_strategy.type == SM_UPDATE_UNSPECIFIED || ud->just_cloned)
     +		determine_submodule_update_strategy(the_repository, ud->just_cloned,
     +						    ud->sm_path, NULL, &strategy);
     +	else
     +		strategy = ud->update_strategy;
    -+
    + 
    +-	switch (ud->update_strategy.type) {
     +	switch (strategy.type) {
      	case SM_UPDATE_CHECKOUT:
      		cp.git_cmd = 1;
    @@ builtin/submodule--helper.c: static int do_run_update_procedure(struct update_da
     -		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;
    --	struct pathspec pathspec;
    --	struct submodule_update_clone opt = SUBMODULE_UPDATE_CLONE_INIT;
    --
    --	struct option module_update_clone_options[] = {
    --		OPT_STRING(0, "prefix", &prefix,
    --			   N_("path"),
    --			   N_("path into the working tree")),
    --		OPT_STRING(0, "recursive-prefix", &opt.recursive_prefix,
    --			   N_("path"),
    --			   N_("path into the working tree, across nested "
    --			      "submodule boundaries")),
    --		OPT_STRING(0, "update", &update,
    --			   N_("string"),
    --			   N_("rebase, merge, checkout or none")),
    --		OPT_STRING_LIST(0, "reference", &opt.references, N_("repo"),
    --			   N_("reference repository")),
    --		OPT_BOOL(0, "dissociate", &opt.dissociate,
    --			   N_("use --reference only while cloning")),
    --		OPT_STRING(0, "depth", &opt.depth, "<depth>",
    --			   N_("create a shallow clone truncated to the "
    --			      "specified number of revisions")),
    --		OPT_INTEGER('j', "jobs", &opt.max_jobs,
    --			    N_("parallel jobs")),
    --		OPT_BOOL(0, "recommend-shallow", &opt.recommend_shallow,
    --			    N_("whether the initial clone should follow the shallow recommendation")),
    --		OPT__QUIET(&opt.quiet, N_("don't print cloning progress")),
    --		OPT_BOOL(0, "progress", &opt.progress,
    --			    N_("force cloning progress")),
    --		OPT_BOOL(0, "require-init", &opt.require_init,
    --			   N_("disallow cloning into non-empty directory")),
    --		OPT_BOOL(0, "single-branch", &opt.single_branch,
    --			 N_("clone only one branch, HEAD or --branch")),
    --		OPT_END()
    --	};
    --
    --	const char *const git_submodule_helper_usage[] = {
    --		N_("git submodule--helper update-clone [--prefix=<path>] [<path>...]"),
    --		NULL
    --	};
    --	opt.prefix = prefix;
    --
    --	update_clone_config_from_gitmodules(&opt.max_jobs);
    --	git_config(git_update_clone_config, &opt.max_jobs);
    --
    --	argc = parse_options(argc, argv, prefix, module_update_clone_options,
    --			     git_submodule_helper_usage, 0);
    --
    --	if (update)
    --		if (parse_submodule_update_strategy(update, &opt.update) < 0)
    --			die(_("bad value for update parameter"));
    --
    --	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0)
    --		return 1;
    --
    --	if (pathspec.nr)
    --		opt.warn_if_uninitialized = 1;
    --
    --	return update_submodules(&opt);
    --}
    --
    +-/*
    +- * NEEDSWORK: Use a forward declaration to avoid moving
    +- * run_update_procedure() (which will be removed soon).
    +- */
    +-static int update_submodule2(struct update_data *update_data);
     -static int run_update_procedure(int argc, const char **argv, const char *prefix)
     -{
     -	char *prefixed_path, *update = NULL;
    @@ builtin/submodule--helper.c: static int do_run_update_procedure(struct update_da
     -		OPT_CALLBACK_F(0, "oid", &opt.oid, N_("sha1"),
     -			       N_("SHA1 expected by superproject"), PARSE_OPT_NONEG,
     -			       parse_opt_object_id),
    --		OPT_CALLBACK_F(0, "suboid", &opt.suboid, N_("subsha1"),
    --			       N_("SHA1 of submodule's HEAD"), PARSE_OPT_NONEG,
    --			       parse_opt_object_id),
    +-		OPT_BOOL(0, "remote", &opt.remote,
    +-			 N_("use SHA-1 of submodule's remote tracking branch")),
     -		OPT_END()
     -	};
     -
    @@ builtin/submodule--helper.c: static int do_run_update_procedure(struct update_da
     -					    &opt.update_strategy);
     -
     -	free(prefixed_path);
    --
    --	if (!oideq(&opt.oid, &opt.suboid) || opt.force)
    --		return do_run_update_procedure(&opt);
    --
    --	return 3;
    +-	return update_submodule2(&opt);
     -}
     -
     -static int resolve_relative_path(int argc, const char **argv, const char *prefix)
    @@ builtin/submodule--helper.c: static int do_run_update_procedure(struct update_da
      static const char *remote_submodule_branch(const char *path)
      {
      	const struct submodule *sub;
    -@@ builtin/submodule--helper.c: static int push_check(int argc, const char **argv, const char *prefix)
    - 	return 0;
    - }
    - 
    --static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
    -+static void ensure_core_worktree(const char *path)
    - {
    --	const char *path;
    - 	const char *cw;
    - 	struct repository subrepo;
    - 
    --	if (argc != 2)
    --		BUG("submodule--helper ensure-core-worktree <path>");
    --
    --	path = argv[1];
    --
    - 	if (repo_submodule_init(&subrepo, the_repository, path, null_oid()))
    - 		die(_("could not get a repository handle for submodule '%s'"), path);
    - 
    -@@ builtin/submodule--helper.c: static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
    - 		free(abs_path);
    - 		strbuf_release(&sb);
    - 	}
    --
    --	return 0;
    - }
    - 
    - static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
     @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char **argv, const char *prefix)
      	return !!ret;
      }
      
    +-/* NEEDSWORK: this is a temporary name until we delete update_submodule() */
    +-static int update_submodule2(struct update_data *update_data)
     +static void update_data_to_args(struct update_data *update_data, struct strvec *args)
     +{
     +	const char *update = submodule_strategy_to_string(&update_data->update_strategy);
    @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *
     +}
     +
     +static int update_submodule(struct update_data *update_data)
    -+{
    + {
     +	char *prefixed_path;
     +
    -+	ensure_core_worktree(update_data->sm_path);
    -+
    + 	ensure_core_worktree(update_data->sm_path);
    + 
     +	if (update_data->recursive_prefix)
     +		prefixed_path = xstrfmt("%s%s", update_data->recursive_prefix,
     +					update_data->sm_path);
    @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *
     +							     update_data->prefix);
     +	free(prefixed_path);
     +
    -+	if (update_data->just_cloned) {
    -+		oidcpy(&update_data->suboid, null_oid());
    -+	} else {
    -+		if (resolve_gitlink_ref(update_data->sm_path, "HEAD", &update_data->suboid))
    -+			die(_("Unable to find current revision in submodule path '%s'"),
    -+			    update_data->displaypath);
    -+	}
    -+
    -+	if (update_data->remote) {
    -+		char *remote_name = get_default_remote_submodule(update_data->sm_path);
    -+		const char *branch = remote_submodule_branch(update_data->sm_path);
    -+		char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
    -+
    -+		if (!update_data->nofetch) {
    -+			if (fetch_in_submodule(update_data->sm_path, update_data->depth,
    -+					      0, NULL))
    -+				die(_("Unable to fetch in submodule path '%s'"),
    -+				    update_data->sm_path);
    -+		}
    -+
    -+		if (resolve_gitlink_ref(update_data->sm_path, remote_ref, &update_data->oid))
    -+			die(_("Unable to find %s revision in submodule path '%s'"),
    -+			    remote_ref, update_data->sm_path);
    -+
    -+		free(remote_ref);
    -+	}
    -+
    -+	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)
    + 	/* NEEDSWORK: fix the style issues e.g. braces */
    + 	if (update_data->just_cloned) {
    + 		oidcpy(&update_data->suboid, null_oid());
    +@@ builtin/submodule--helper.c: static int update_submodule2(struct update_data *update_data)
    + 	}
    + 
    + 	if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)
    +-		return do_run_update_procedure(update_data);
     +		if (run_update_procedure(update_data))
     +			return 1;
    -+
    + 
    +-	return 3;
     +	if (update_data->recursive) {
     +		struct child_process cp = CHILD_PROCESS_INIT;
     +		struct update_data next = *update_data;
    @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *
     +	}
     +
     +	return 0;
    -+}
    -+
    + }
    + 
    +-static int update_submodules(struct submodule_update_clone *suc)
     +static int update_submodules(struct update_data *update_data)
    -+{
    + {
    +-	int i;
     +	int i, res = 0;
     +	struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
    -+
    + 
    +-	run_processes_parallel_tr2(suc->max_jobs, update_clone_get_next_task,
     +	update_clone_from_update_data(&suc, update_data);
     +	run_processes_parallel_tr2(suc.max_jobs, update_clone_get_next_task,
    -+				   update_clone_start_failure,
    + 				   update_clone_start_failure,
    +-				   update_clone_task_finished, suc, "submodule",
     +				   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.
    -+	 */
    + 				   "parallel/update");
    + 
    + 	/*
    +@@ builtin/submodule--helper.c: static int update_submodules(struct submodule_update_clone *suc)
    + 	 *   checkout involve more straightforward sequential I/O.
    + 	 * - the listener can avoid doing any work if fetching failed.
    + 	 */
    +-	if (suc->quickstop)
    +-		return 1;
     +	if (suc.quickstop) {
     +		res = 1;
     +		goto cleanup;
     +	}
    -+
    + 
    +-	for (i = 0; i < suc->update_clone_nr; i++)
    +-		update_submodule(&suc->update_clone[i]);
     +	for (i = 0; i < suc.update_clone_nr; i++) {
     +		struct update_clone_data ucd = suc.update_clone[i];
    -+
    + 
    +-	return 0;
     +		oidcpy(&update_data->oid, &ucd.oid);
     +		update_data->just_cloned = ucd.just_cloned;
     +		update_data->sm_path = ucd.sub->path;
    @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *
     +cleanup:
     +	string_list_clear(&update_data->references, 0);
     +	return res;
    -+}
    -+
    + }
    + 
    +-static int update_clone(int argc, const char **argv, const char *prefix)
     +static int module_update(int argc, const char **argv, const char *prefix)
    -+{
    -+	const char *update = NULL;
    -+	struct pathspec pathspec;
    + {
    + 	const char *update = NULL;
    + 	struct pathspec pathspec;
    +-	struct submodule_update_clone opt = SUBMODULE_UPDATE_CLONE_INIT;
     +	struct update_data opt = UPDATE_DATA_INIT;
    -+
    -+	struct option module_update_clone_options[] = {
    + 
    ++	/* NEEDSWORK: update names and strings */
    + 	struct option module_update_clone_options[] = {
     +		OPT__FORCE(&opt.force, N_("force checkout updates"), 0),
    -+		OPT_BOOL(0, "init", &opt.init,
    -+			 N_("initialize uninitialized submodules before update")),
    + 		OPT_BOOL(0, "init", &opt.init,
    + 			 N_("initialize uninitialized submodules before update")),
    +-		OPT_STRING(0, "prefix", &prefix,
     +		OPT_BOOL(0, "remote", &opt.remote,
     +			 N_("use SHA-1 of submodule's remote tracking branch")),
     +		OPT_BOOL(0, "recursive", &opt.recursive,
    @@ builtin/submodule--helper.c: static int module_set_branch(int argc, const char *
     +		OPT_BOOL('N', "no-fetch", &opt.nofetch,
     +			 N_("don't fetch new objects from the remote site")),
     +		OPT_STRING(0, "prefix", &opt.prefix,
    -+			   N_("path"),
    -+			   N_("path into the working tree")),
    -+		OPT_STRING(0, "recursive-prefix", &opt.recursive_prefix,
    -+			   N_("path"),
    -+			   N_("path into the working tree, across nested "
    -+			      "submodule boundaries")),
    -+		OPT_STRING(0, "update", &update,
    -+			   N_("string"),
    -+			   N_("rebase, merge, checkout or none")),
    -+		OPT_STRING_LIST(0, "reference", &opt.references, N_("repo"),
    + 			   N_("path"),
    + 			   N_("path into the working tree")),
    + 		OPT_STRING(0, "recursive-prefix", &opt.recursive_prefix,
    +@@ builtin/submodule--helper.c: static int update_clone(int argc, const char **argv, const char *prefix)
    + 			   N_("string"),
    + 			   N_("rebase, merge, checkout or none")),
    + 		OPT_STRING_LIST(0, "reference", &opt.references, N_("repo"),
    +-			   N_("reference repository")),
     +				N_("reference repository")),
    -+		OPT_BOOL(0, "dissociate", &opt.dissociate,
    + 		OPT_BOOL(0, "dissociate", &opt.dissociate,
    +-			   N_("use --reference only while cloning")),
    +-		OPT_STRING(0, "depth", &opt.depth, "<depth>",
    +-			   N_("create a shallow clone truncated to the "
    +-			      "specified number of revisions")),
     +			 N_("use --reference only while cloning")),
     +		OPT_INTEGER(0, "depth", &opt.depth,
     +			    N_("create a shallow clone truncated to the "
     +			       "specified number of revisions")),
    -+		OPT_INTEGER('j', "jobs", &opt.max_jobs,
    -+			    N_("parallel jobs")),
    -+		OPT_BOOL(0, "recommend-shallow", &opt.recommend_shallow,
    + 		OPT_INTEGER('j', "jobs", &opt.max_jobs,
    + 			    N_("parallel jobs")),
    + 		OPT_BOOL(0, "recommend-shallow", &opt.recommend_shallow,
    +-			    N_("whether the initial clone should follow the shallow recommendation")),
     +			 N_("whether the initial clone should follow the shallow recommendation")),
    -+		OPT__QUIET(&opt.quiet, N_("don't print cloning progress")),
    -+		OPT_BOOL(0, "progress", &opt.progress,
    + 		OPT__QUIET(&opt.quiet, N_("don't print cloning progress")),
    + 		OPT_BOOL(0, "progress", &opt.progress,
    +-			    N_("force cloning progress")),
     +			 N_("force cloning progress")),
    -+		OPT_BOOL(0, "require-init", &opt.require_init,
    + 		OPT_BOOL(0, "require-init", &opt.require_init,
    +-			   N_("disallow cloning into non-empty directory")),
     +			 N_("disallow cloning into non-empty directory")),
    -+		OPT_BOOL(0, "single-branch", &opt.single_branch,
    -+			 N_("clone only one branch, HEAD or --branch")),
    -+		OPT_END()
    -+	};
    -+
    -+	const char *const git_submodule_helper_usage[] = {
    -+		N_("git submodule--helper update-clone [--prefix=<path>] [<path>...]"),
    -+		NULL
    -+	};
    -+
    -+	update_clone_config_from_gitmodules(&opt.max_jobs);
    -+	git_config(git_update_clone_config, &opt.max_jobs);
    -+
    -+	argc = parse_options(argc, argv, prefix, module_update_clone_options,
    -+			     git_submodule_helper_usage, 0);
    + 		OPT_BOOL(0, "single-branch", &opt.single_branch,
    + 			 N_("clone only one branch, HEAD or --branch")),
    + 		OPT_END()
    +@@ builtin/submodule--helper.c: static int update_clone(int argc, const char **argv, const char *prefix)
    + 		N_("git submodule--helper update-clone [--prefix=<path>] [<path>...]"),
    + 		NULL
    + 	};
    +-	opt.prefix = prefix;
    + 
    + 	update_clone_config_from_gitmodules(&opt.max_jobs);
    + 	git_config(git_update_clone_config, &opt.max_jobs);
    + 
    + 	argc = parse_options(argc, argv, prefix, module_update_clone_options,
    + 			     git_submodule_helper_usage, 0);
     +	oidcpy(&opt.oid, null_oid());
     +	oidcpy(&opt.suboid, null_oid());
    -+
    -+	if (update)
    + 
    + 	if (update)
    +-		if (parse_submodule_update_strategy(update, &opt.update) < 0)
     +		if (parse_submodule_update_strategy(update,
     +						    &opt.update_strategy) < 0)
    -+			die(_("bad value for update parameter"));
    -+
    -+	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0)
    -+		return 1;
    -+
    -+	if (pathspec.nr)
    -+		opt.warn_if_uninitialized = 1;
    -+
    -+	if (opt.init) {
    -+		struct module_list list = MODULE_LIST_INIT;
    -+		struct init_cb info = INIT_CB_INIT;
    -+
    -+		if (module_list_compute(argc, argv, opt.prefix,
    -+					&pathspec, &list) < 0)
    -+			return 1;
    -+
    -+		/*
    -+		 * If there are no path args and submodule.active is set then,
    -+		 * by default, only initialize 'active' modules.
    -+		 */
    -+		if (!argc && git_config_get_value_multi("submodule.active"))
    -+			module_list_active(&list);
    -+
    -+		info.prefix = opt.prefix;
    -+		info.superprefix = opt.recursive_prefix;
    -+		if (opt.quiet)
    -+			info.flags |= OPT_QUIET;
    -+
    -+		for_each_listed_submodule(&list, init_submodule_cb, &info);
    -+	}
    -+
    -+	return update_submodules(&opt);
    -+}
    -+
    - struct add_data {
    - 	const char *prefix;
    - 	const char *branch;
    + 			die(_("bad value for update parameter"));
    + 
    + 	if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0)
     @@ builtin/submodule--helper.c: static struct cmd_struct commands[] = {
      	{"name", module_name, 0},
      	{"clone", module_clone, 0},
      	{"add", module_add, SUPPORT_SUPER_PREFIX},
    --	{"update-module-mode", module_update_module_mode, 0},
     -	{"update-clone", update_clone, 0},
     -	{"run-update-procedure", run_update_procedure, 0},
    --	{"ensure-core-worktree", ensure_core_worktree, 0},
     -	{"relative-path", resolve_relative_path, 0},
     +	{"update", module_update, 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},
    - 	{"sync", module_sync, SUPPORT_SUPER_PREFIX},
    - 	{"deinit", module_deinit, 0},
    - 	{"summary", module_summary, SUPPORT_SUPER_PREFIX},
     
      ## git-submodule.sh ##
    -@@ git-submodule.sh: cmd_deinit()
    - 	git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${force:+--force} ${deinit_all:+--all} -- "$@"
    - }
    - 
    --# usage: fetch_in_submodule <module_path> [<depth>] [<sha1>]
    --# Because arguments are positional, use an empty string to omit <depth>
    --# but include <sha1>.
    --fetch_in_submodule () (
    --	sanitize_submodule_env &&
    --	cd "$1" &&
    --	if test $# -eq 3
    --	then
    --		echo "$3" | git fetch ${GIT_QUIET:+--quiet} --stdin ${2:+"$2"}
    --	else
    --		git fetch ${GIT_QUIET:+--quiet} ${2:+"$2"}
    --	fi
    --)
    --
    - #
    - # Update each submodule path to correct revision, using clone and checkout as needed
    - #
     @@ git-submodule.sh: cmd_update()
      		shift
      	done
      
    --	if test -n "$init"
    --	then
    --		cmd_init "--" "$@" || return
    --	fi
    --
     -	{
    --	git submodule--helper update-clone ${GIT_QUIET:+--quiet} \
    --		${progress:+"--progress"} \
    +-	git ${wt_prefix:+-C "$wt_prefix"} submodule--helper update-clone \
     +	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper update \
    -+		${GIT_QUIET:+--quiet} \
    + 		${GIT_QUIET:+--quiet} \
    +-		${progress:+"--progress"} \
     +		${force:+--force} \
     +		${progress:+--progress} \
     +		${dissociate:+--dissociate} \
     +		${remote:+--remote} \
     +		${recursive:+--recursive} \
    -+		${init:+--init} \
    + 		${init:+--init} \
     +		${require_init:+--require-init} \
     +		${nofetch:+--no-fetch} \
      		${wt_prefix:+--prefix "$wt_prefix"} \
    @@ git-submodule.sh: cmd_update()
     -	do
     -		die_if_unmatched "$quickabort" "$sha1"
     -
    --		git submodule--helper ensure-core-worktree "$sm_path" || exit 1
    --
     -		displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
     -
    --		if test $just_cloned -eq 1
    +-		if test $just_cloned -eq 0
     -		then
    --			subsha1=
    --		else
     -			just_cloned=
    --			subsha1=$(sanitize_submodule_env; cd "$sm_path" &&
    --				git rev-parse --verify HEAD) ||
    --			die "fatal: $(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
    --		fi
    --
    --		if test -n "$remote"
    --		then
    --			branch=$(git submodule--helper remote-branch "$sm_path")
    --			if test -z "$nofetch"
    --			then
    --				# Fetch remote before determining tracking $sha1
    --				fetch_in_submodule "$sm_path" $depth ||
    --				die "fatal: $(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
    --			fi
    --			remote_name=$(sanitize_submodule_env; cd "$sm_path" && git submodule--helper print-default-remote)
    --			sha1=$(sanitize_submodule_env; cd "$sm_path" &&
    --				git rev-parse --verify "${remote_name}/${branch}") ||
    --			die "fatal: $(eval_gettext "Unable to find current \${remote_name}/\${branch} revision in submodule path '\$sm_path'")"
     -		fi
     -
     -		out=$(git submodule--helper run-update-procedure \
    @@ git-submodule.sh: cmd_update()
     -			  ${update:+--update "$update"} \
     -			  ${prefix:+--recursive-prefix "$prefix"} \
     -			  ${sha1:+--oid "$sha1"} \
    --			  ${subsha1:+--suboid "$subsha1"} \
    +-			  ${remote:+--remote} \
     -			  "--" \
     -			  "$sm_path")
     -

base-commit: b23dac905bde28da47543484320db16312c87551