Skip to content

Commit c51f8f9

Browse files
tfidfwastakengitster
authored andcommitted
submodule--helper: run update procedures from C
Add a new submodule--helper subcommand `run-update-procedure` that runs the update procedure if the SHA1 of the submodule does not match what the superproject expects. This is an intermediate change that works towards total conversion of `submodule update` from shell to C. Specific error codes are returned so that the shell script calling the subcommand can take a decision on the control flow, and preserve the error messages across subsequent recursive calls of `cmd_update`. This change is more focused on doing a faithful conversion, so for now we are not too concerned with trying to reduce subprocess spawns. Mentored-by: Christian Couder <[email protected]> Mentored-by: Shourya Shukla <[email protected]> Signed-off-by: Atharva Raykar <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 5d213e4 commit c51f8f9

File tree

2 files changed

+290
-73
lines changed

2 files changed

+290
-73
lines changed

builtin/submodule--helper.c

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2045,6 +2045,20 @@ struct submodule_update_clone {
20452045
.max_jobs = 1, \
20462046
}
20472047

2048+
struct update_data {
2049+
const char *recursive_prefix;
2050+
const char *sm_path;
2051+
const char *displaypath;
2052+
struct object_id oid;
2053+
struct object_id suboid;
2054+
struct submodule_update_strategy update_strategy;
2055+
int depth;
2056+
unsigned int force: 1;
2057+
unsigned int quiet: 1;
2058+
unsigned int nofetch: 1;
2059+
unsigned int just_cloned: 1;
2060+
};
2061+
#define UPDATE_DATA_INIT { .update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT }
20482062

20492063
static void next_submodule_warn_missing(struct submodule_update_clone *suc,
20502064
struct strbuf *out, const char *displaypath)
@@ -2298,6 +2312,181 @@ static int git_update_clone_config(const char *var, const char *value,
22982312
return 0;
22992313
}
23002314

2315+
static int is_tip_reachable(const char *path, struct object_id *oid)
2316+
{
2317+
struct child_process cp = CHILD_PROCESS_INIT;
2318+
struct strbuf rev = STRBUF_INIT;
2319+
char *hex = oid_to_hex(oid);
2320+
2321+
cp.git_cmd = 1;
2322+
cp.dir = xstrdup(path);
2323+
cp.no_stderr = 1;
2324+
strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
2325+
2326+
prepare_submodule_repo_env(&cp.env_array);
2327+
2328+
if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)
2329+
return 0;
2330+
2331+
return 1;
2332+
}
2333+
2334+
static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)
2335+
{
2336+
struct child_process cp = CHILD_PROCESS_INIT;
2337+
2338+
prepare_submodule_repo_env(&cp.env_array);
2339+
cp.git_cmd = 1;
2340+
cp.dir = xstrdup(module_path);
2341+
2342+
strvec_push(&cp.args, "fetch");
2343+
if (quiet)
2344+
strvec_push(&cp.args, "--quiet");
2345+
if (depth)
2346+
strvec_pushf(&cp.args, "--depth=%d", depth);
2347+
if (oid) {
2348+
char *hex = oid_to_hex(oid);
2349+
char *remote = get_default_remote();
2350+
strvec_pushl(&cp.args, remote, hex, NULL);
2351+
}
2352+
2353+
return run_command(&cp);
2354+
}
2355+
2356+
static int run_update_command(struct update_data *ud, int subforce)
2357+
{
2358+
struct strvec args = STRVEC_INIT;
2359+
struct strvec child_env = STRVEC_INIT;
2360+
char *oid = oid_to_hex(&ud->oid);
2361+
int must_die_on_failure = 0;
2362+
int git_cmd;
2363+
2364+
switch (ud->update_strategy.type) {
2365+
case SM_UPDATE_CHECKOUT:
2366+
git_cmd = 1;
2367+
strvec_pushl(&args, "checkout", "-q", NULL);
2368+
if (subforce)
2369+
strvec_push(&args, "-f");
2370+
break;
2371+
case SM_UPDATE_REBASE:
2372+
git_cmd = 1;
2373+
strvec_push(&args, "rebase");
2374+
if (ud->quiet)
2375+
strvec_push(&args, "--quiet");
2376+
must_die_on_failure = 1;
2377+
break;
2378+
case SM_UPDATE_MERGE:
2379+
git_cmd = 1;
2380+
strvec_push(&args, "merge");
2381+
if (ud->quiet)
2382+
strvec_push(&args, "--quiet");
2383+
must_die_on_failure = 1;
2384+
break;
2385+
case SM_UPDATE_COMMAND:
2386+
git_cmd = 0;
2387+
strvec_push(&args, ud->update_strategy.command);
2388+
must_die_on_failure = 1;
2389+
break;
2390+
default:
2391+
BUG("unexpected update strategy type: %s",
2392+
submodule_strategy_to_string(&ud->update_strategy));
2393+
}
2394+
strvec_push(&args, oid);
2395+
2396+
prepare_submodule_repo_env(&child_env);
2397+
if (run_command_v_opt_cd_env(args.v, git_cmd ? RUN_GIT_CMD : RUN_USING_SHELL,
2398+
ud->sm_path, child_env.v)) {
2399+
switch (ud->update_strategy.type) {
2400+
case SM_UPDATE_CHECKOUT:
2401+
printf(_("Unable to checkout '%s' in submodule path '%s'"),
2402+
oid, ud->displaypath);
2403+
break;
2404+
case SM_UPDATE_REBASE:
2405+
printf(_("Unable to rebase '%s' in submodule path '%s'"),
2406+
oid, ud->displaypath);
2407+
break;
2408+
case SM_UPDATE_MERGE:
2409+
printf(_("Unable to merge '%s' in submodule path '%s'"),
2410+
oid, ud->displaypath);
2411+
break;
2412+
case SM_UPDATE_COMMAND:
2413+
printf(_("Execution of '%s %s' failed in submodule path '%s'"),
2414+
ud->update_strategy.command, oid, ud->displaypath);
2415+
break;
2416+
default:
2417+
BUG("unexpected update strategy type: %s",
2418+
submodule_strategy_to_string(&ud->update_strategy));
2419+
}
2420+
/*
2421+
* NEEDSWORK: We are currently printing to stdout with error
2422+
* return so that the shell caller handles the error output
2423+
* properly. Once we start handling the error messages within
2424+
* C, we should use die() instead.
2425+
*/
2426+
if (must_die_on_failure)
2427+
return 2;
2428+
/*
2429+
* This signifies to the caller in shell that the command
2430+
* failed without dying
2431+
*/
2432+
return 1;
2433+
}
2434+
2435+
switch (ud->update_strategy.type) {
2436+
case SM_UPDATE_CHECKOUT:
2437+
printf(_("Submodule path '%s': checked out '%s'\n"),
2438+
ud->displaypath, oid);
2439+
break;
2440+
case SM_UPDATE_REBASE:
2441+
printf(_("Submodule path '%s': rebased into '%s'\n"),
2442+
ud->displaypath, oid);
2443+
break;
2444+
case SM_UPDATE_MERGE:
2445+
printf(_("Submodule path '%s': merged in '%s'\n"),
2446+
ud->displaypath, oid);
2447+
break;
2448+
case SM_UPDATE_COMMAND:
2449+
printf(_("Submodule path '%s': '%s %s'\n"),
2450+
ud->displaypath, ud->update_strategy.command, oid);
2451+
break;
2452+
default:
2453+
BUG("unexpected update strategy type: %s",
2454+
submodule_strategy_to_string(&ud->update_strategy));
2455+
}
2456+
2457+
return 0;
2458+
}
2459+
2460+
static int do_run_update_procedure(struct update_data *ud)
2461+
{
2462+
int subforce = is_null_oid(&ud->suboid) || ud->force;
2463+
2464+
if (!ud->nofetch) {
2465+
/*
2466+
* Run fetch only if `oid` isn't present or it
2467+
* is not reachable from a ref.
2468+
*/
2469+
if (!is_tip_reachable(ud->sm_path, &ud->oid) &&
2470+
fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, NULL) &&
2471+
!ud->quiet)
2472+
fprintf_ln(stderr,
2473+
_("Unable to fetch in submodule path '%s'; "
2474+
"trying to directly fetch %s:"),
2475+
ud->displaypath, oid_to_hex(&ud->oid));
2476+
/*
2477+
* Now we tried the usual fetch, but `oid` may
2478+
* not be reachable from any of the refs.
2479+
*/
2480+
if (!is_tip_reachable(ud->sm_path, &ud->oid) &&
2481+
fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, &ud->oid))
2482+
die(_("Fetched in submodule path '%s', but it did not "
2483+
"contain %s. Direct fetching of that commit failed."),
2484+
ud->displaypath, oid_to_hex(&ud->oid));
2485+
}
2486+
2487+
return run_update_command(ud, subforce);
2488+
}
2489+
23012490
static void update_submodule(struct update_clone_data *ucd)
23022491
{
23032492
fprintf(stdout, "dummy %s %d\t%s\n",
@@ -2395,6 +2584,73 @@ static int update_clone(int argc, const char **argv, const char *prefix)
23952584
return update_submodules(&suc);
23962585
}
23972586

2587+
static int run_update_procedure(int argc, const char **argv, const char *prefix)
2588+
{
2589+
int force = 0, quiet = 0, nofetch = 0, just_cloned = 0;
2590+
char *prefixed_path, *update = NULL;
2591+
struct update_data update_data = UPDATE_DATA_INIT;
2592+
2593+
struct option options[] = {
2594+
OPT__QUIET(&quiet, N_("suppress output for update by rebase or merge")),
2595+
OPT__FORCE(&force, N_("force checkout updates"), 0),
2596+
OPT_BOOL('N', "no-fetch", &nofetch,
2597+
N_("don't fetch new objects from the remote site")),
2598+
OPT_BOOL(0, "just-cloned", &just_cloned,
2599+
N_("overrides update mode in case the repository is a fresh clone")),
2600+
OPT_INTEGER(0, "depth", &update_data.depth, N_("depth for shallow fetch")),
2601+
OPT_STRING(0, "prefix", &prefix,
2602+
N_("path"),
2603+
N_("path into the working tree")),
2604+
OPT_STRING(0, "update", &update,
2605+
N_("string"),
2606+
N_("rebase, merge, checkout or none")),
2607+
OPT_STRING(0, "recursive-prefix", &update_data.recursive_prefix, N_("path"),
2608+
N_("path into the working tree, across nested "
2609+
"submodule boundaries")),
2610+
OPT_CALLBACK_F(0, "oid", &update_data.oid, N_("sha1"),
2611+
N_("SHA1 expected by superproject"), PARSE_OPT_NONEG,
2612+
parse_opt_object_id),
2613+
OPT_CALLBACK_F(0, "suboid", &update_data.suboid, N_("subsha1"),
2614+
N_("SHA1 of submodule's HEAD"), PARSE_OPT_NONEG,
2615+
parse_opt_object_id),
2616+
OPT_END()
2617+
};
2618+
2619+
const char *const usage[] = {
2620+
N_("git submodule--helper run-update-procedure [<options>] <path>"),
2621+
NULL
2622+
};
2623+
2624+
argc = parse_options(argc, argv, prefix, options, usage, 0);
2625+
2626+
if (argc != 1)
2627+
usage_with_options(usage, options);
2628+
2629+
update_data.force = !!force;
2630+
update_data.quiet = !!quiet;
2631+
update_data.nofetch = !!nofetch;
2632+
update_data.just_cloned = !!just_cloned;
2633+
update_data.sm_path = argv[0];
2634+
2635+
if (update_data.recursive_prefix)
2636+
prefixed_path = xstrfmt("%s%s", update_data.recursive_prefix, update_data.sm_path);
2637+
else
2638+
prefixed_path = xstrdup(update_data.sm_path);
2639+
2640+
update_data.displaypath = get_submodule_displaypath(prefixed_path, prefix);
2641+
2642+
determine_submodule_update_strategy(the_repository, update_data.just_cloned,
2643+
update_data.sm_path, update,
2644+
&update_data.update_strategy);
2645+
2646+
free(prefixed_path);
2647+
2648+
if (!oideq(&update_data.oid, &update_data.suboid) || update_data.force)
2649+
return do_run_update_procedure(&update_data);
2650+
2651+
return 3;
2652+
}
2653+
23982654
static int resolve_relative_path(int argc, const char **argv, const char *prefix)
23992655
{
24002656
struct strbuf sb = STRBUF_INIT;
@@ -2951,6 +3207,7 @@ static struct cmd_struct commands[] = {
29513207
{"add-clone", add_clone, 0},
29523208
{"update-module-mode", module_update_module_mode, 0},
29533209
{"update-clone", update_clone, 0},
3210+
{"run-update-procedure", run_update_procedure, 0},
29543211
{"ensure-core-worktree", ensure_core_worktree, 0},
29553212
{"relative-path", resolve_relative_path, 0},
29563213
{"resolve-relative-url", resolve_relative_url, 0},

0 commit comments

Comments
 (0)