submodule: move core cmd_update() logic to C

This patch completes the conversion past the flag parsing of `submodule update` by introducing a helper subcommand called `submodule--helper update`. The behaviour of `submodule update` should remain the same after this patch. Prior to this patch, `submodule update` was implemented by piping the output of `update-clone` (which clones all missing submodules, then prints relevant information for all submodules) into `run-update-procedure` (which reads the information and updates the submodule tree). With `submodule--helper update`, we iterate over the submodules and update the submodule tree in the same process. This reuses most of existing code structure, except that `update_submodule()` now updates the submodule tree (instead of printing submodule information to be consumed by another process). Recursing on a submodule is done by calling a subprocess that launches `submodule--helper update`, with a modified `--recursive-prefix` and `--prefix` parameter. 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> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Atharva Raykar committed Mar 15, 2022 at 14:09 UTC b3c5f5cb04854b56b39a40696c366053c8f09b58
2 files changed +131 -202
builtin/submodule--helper.c
+124 -105
@@ -1976,7 +1976,7 @@ struct submodule_update_clone {
1976 /* configuration parameters which are passed on to the children */
1977 struct update_data *update_data;
1978
1979 - /* to be consumed by git-submodule.sh */
1979 + /* to be consumed by update_submodule() */
1980 struct update_clone_data *update_clone;
1981 int update_clone_nr; int update_clone_alloc;
1982
@@ -1992,10 +1992,8 @@ struct submodule_update_clone {
1992 struct update_data {
1993 const char *prefix;
1994 const char *recursive_prefix;
1995 - const char *sm_path;
1995 const char *displaypath;
1996 const char *update_default;
1998 - struct object_id oid;
1997 struct object_id suboid;
1998 struct string_list references;
1999 struct submodule_update_strategy update_strategy;
@@ -2009,12 +2007,17 @@ struct update_data {
2007 unsigned int force;
2008 unsigned int quiet;
2009 unsigned int nofetch;
2012 - unsigned int just_cloned;
2010 unsigned int remote;
2011 unsigned int progress;
2012 unsigned int dissociate;
2013 unsigned int init;
2014 unsigned int warn_if_uninitialized;
2015 + unsigned int recursive;
2016 +
2017 + /* copied over from update_clone_data */
2018 + struct object_id oid;
2019 + unsigned int just_cloned;
2020 + const char *sm_path;
2021 };
2022 #define UPDATE_DATA_INIT { \
2023 .update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT, \
@@ -2419,7 +2422,7 @@ static int run_update_command(struct update_data *ud, int subforce)
2422 return 0;
2423 }
2424
2422 -static int do_run_update_procedure(struct update_data *ud)
2425 +static int run_update_procedure(struct update_data *ud)
2426 {
2427 int subforce = is_null_oid(&ud->suboid) || ud->force;
2428
@@ -2449,27 +2452,10 @@ static int do_run_update_procedure(struct update_data *ud)
2452 return run_update_command(ud, subforce);
2453 }
2454
2452 -/*
2453 - * NEEDSWORK: As we convert "git submodule update" to C,
2454 - * update_submodule2() will invoke more and more functions, making it
2455 - * difficult to preserve the function ordering without forward
2456 - * declarations.
2457 - *
2458 - * When the conversion is complete, this forward declaration will be
2459 - * unnecessary and should be removed.
2460 - */
2461 -static int update_submodule2(struct update_data *update_data);
2462 -static void update_submodule(struct update_clone_data *ucd)
2463 -{
2464 - fprintf(stdout, "dummy %s %d\t%s\n",
2465 - oid_to_hex(&ucd->oid),
2466 - ucd->just_cloned,
2467 - ucd->sub->path);
2468 -}
2469 -
2455 +static int update_submodule(struct update_data *update_data);
2456 static int update_submodules(struct update_data *update_data)
2457 {
2472 - int i;
2458 + int i, res = 0;
2459 struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
2460
2461 suc.update_data = update_data;
@@ -2486,25 +2472,44 @@ static int update_submodules(struct update_data *update_data)
2472 * checkout involve more straightforward sequential I/O.
2473 * - the listener can avoid doing any work if fetching failed.
2474 */
2489 - if (suc.quickstop)
2490 - return 1;
2475 + if (suc.quickstop) {
2476 + res = 1;
2477 + goto cleanup;
2478 + }
2479
2492 - for (i = 0; i < suc.update_clone_nr; i++)
2493 - update_submodule(&suc.update_clone[i]);
2480 + for (i = 0; i < suc.update_clone_nr; i++) {
2481 + struct update_clone_data ucd = suc.update_clone[i];
2482
2495 - return 0;
2483 + oidcpy(&update_data->oid, &ucd.oid);
2484 + update_data->just_cloned = ucd.just_cloned;
2485 + update_data->sm_path = ucd.sub->path;
2486 +
2487 + if (update_submodule(update_data))
2488 + res = 1;
2489 + }
2490 +
2491 +cleanup:
2492 + string_list_clear(&update_data->references, 0);
2493 + return res;
2494 }
2495
2498 -static int update_clone(int argc, const char **argv, const char *prefix)
2496 +static int module_update(int argc, const char **argv, const char *prefix)
2497 {
2498 struct pathspec pathspec;
2499 struct update_data opt = UPDATE_DATA_INIT;
2500 struct list_objects_filter_options filter_options;
2501 int ret;
2502
2505 - struct option module_update_clone_options[] = {
2503 + struct option module_update_options[] = {
2504 + OPT__FORCE(&opt.force, N_("force checkout updates"), 0),
2505 OPT_BOOL(0, "init", &opt.init,
2506 N_("initialize uninitialized submodules before update")),
2507 + OPT_BOOL(0, "remote", &opt.remote,
2508 + N_("use SHA-1 of submodule's remote tracking branch")),
2509 + OPT_BOOL(0, "recursive", &opt.recursive,
2510 + N_("traverse submodules recursively")),
2511 + OPT_BOOL('N', "no-fetch", &opt.nofetch,
2512 + N_("don't fetch new objects from the remote site")),
2513 OPT_STRING(0, "prefix", &opt.prefix,
2514 N_("path"),
2515 N_("path into the working tree")),
@@ -2551,19 +2556,12 @@ static int update_clone(int argc, const char **argv, const char *prefix)
2556 git_config(git_update_clone_config, &opt.max_jobs);
2557
2558 memset(&filter_options, 0, sizeof(filter_options));
2554 - argc = parse_options(argc, argv, prefix, module_update_clone_options,
2559 + argc = parse_options(argc, argv, prefix, module_update_options,
2560 git_submodule_helper_usage, 0);
2561
2562 if (filter_options.choice && !opt.init) {
2558 - /*
2559 - * NEEDSWORK: Don't use usage_with_options() because the
2560 - * usage string is for "git submodule update", but the
2561 - * options are for "git submodule--helper update-clone".
2562 - *
2563 - * This will no longer be an issue when "update-clone"
2564 - * is replaced by "git submodule--helper update".
2565 - */
2566 - usage(git_submodule_helper_usage[0]);
2563 + usage_with_options(git_submodule_helper_usage,
2564 + module_update_options);
2565 }
2566
2567 opt.filter_options = &filter_options;
@@ -2609,63 +2607,6 @@ static int update_clone(int argc, const char **argv, const char *prefix)
2607 return ret;
2608 }
2609
2612 -static int run_update_procedure(int argc, const char **argv, const char *prefix)
2613 -{
2614 - struct update_data update_data = UPDATE_DATA_INIT;
2615 -
2616 - struct option options[] = {
2617 - OPT__QUIET(&update_data.quiet,
2618 - N_("suppress output for update by rebase or merge")),
2619 - OPT__FORCE(&update_data.force, N_("force checkout updates"),
2620 - 0),
2621 - OPT_BOOL('N', "no-fetch", &update_data.nofetch,
2622 - N_("don't fetch new objects from the remote site")),
2623 - OPT_BOOL(0, "just-cloned", &update_data.just_cloned,
2624 - N_("overrides update mode in case the repository is a fresh clone")),
2625 - OPT_INTEGER(0, "depth", &update_data.depth, N_("depth for shallow fetch")),
2626 - OPT_STRING(0, "prefix", &prefix,
2627 - N_("path"),
2628 - N_("path into the working tree")),
2629 - OPT_STRING(0, "update", &update_data.update_default,
2630 - N_("string"),
2631 - N_("rebase, merge, checkout or none")),
2632 - OPT_STRING(0, "recursive-prefix", &update_data.recursive_prefix, N_("path"),
2633 - N_("path into the working tree, across nested "
2634 - "submodule boundaries")),
2635 - OPT_CALLBACK_F(0, "oid", &update_data.oid, N_("sha1"),
2636 - N_("SHA1 expected by superproject"), PARSE_OPT_NONEG,
2637 - parse_opt_object_id),
2638 - OPT_BOOL(0, "remote", &update_data.remote,
2639 - N_("use SHA-1 of submodule's remote tracking branch")),
2640 - OPT_END()
2641 - };
2642 -
2643 - const char *const usage[] = {
2644 - N_("git submodule--helper run-update-procedure [<options>] <path>"),
2645 - NULL
2646 - };
2647 -
2648 - argc = parse_options(argc, argv, prefix, options, usage, 0);
2649 -
2650 - if (argc != 1)
2651 - usage_with_options(usage, options);
2652 -
2653 - update_data.sm_path = argv[0];
2654 -
2655 - return update_submodule2(&update_data);
2656 -}
2657 -
2658 -static int resolve_relative_path(int argc, const char **argv, const char *prefix)
2659 -{
2660 - struct strbuf sb = STRBUF_INIT;
2661 - if (argc != 3)
2662 - die("submodule--helper relative-path takes exactly 2 arguments, got %d", argc);
2663 -
2664 - printf("%s", relative_path(argv[1], argv[2], &sb));
2665 - strbuf_release(&sb);
2666 - return 0;
2667 -}
2668 -
2610 static const char *remote_submodule_branch(const char *path)
2611 {
2612 const struct submodule *sub;
@@ -3028,8 +2969,53 @@ static int module_create_branch(int argc, const char **argv, const char *prefix)
2969 return 0;
2970 }
2971
3031 -/* NEEDSWORK: this is a temporary name until we delete update_submodule() */
3032 -static int update_submodule2(struct update_data *update_data)
2972 +static void update_data_to_args(struct update_data *update_data, struct strvec *args)
2973 +{
2974 + strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
2975 + strvec_pushf(args, "--jobs=%d", update_data->max_jobs);
2976 + if (update_data->recursive_prefix)
2977 + strvec_pushl(args, "--recursive-prefix",
2978 + update_data->recursive_prefix, NULL);
2979 + if (update_data->quiet)
2980 + strvec_push(args, "--quiet");
2981 + if (update_data->force)
2982 + strvec_push(args, "--force");
2983 + if (update_data->init)
2984 + strvec_push(args, "--init");
2985 + if (update_data->remote)
2986 + strvec_push(args, "--remote");
2987 + if (update_data->nofetch)
2988 + strvec_push(args, "--no-fetch");
2989 + if (update_data->dissociate)
2990 + strvec_push(args, "--dissociate");
2991 + if (update_data->progress)
2992 + strvec_push(args, "--progress");
2993 + if (update_data->require_init)
2994 + strvec_push(args, "--require-init");
2995 + if (update_data->depth)
2996 + strvec_pushf(args, "--depth=%d", update_data->depth);
2997 + if (update_data->update_default)
2998 + strvec_pushl(args, "--update", update_data->update_default, NULL);
2999 + if (update_data->references.nr) {
3000 + struct string_list_item *item;
3001 + for_each_string_list_item(item, &update_data->references)
3002 + strvec_pushl(args, "--reference", item->string, NULL);
3003 + }
3004 + if (update_data->filter_options && update_data->filter_options->choice)
3005 + strvec_pushf(args, "--filter=%s",
3006 + expand_list_objects_filter_spec(
3007 + update_data->filter_options));
3008 + if (update_data->recommend_shallow == 0)
3009 + strvec_push(args, "--no-recommend-shallow");
3010 + else if (update_data->recommend_shallow == 1)
3011 + strvec_push(args, "--recommend-shallow");
3012 + if (update_data->single_branch >= 0)
3013 + strvec_push(args, update_data->single_branch ?
3014 + "--single-branch" :
3015 + "--no-single-branch");
3016 +}
3017 +
3018 +static int update_submodule(struct update_data *update_data)
3019 {
3020 char *prefixed_path;
3021
@@ -3075,9 +3061,44 @@ static int update_submodule2(struct update_data *update_data)
3061 }
3062
3063 if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)
3078 - return do_run_update_procedure(update_data);
3064 + if (run_update_procedure(update_data))
3065 + return 1;
3066 +
3067 + if (update_data->recursive) {
3068 + struct child_process cp = CHILD_PROCESS_INIT;
3069 + struct update_data next = *update_data;
3070 + int res;
3071 +
3072 + if (update_data->recursive_prefix)
3073 + prefixed_path = xstrfmt("%s%s/", update_data->recursive_prefix,
3074 + update_data->sm_path);
3075 + else
3076 + prefixed_path = xstrfmt("%s/", update_data->sm_path);
3077 +
3078 + next.recursive_prefix = get_submodule_displaypath(prefixed_path,
3079 + update_data->prefix);
3080 + next.prefix = NULL;
3081 + oidcpy(&next.oid, null_oid());
3082 + oidcpy(&next.suboid, null_oid());
3083 +
3084 + cp.dir = update_data->sm_path;
3085 + cp.git_cmd = 1;
3086 + prepare_submodule_repo_env(&cp.env_array);
3087 + update_data_to_args(&next, &cp.args);
3088
3080 - return 3;
3089 + /* die() if child process die()'d */
3090 + res = run_command(&cp);
3091 + if (!res)
3092 + return 0;
3093 + die_message(_("Failed to recurse into submodule path '%s'"),
3094 + update_data->displaypath);
3095 + if (res == 128)
3096 + exit(res);
3097 + else if (res)
3098 + return 1;
3099 + }
3100 +
3101 + return 0;
3102 }
3103
3104 struct add_data {
@@ -3468,9 +3489,7 @@ static struct cmd_struct commands[] = {
3489 {"name", module_name, 0},
3490 {"clone", module_clone, 0},
3491 {"add", module_add, SUPPORT_SUPER_PREFIX},
3471 - {"update-clone", update_clone, 0},
3472 - {"run-update-procedure", run_update_procedure, 0},
3473 - {"relative-path", resolve_relative_path, 0},
3492 + {"update", module_update, 0},
3493 {"resolve-relative-url-test", resolve_relative_url_test, 0},
3494 {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
3495 {"init", module_init, SUPPORT_SUPER_PREFIX},
git-submodule.sh
+7 -97
@@ -51,14 +51,6 @@ jobs=
51 recommend_shallow=
52 filter=
53
54 -die_if_unmatched ()
55 -{
56 - if test "$1" = "#unmatched"
57 - then
58 - exit ${2:-1}
59 - fi
60 -}
61 -
54 isnumber()
55 {
56 n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
@@ -356,11 +348,14 @@ cmd_update()
348 shift
349 done
350
359 - {
360 - git ${wt_prefix:+-C "$wt_prefix"} submodule--helper update-clone \
351 + git ${wt_prefix:+-C "$wt_prefix"} submodule--helper update \
352 ${GIT_QUIET:+--quiet} \
353 + ${force:+--force} \
354 ${progress:+"--progress"} \
355 + ${remote:+--remote} \
356 + ${recursive:+--recursive} \
357 ${init:+--init} \
358 + ${nofetch:+--no-fetch} \
359 ${wt_prefix:+--prefix "$wt_prefix"} \
360 ${prefix:+--recursive-prefix "$prefix"} \
361 ${update:+--update "$update"} \
@@ -368,98 +363,13 @@ cmd_update()
363 ${dissociate:+"--dissociate"} \
364 ${depth:+"$depth"} \
365 ${require_init:+--require-init} \
366 + ${dissociate:+"--dissociate"} \
367 $single_branch \
368 $recommend_shallow \
369 $jobs \
370 $filter \
371 -- \
376 - "$@" || echo "#unmatched" $?
377 - } | {
378 - err=
379 - while read -r quickabort sha1 just_cloned sm_path
380 - do
381 - die_if_unmatched "$quickabort" "$sha1"
382 -
383 - displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
384 -
385 - if test $just_cloned -eq 0
386 - then
387 - just_cloned=
388 - fi
389 -
390 - out=$(git submodule--helper run-update-procedure \
391 - ${wt_prefix:+--prefix "$wt_prefix"} \
392 - ${GIT_QUIET:+--quiet} \
393 - ${force:+--force} \
394 - ${just_cloned:+--just-cloned} \
395 - ${nofetch:+--no-fetch} \
396 - ${depth:+"$depth"} \
397 - ${update:+--update "$update"} \
398 - ${prefix:+--recursive-prefix "$prefix"} \
399 - ${sha1:+--oid "$sha1"} \
400 - ${remote:+--remote} \
401 - "--" \
402 - "$sm_path")
403 -
404 - # exit codes for run-update-procedure:
405 - # 0: update was successful, say command output
406 - # 1: update procedure failed, but should not die
407 - # 128: subcommand died during execution
408 - # 3: no update procedure was run
409 - res="$?"
410 - case $res in
411 - 0)
412 - say "$out"
413 - ;;
414 - 1)
415 - err="${err};$out"
416 - continue
417 - ;;
418 - 128)
419 - printf >&2 "$out"
420 - exit $res
421 - ;;
422 - esac
423 -
424 - if test -n "$recursive"
425 - then
426 - (
427 - prefix=$(git submodule--helper relative-path "$prefix$sm_path/" "$wt_prefix")
428 - wt_prefix=
429 - sanitize_submodule_env
430 - cd "$sm_path" &&
431 - eval cmd_update
432 - )
433 - res=$?
434 - if test $res -gt 0
435 - then
436 - die_msg="fatal: $(eval_gettext "Failed to recurse into submodule path '\$displaypath'")"
437 - if test $res -ne 2
438 - then
439 - err="${err};$die_msg"
440 - continue
441 - else
442 - die_with_status $res "$die_msg"
443 - fi
444 - fi
445 - fi
446 - done
447 -
448 - if test -n "$err"
449 - then
450 - OIFS=$IFS
451 - IFS=';'
452 - for e in $err
453 - do
454 - if test -n "$e"
455 - then
456 - echo >&2 "$e"
457 - fi
458 - done
459 - IFS=$OIFS
460 - exit 1
461 - fi
462 - }
372 + "$@"
373 }
374
375 #