summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin/submodule--helper.c248
-rwxr-xr-xgit-submodule.sh54
-rwxr-xr-xt/t7406-submodule-update.sh26
-rwxr-xr-xt/t7408-submodule-reference.sh14
4 files changed, 183 insertions, 159 deletions
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2f7c58362b..5301612d24 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -31,11 +31,13 @@
typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
void *cb_data);
-static char *get_default_remote(void)
+static char *repo_get_default_remote(struct repository *repo)
{
char *dest = NULL, *ret;
struct strbuf sb = STRBUF_INIT;
- const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+ struct ref_store *store = get_main_ref_store(repo);
+ const char *refname = refs_resolve_ref_unsafe(store, "HEAD", 0, NULL,
+ NULL);
if (!refname)
die(_("No such ref: %s"), "HEAD");
@@ -48,7 +50,7 @@ static char *get_default_remote(void)
die(_("Expecting a full ref name, got %s"), refname);
strbuf_addf(&sb, "branch.%s.remote", refname);
- if (git_config_get_string(sb.buf, &dest))
+ if (repo_config_get_string(repo, sb.buf, &dest))
ret = xstrdup("origin");
else
ret = dest;
@@ -57,19 +59,17 @@ static char *get_default_remote(void)
return ret;
}
-static int print_default_remote(int argc, const char **argv, const char *prefix)
+static char *get_default_remote_submodule(const char *module_path)
{
- char *remote;
-
- if (argc != 1)
- die(_("submodule--helper print-default-remote takes no arguments"));
+ struct repository subrepo;
- remote = get_default_remote();
- if (remote)
- printf("%s\n", remote);
+ repo_submodule_init(&subrepo, the_repository, module_path, null_oid());
+ return repo_get_default_remote(&subrepo);
+}
- free(remote);
- return 0;
+static char *get_default_remote(void)
+{
+ return repo_get_default_remote(the_repository);
}
static int starts_with_dot_slash(const char *str)
@@ -247,11 +247,10 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr
return 0;
}
-/* the result should be freed by the caller. */
-static char *get_submodule_displaypath(const char *path, const char *prefix)
+static char *do_get_submodule_displaypath(const char *path,
+ const char *prefix,
+ const char *super_prefix)
{
- const char *super_prefix = get_super_prefix();
-
if (prefix && super_prefix) {
BUG("cannot have prefix '%s' and superprefix '%s'",
prefix, super_prefix);
@@ -267,6 +266,13 @@ static char *get_submodule_displaypath(const char *path, const char *prefix)
}
}
+/* the result should be freed by the caller. */
+static char *get_submodule_displaypath(const char *path, const char *prefix)
+{
+ const char *super_prefix = get_super_prefix();
+ return do_get_submodule_displaypath(path, prefix, super_prefix);
+}
+
static char *compute_rev_name(const char *sub_path, const char* object_id)
{
struct strbuf sb = STRBUF_INIT;
@@ -588,18 +594,22 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
struct init_cb {
const char *prefix;
+ const char *superprefix;
unsigned int flags;
};
#define INIT_CB_INIT { 0 }
static void init_submodule(const char *path, const char *prefix,
- unsigned int flags)
+ const char *superprefix, unsigned int flags)
{
const struct submodule *sub;
struct strbuf sb = STRBUF_INIT;
char *upd = NULL, *url = NULL, *displaypath;
- displaypath = get_submodule_displaypath(path, prefix);
+ /* try superprefix from the environment, if it is not passed explicitly */
+ if (!superprefix)
+ superprefix = get_super_prefix();
+ displaypath = do_get_submodule_displaypath(path, prefix, superprefix);
sub = submodule_from_path(the_repository, null_oid(), path);
@@ -673,7 +683,7 @@ static void init_submodule(const char *path, const char *prefix,
static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)
{
struct init_cb *info = cb_data;
- init_submodule(list_item->name, info->prefix, info->flags);
+ init_submodule(list_item->name, info->prefix, info->superprefix, info->flags);
}
static int module_init(int argc, const char **argv, const char *prefix)
@@ -1343,9 +1353,8 @@ static void sync_submodule(const char *path, const char *prefix,
{
const struct submodule *sub;
char *remote_key = NULL;
- char *sub_origin_url, *super_config_url, *displaypath;
+ char *sub_origin_url, *super_config_url, *displaypath, *default_remote;
struct strbuf sb = STRBUF_INIT;
- struct child_process cp = CHILD_PROCESS_INIT;
char *sub_config_path = NULL;
if (!is_submodule_active(the_repository, path))
@@ -1384,21 +1393,15 @@ static void sync_submodule(const char *path, const char *prefix,
if (!is_submodule_populated_gently(path, NULL))
goto cleanup;
- prepare_submodule_repo_env(&cp.env_array);
- cp.git_cmd = 1;
- cp.dir = path;
- strvec_pushl(&cp.args, "submodule--helper",
- "print-default-remote", NULL);
-
strbuf_reset(&sb);
- if (capture_command(&cp, &sb, 0))
+ default_remote = get_default_remote_submodule(path);
+ if (!default_remote)
die(_("failed to get the default remote for submodule '%s'"),
path);
- strbuf_strip_suffix(&sb, "\n");
- remote_key = xstrfmt("remote.%s.url", sb.buf);
+ remote_key = xstrfmt("remote.%s.url", default_remote);
+ free(default_remote);
- strbuf_reset(&sb);
submodule_to_gitdir(&sb, path);
strbuf_addstr(&sb, "/config");
@@ -1957,29 +1960,6 @@ 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;
@@ -2020,6 +2000,7 @@ 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, \
@@ -2038,10 +2019,11 @@ struct update_data {
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;
+ unsigned int force;
+ unsigned int quiet;
+ unsigned int nofetch;
+ unsigned int just_cloned;
+ unsigned int remote;
};
#define UPDATE_DATA_INIT { .update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT }
@@ -2475,6 +2457,16 @@ static int do_run_update_procedure(struct update_data *ud)
return run_update_command(ud, subforce);
}
+/*
+ * NEEDSWORK: As we convert "git submodule update" to C,
+ * update_submodule2() will invoke more and more functions, making it
+ * difficult to preserve the function ordering without forward
+ * declarations.
+ *
+ * When the conversion is complete, this forward declaration will be
+ * unnecessary and should be removed.
+ */
+static int update_submodule2(struct update_data *update_data);
static void update_submodule(struct update_clone_data *ucd)
{
fprintf(stdout, "dummy %s %d\t%s\n",
@@ -2518,6 +2510,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
int ret;
struct option module_update_clone_options[] = {
+ OPT_BOOL(0, "init", &suc.init,
+ N_("initialize uninitialized submodules before update")),
OPT_STRING(0, "prefix", &prefix,
N_("path"),
N_("path into the working tree")),
@@ -2551,7 +2545,12 @@ static int update_clone(int argc, const char **argv, const char *prefix)
};
const char *const git_submodule_helper_usage[] = {
- N_("git submodule--helper update-clone [--prefix=<path>] [<path>...]"),
+ N_("git submodule [--quiet] update"
+ " [--init [--filter=<filter-spec>]] [--remote]"
+ " [-N|--no-fetch] [-f|--force]"
+ " [--checkout|--merge|--rebase]"
+ " [--[no-]recommend-shallow] [--reference <repository>]"
+ " [--recursive] [--[no-]single-branch] [--] [<path>...]"),
NULL
};
suc.prefix = prefix;
@@ -2562,6 +2561,19 @@ static int update_clone(int argc, const char **argv, const char *prefix)
memset(&filter_options, 0, sizeof(filter_options));
argc = parse_options(argc, argv, prefix, module_update_clone_options,
git_submodule_helper_usage, 0);
+
+ if (filter_options.choice && !suc.init) {
+ /*
+ * NEEDSWORK: Don't use usage_with_options() because the
+ * usage string is for "git submodule update", but the
+ * options are for "git submodule--helper update-clone".
+ *
+ * This will no longer be an issue when "update-clone"
+ * is replaced by "git submodule--helper update".
+ */
+ usage(git_submodule_helper_usage[0]);
+ }
+
suc.filter_options = &filter_options;
if (update)
@@ -2576,6 +2588,29 @@ static int update_clone(int argc, const char **argv, const char *prefix)
if (pathspec.nr)
suc.warn_if_uninitialized = 1;
+ if (suc.init) {
+ struct module_list list = MODULE_LIST_INIT;
+ struct init_cb info = INIT_CB_INIT;
+
+ if (module_list_compute(argc, argv, suc.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 = suc.prefix;
+ info.superprefix = suc.recursive_prefix;
+ if (suc.quiet)
+ info.flags |= OPT_QUIET;
+
+ for_each_listed_submodule(&list, init_submodule_cb, &info);
+ }
+
ret = update_submodules(&suc);
list_objects_filter_release(&filter_options);
return ret;
@@ -2583,16 +2618,17 @@ static int update_clone(int argc, const char **argv, const char *prefix)
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,
+ OPT__QUIET(&update_data.quiet,
+ N_("suppress output for update by rebase or merge")),
+ OPT__FORCE(&update_data.force, N_("force checkout updates"),
+ 0),
+ OPT_BOOL('N', "no-fetch", &update_data.nofetch,
N_("don't fetch new objects from the remote site")),
- OPT_BOOL(0, "just-cloned", &just_cloned,
+ OPT_BOOL(0, "just-cloned", &update_data.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,
@@ -2607,9 +2643,8 @@ static int run_update_procedure(int argc, const char **argv, const char *prefix)
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_BOOL(0, "remote", &update_data.remote,
+ N_("use SHA-1 of submodule's remote tracking branch")),
OPT_END()
};
@@ -2623,10 +2658,6 @@ static int run_update_procedure(int argc, const char **argv, const char *prefix)
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)
@@ -2641,11 +2672,7 @@ static int run_update_procedure(int argc, const char **argv, const char *prefix)
&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;
+ return update_submodule2(&update_data);
}
static int resolve_relative_path(int argc, const char **argv, const char *prefix)
@@ -2697,23 +2724,6 @@ static const char *remote_submodule_branch(const char *path)
return branch;
}
-static int resolve_remote_submodule_branch(int argc, const char **argv,
- const char *prefix)
-{
- const char *ret;
- struct strbuf sb = STRBUF_INIT;
- if (argc != 2)
- die("submodule--helper remote-branch takes exactly one arguments, got %d", argc);
-
- ret = remote_submodule_branch(argv[1]);
- if (!ret)
- die("submodule %s doesn't exist", argv[1]);
-
- printf("%s", ret);
- strbuf_release(&sb);
- return 0;
-}
-
static int push_check(int argc, const char **argv, const char *prefix)
{
struct remote *remote;
@@ -2791,17 +2801,11 @@ 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);
@@ -2821,8 +2825,6 @@ 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)
@@ -3045,6 +3047,42 @@ static int module_create_branch(int argc, const char **argv, const char *prefix)
force, reflog, quiet, track, dry_run);
return 0;
}
+
+/* NEEDSWORK: this is a temporary name until we delete update_submodule() */
+static int update_submodule2(struct update_data *update_data)
+{
+ ensure_core_worktree(update_data->sm_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)
+ return do_run_update_procedure(update_data);
+
+ return 3;
+}
+
struct add_data {
const char *prefix;
const char *branch;
@@ -3433,20 +3471,16 @@ 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},
{"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},
- {"remote-branch", resolve_remote_submodule_branch, 0},
{"push-check", push_check, 0},
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
{"is-active", is_active, 0},
diff --git a/git-submodule.sh b/git-submodule.sh
index 87772ac891..aa8bdfca9d 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -247,20 +247,6 @@ 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
#
@@ -370,19 +356,11 @@ cmd_update()
shift
done
- if test -n "$filter" && test "$init" != "1"
- then
- usage
- fi
-
- if test -n "$init"
- then
- cmd_init "--" "$@" || return
- fi
-
{
- git submodule--helper update-clone ${GIT_QUIET:+--quiet} \
+ git ${wt_prefix:+-C "$wt_prefix"} submodule--helper update-clone \
+ ${GIT_QUIET:+--quiet} \
${progress:+"--progress"} \
+ ${init:+--init} \
${wt_prefix:+--prefix "$wt_prefix"} \
${prefix:+--recursive-prefix "$prefix"} \
${update:+--update "$update"} \
@@ -402,33 +380,11 @@ 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 \
@@ -441,7 +397,7 @@ cmd_update()
${update:+--update "$update"} \
${prefix:+--recursive-prefix "$prefix"} \
${sha1:+--oid "$sha1"} \
- ${subsha1:+--suboid "$subsha1"} \
+ ${remote:+--remote} \
"--" \
"$sm_path")
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 11cccbb333..000e055811 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -205,8 +205,18 @@ test_expect_success 'submodule update should fail due to local changes' '
(cd submodule &&
compare_head
) &&
- test_must_fail git submodule update submodule
- )
+ test_must_fail git submodule update submodule 2>../actual.raw
+ ) &&
+ sed "s/^> //" >expect <<-\EOF &&
+ > error: Your local changes to the following files would be overwritten by checkout:
+ > file
+ > Please commit your changes or stash them before you switch branches.
+ > Aborting
+ > fatal: Unable to checkout OID in submodule path '\''submodule'\''
+ EOF
+ sed -e "s/checkout $SQ[^$SQ]*$SQ/checkout OID/" <actual.raw >actual &&
+ test_cmp expect actual
+
'
test_expect_success 'submodule update should throw away changes with --force ' '
(cd super &&
@@ -1061,4 +1071,16 @@ test_expect_success 'submodule update --quiet passes quietness to fetch with a s
)
'
+test_expect_success 'submodule update --filter requires --init' '
+ test_expect_code 129 git -C super submodule update --filter blob:none
+'
+
+test_expect_success 'submodule update --filter sets partial clone settings' '
+ test_when_finished "rm -rf super-filter" &&
+ git clone cloned super-filter &&
+ git -C super-filter submodule update --init --filter blob:none &&
+ test_cmp_config -C super-filter/submodule true remote.origin.promisor &&
+ test_cmp_config -C super-filter/submodule blob:none remote.origin.partialclonefilter
+'
+
test_done
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
index a3892f494b..c3a4545510 100755
--- a/t/t7408-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
@@ -193,7 +193,19 @@ test_expect_success 'missing nested submodule alternate fails clone and submodul
cd supersuper-clone &&
check_that_two_of_three_alternates_are_used &&
# update of the submodule fails
- test_must_fail git submodule update --init --recursive
+ cat >expect <<-\EOF &&
+ fatal: submodule '\''sub'\'' cannot add alternate: path ... does not exist
+ Failed to clone '\''sub'\''. Retry scheduled
+ fatal: submodule '\''sub-dissociate'\'' cannot add alternate: path ... does not exist
+ Failed to clone '\''sub-dissociate'\''. Retry scheduled
+ fatal: submodule '\''sub'\'' cannot add alternate: path ... does not exist
+ Failed to clone '\''sub'\'' a second time, aborting
+ fatal: Failed to recurse into submodule path ...
+ EOF
+ test_must_fail git submodule update --init --recursive 2>err &&
+ grep -e fatal: -e ^Failed err >actual.raw &&
+ sed -e "s/path $SQ[^$SQ]*$SQ/path .../" <actual.raw >actual &&
+ test_cmp expect actual
)
'