summaryrefslogtreecommitdiff
path: root/builtin/submodule--helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/submodule--helper.c')
-rw-r--r--builtin/submodule--helper.c257
1 files changed, 257 insertions, 0 deletions
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 77826ea9dd..f8fb70c826 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2009,6 +2009,20 @@ struct submodule_update_clone {
.max_jobs = 1, \
}
+struct update_data {
+ const char *recursive_prefix;
+ const char *sm_path;
+ const char *displaypath;
+ struct object_id oid;
+ struct object_id suboid;
+ struct submodule_update_strategy update_strategy;
+ int depth;
+ unsigned int force: 1;
+ unsigned int quiet: 1;
+ unsigned int nofetch: 1;
+ unsigned int just_cloned: 1;
+};
+#define UPDATE_DATA_INIT { .update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT }
static void next_submodule_warn_missing(struct submodule_update_clone *suc,
struct strbuf *out, const char *displaypath)
@@ -2262,6 +2276,181 @@ static int git_update_clone_config(const char *var, const char *value,
return 0;
}
+static int is_tip_reachable(const char *path, struct object_id *oid)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf rev = STRBUF_INIT;
+ char *hex = oid_to_hex(oid);
+
+ cp.git_cmd = 1;
+ cp.dir = xstrdup(path);
+ cp.no_stderr = 1;
+ strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
+
+ prepare_submodule_repo_env(&cp.env_array);
+
+ if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)
+ return 0;
+
+ return 1;
+}
+
+static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.git_cmd = 1;
+ cp.dir = xstrdup(module_path);
+
+ strvec_push(&cp.args, "fetch");
+ if (quiet)
+ strvec_push(&cp.args, "--quiet");
+ if (depth)
+ strvec_pushf(&cp.args, "--depth=%d", depth);
+ if (oid) {
+ char *hex = oid_to_hex(oid);
+ char *remote = get_default_remote();
+ strvec_pushl(&cp.args, remote, hex, NULL);
+ }
+
+ return run_command(&cp);
+}
+
+static int run_update_command(struct update_data *ud, int subforce)
+{
+ struct strvec args = STRVEC_INIT;
+ struct strvec child_env = STRVEC_INIT;
+ char *oid = oid_to_hex(&ud->oid);
+ int must_die_on_failure = 0;
+ int git_cmd;
+
+ switch (ud->update_strategy.type) {
+ case SM_UPDATE_CHECKOUT:
+ git_cmd = 1;
+ strvec_pushl(&args, "checkout", "-q", NULL);
+ if (subforce)
+ strvec_push(&args, "-f");
+ break;
+ case SM_UPDATE_REBASE:
+ git_cmd = 1;
+ strvec_push(&args, "rebase");
+ if (ud->quiet)
+ strvec_push(&args, "--quiet");
+ must_die_on_failure = 1;
+ break;
+ case SM_UPDATE_MERGE:
+ git_cmd = 1;
+ strvec_push(&args, "merge");
+ if (ud->quiet)
+ strvec_push(&args, "--quiet");
+ must_die_on_failure = 1;
+ break;
+ case SM_UPDATE_COMMAND:
+ git_cmd = 0;
+ strvec_push(&args, ud->update_strategy.command);
+ must_die_on_failure = 1;
+ break;
+ default:
+ BUG("unexpected update strategy type: %s",
+ submodule_strategy_to_string(&ud->update_strategy));
+ }
+ strvec_push(&args, oid);
+
+ prepare_submodule_repo_env(&child_env);
+ if (run_command_v_opt_cd_env(args.v, git_cmd ? RUN_GIT_CMD : RUN_USING_SHELL,
+ ud->sm_path, child_env.v)) {
+ switch (ud->update_strategy.type) {
+ case SM_UPDATE_CHECKOUT:
+ printf(_("Unable to checkout '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
+ break;
+ case SM_UPDATE_REBASE:
+ printf(_("Unable to rebase '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
+ break;
+ case SM_UPDATE_MERGE:
+ printf(_("Unable to merge '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
+ break;
+ case SM_UPDATE_COMMAND:
+ printf(_("Execution of '%s %s' failed in submodule path '%s'"),
+ ud->update_strategy.command, oid, ud->displaypath);
+ break;
+ default:
+ BUG("unexpected update strategy type: %s",
+ submodule_strategy_to_string(&ud->update_strategy));
+ }
+ /*
+ * NEEDSWORK: We are currently printing to stdout with error
+ * return so that the shell caller handles the error output
+ * properly. Once we start handling the error messages within
+ * C, we should use die() instead.
+ */
+ if (must_die_on_failure)
+ return 2;
+ /*
+ * This signifies to the caller in shell that the command
+ * failed without dying
+ */
+ return 1;
+ }
+
+ switch (ud->update_strategy.type) {
+ case SM_UPDATE_CHECKOUT:
+ printf(_("Submodule path '%s': checked out '%s'\n"),
+ ud->displaypath, oid);
+ break;
+ case SM_UPDATE_REBASE:
+ printf(_("Submodule path '%s': rebased into '%s'\n"),
+ ud->displaypath, oid);
+ break;
+ case SM_UPDATE_MERGE:
+ printf(_("Submodule path '%s': merged in '%s'\n"),
+ ud->displaypath, oid);
+ break;
+ case SM_UPDATE_COMMAND:
+ printf(_("Submodule path '%s': '%s %s'\n"),
+ ud->displaypath, ud->update_strategy.command, oid);
+ break;
+ default:
+ BUG("unexpected update strategy type: %s",
+ submodule_strategy_to_string(&ud->update_strategy));
+ }
+
+ return 0;
+}
+
+static int do_run_update_procedure(struct update_data *ud)
+{
+ int subforce = is_null_oid(&ud->suboid) || ud->force;
+
+ if (!ud->nofetch) {
+ /*
+ * Run fetch only if `oid` isn't present or it
+ * is not reachable from a ref.
+ */
+ if (!is_tip_reachable(ud->sm_path, &ud->oid) &&
+ fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, NULL) &&
+ !ud->quiet)
+ fprintf_ln(stderr,
+ _("Unable to fetch in submodule path '%s'; "
+ "trying to directly fetch %s:"),
+ ud->displaypath, oid_to_hex(&ud->oid));
+ /*
+ * Now we tried the usual fetch, but `oid` may
+ * not be reachable from any of the refs.
+ */
+ if (!is_tip_reachable(ud->sm_path, &ud->oid) &&
+ fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, &ud->oid))
+ die(_("Fetched in submodule path '%s', but it did not "
+ "contain %s. Direct fetching of that commit failed."),
+ ud->displaypath, oid_to_hex(&ud->oid));
+ }
+
+ return run_update_command(ud, subforce);
+}
+
static void update_submodule(struct update_clone_data *ucd)
{
fprintf(stdout, "dummy %s %d\t%s\n",
@@ -2359,6 +2548,73 @@ static int update_clone(int argc, const char **argv, const char *prefix)
return update_submodules(&suc);
}
+static int run_update_procedure(int argc, const char **argv, const char *prefix)
+{
+ int force = 0, quiet = 0, nofetch = 0, just_cloned = 0;
+ char *prefixed_path, *update = NULL;
+ struct update_data update_data = UPDATE_DATA_INIT;
+
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("suppress output for update by rebase or merge")),
+ OPT__FORCE(&force, N_("force checkout updates"), 0),
+ OPT_BOOL('N', "no-fetch", &nofetch,
+ N_("don't fetch new objects from the remote site")),
+ OPT_BOOL(0, "just-cloned", &just_cloned,
+ N_("overrides update mode in case the repository is a fresh clone")),
+ OPT_INTEGER(0, "depth", &update_data.depth, N_("depth for shallow fetch")),
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("path into the working tree")),
+ OPT_STRING(0, "update", &update,
+ N_("string"),
+ N_("rebase, merge, checkout or none")),
+ OPT_STRING(0, "recursive-prefix", &update_data.recursive_prefix, N_("path"),
+ N_("path into the working tree, across nested "
+ "submodule boundaries")),
+ OPT_CALLBACK_F(0, "oid", &update_data.oid, N_("sha1"),
+ N_("SHA1 expected by superproject"), PARSE_OPT_NONEG,
+ parse_opt_object_id),
+ OPT_CALLBACK_F(0, "suboid", &update_data.suboid, N_("subsha1"),
+ N_("SHA1 of submodule's HEAD"), PARSE_OPT_NONEG,
+ parse_opt_object_id),
+ OPT_END()
+ };
+
+ const char *const usage[] = {
+ N_("git submodule--helper run-update-procedure [<options>] <path>"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+ if (argc != 1)
+ usage_with_options(usage, options);
+
+ update_data.force = !!force;
+ update_data.quiet = !!quiet;
+ update_data.nofetch = !!nofetch;
+ update_data.just_cloned = !!just_cloned;
+ update_data.sm_path = argv[0];
+
+ if (update_data.recursive_prefix)
+ prefixed_path = xstrfmt("%s%s", update_data.recursive_prefix, update_data.sm_path);
+ else
+ prefixed_path = xstrdup(update_data.sm_path);
+
+ update_data.displaypath = get_submodule_displaypath(prefixed_path, prefix);
+
+ determine_submodule_update_strategy(the_repository, update_data.just_cloned,
+ update_data.sm_path, update,
+ &update_data.update_strategy);
+
+ free(prefixed_path);
+
+ if (!oideq(&update_data.oid, &update_data.suboid) || update_data.force)
+ return do_run_update_procedure(&update_data);
+
+ return 3;
+}
+
static int resolve_relative_path(int argc, const char **argv, const char *prefix)
{
struct strbuf sb = STRBUF_INIT;
@@ -3098,6 +3354,7 @@ static struct cmd_struct commands[] = {
{"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},
{"resolve-relative-url-test", resolve_relative_url_test, 0},