Browse Source

clone: teach --recurse-submodules to optionally take a pathspec

Teach clone --recurse-submodules to optionally take a pathspec argument
which describes which submodules should be recursively initialized and
cloned.  If no pathspec is provided, --recurse-submodules will
recursively initialize and clone all submodules by using a default
pathspec of ".".  In order to construct more complex pathspecs,
--recurse-submodules can be given multiple times.

This also configures the 'submodule.active' configuration option to be
the given pathspec, such that any future invocation of `git submodule
update` will keep up with the pathspec.

Additionally the switch '--recurse' is removed from the Documentation as
well as marked hidden in the options array, to streamline the options
for submodules.  A simple '--recurse' doesn't convey what is being
recursed, e.g. it could mean directories or trees (c.f. ls-tree) In a
lot of other commands we already have '--recurse-submodules' to mean
recursing into submodules, so advertise this spelling here as the
genuine option.

Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Brandon Williams 8 years ago committed by Junio C Hamano
parent
commit
bb62e0a99f
  1. 14
      Documentation/git-clone.txt
  2. 50
      builtin/clone.c
  3. 68
      t/t7400-submodule-basic.sh

14
Documentation/git-clone.txt

@ -14,7 +14,7 @@ SYNOPSIS
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>] [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
[--dissociate] [--separate-git-dir <git dir>] [--dissociate] [--separate-git-dir <git dir>]
[--depth <depth>] [--[no-]single-branch] [--depth <depth>] [--[no-]single-branch]
[--recursive | --recurse-submodules] [--[no-]shallow-submodules] [--recurse-submodules] [--[no-]shallow-submodules]
[--jobs <n>] [--] <repository> [<directory>] [--jobs <n>] [--] <repository> [<directory>]


DESCRIPTION DESCRIPTION
@ -215,10 +215,14 @@ objects from the source repository into a pack in the cloned repository.
branch when `--single-branch` clone was made, no remote-tracking branch when `--single-branch` clone was made, no remote-tracking
branch is created. branch is created.


--recursive:: --recurse-submodules[=<pathspec]::
--recurse-submodules:: After the clone is created, initialize and clone submodules
After the clone is created, initialize all submodules within, within based on the provided pathspec. If no pathspec is
using their default settings. This is equivalent to running provided, all submodules are initialized and cloned.
Submodules are initialized and cloned using their default
settings. The resulting clone has `submodule.active` set to
the provided pathspec, or "." (meaning all submodules) if no
pathspec is provided. This is equivalent to running
`git submodule update --init --recursive` immediately after `git submodule update --init --recursive` immediately after
the clone is finished. This option is ignored if the cloned the clone is finished. This option is ignored if the cloned
repository does not have a worktree/checkout (i.e. if any of repository does not have a worktree/checkout (i.e. if any of

50
builtin/clone.c

@ -39,7 +39,7 @@ static const char * const builtin_clone_usage[] = {
}; };


static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
static int option_local = -1, option_no_hardlinks, option_shared, option_recursive; static int option_local = -1, option_no_hardlinks, option_shared;
static int option_shallow_submodules; static int option_shallow_submodules;
static int deepen; static int deepen;
static char *option_template, *option_depth, *option_since; static char *option_template, *option_depth, *option_since;
@ -56,6 +56,21 @@ static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP; static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
static int option_dissociate; static int option_dissociate;
static int max_jobs = -1; static int max_jobs = -1;
static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;

static int recurse_submodules_cb(const struct option *opt,
const char *arg, int unset)
{
if (unset)
string_list_clear((struct string_list *)opt->value, 0);
else if (arg)
string_list_append((struct string_list *)opt->value, arg);
else
string_list_append((struct string_list *)opt->value,
(const char *)opt->defval);

return 0;
}


static struct option builtin_clone_options[] = { static struct option builtin_clone_options[] = {
OPT__VERBOSITY(&option_verbosity), OPT__VERBOSITY(&option_verbosity),
@ -74,10 +89,13 @@ static struct option builtin_clone_options[] = {
N_("don't use local hardlinks, always copy")), N_("don't use local hardlinks, always copy")),
OPT_BOOL('s', "shared", &option_shared, OPT_BOOL('s', "shared", &option_shared,
N_("setup as shared repository")), N_("setup as shared repository")),
OPT_BOOL(0, "recursive", &option_recursive, { OPTION_CALLBACK, 0, "recursive", &option_recurse_submodules,
N_("initialize submodules in the clone")), N_("pathspec"), N_("initialize submodules in the clone"),
OPT_BOOL(0, "recurse-submodules", &option_recursive, PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, recurse_submodules_cb,
N_("initialize submodules in the clone")), (intptr_t)"." },
{ OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
N_("pathspec"), N_("initialize submodules in the clone"),
PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
OPT_INTEGER('j', "jobs", &max_jobs, OPT_INTEGER('j', "jobs", &max_jobs,
N_("number of submodules cloned in parallel")), N_("number of submodules cloned in parallel")),
OPT_STRING(0, "template", &option_template, N_("template-directory"), OPT_STRING(0, "template", &option_template, N_("template-directory"),
@ -733,7 +751,7 @@ static int checkout(int submodule_progress)
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1), err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
sha1_to_hex(sha1), "1", NULL); sha1_to_hex(sha1), "1", NULL);


if (!err && option_recursive) { if (!err && (option_recurse_submodules.nr > 0)) {
struct argv_array args = ARGV_ARRAY_INIT; struct argv_array args = ARGV_ARRAY_INIT;
argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL); argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);


@ -957,7 +975,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
fprintf(stderr, _("Cloning into '%s'...\n"), dir); fprintf(stderr, _("Cloning into '%s'...\n"), dir);
} }


if (option_recursive) { if (option_recurse_submodules.nr > 0) {
struct string_list_item *item;
struct strbuf sb = STRBUF_INIT;

/* remove duplicates */
string_list_sort(&option_recurse_submodules);
string_list_remove_duplicates(&option_recurse_submodules, 0);

/*
* NEEDSWORK: In a multi-working-tree world, this needs to be
* set in the per-worktree config.
*/
for_each_string_list_item(item, &option_recurse_submodules) {
strbuf_addf(&sb, "submodule.active=%s",
item->string);
string_list_append(&option_config,
strbuf_detach(&sb, NULL));
}

if (option_required_reference.nr && if (option_required_reference.nr &&
option_optional_reference.nr) option_optional_reference.nr)
die(_("clone --recursive is not compatible with " die(_("clone --recursive is not compatible with "

68
t/t7400-submodule-basic.sh

@ -1188,4 +1188,72 @@ test_expect_success 'submodule update and setting submodule.<name>.active' '
test_cmp expect actual test_cmp expect actual
' '


test_expect_success 'clone --recurse-submodules with a pathspec works' '
test_when_finished "rm -rf multisuper_clone" &&
cat >expected <<-\EOF &&
sub0 (test2)
-sub1
-sub2
-sub3
EOF

git clone --recurse-submodules="sub0" multisuper multisuper_clone &&
git -C multisuper_clone submodule status |cut -c1,43- >actual &&
test_cmp actual expected
'

test_expect_success 'clone with multiple --recurse-submodules options' '
test_when_finished "rm -rf multisuper_clone" &&
cat >expect <<-\EOF &&
-sub0
sub1 (test2)
-sub2
sub3 (test2)
EOF

git clone --recurse-submodules="." \
--recurse-submodules=":(exclude)sub0" \
--recurse-submodules=":(exclude)sub2" \
multisuper multisuper_clone &&
git -C multisuper_clone submodule status |cut -c1,43- >actual &&
test_cmp expect actual
'

test_expect_success 'clone and subsequent updates correctly auto-initialize submodules' '
test_when_finished "rm -rf multisuper_clone" &&
cat <<-\EOF >expect &&
-sub0
sub1 (test2)
-sub2
sub3 (test2)
EOF

cat <<-\EOF >expect2 &&
-sub0
sub1 (test2)
-sub2
sub3 (test2)
-sub4
sub5 (test2)
EOF

git clone --recurse-submodules="." \
--recurse-submodules=":(exclude)sub0" \
--recurse-submodules=":(exclude)sub2" \
--recurse-submodules=":(exclude)sub4" \
multisuper multisuper_clone &&

git -C multisuper_clone submodule status |cut -c1,43- >actual &&
test_cmp expect actual &&

git -C multisuper submodule add ../sub1 sub4 &&
git -C multisuper submodule add ../sub1 sub5 &&
git -C multisuper commit -m "add more submodules" &&
# obtain the new superproject
git -C multisuper_clone pull &&
git -C multisuper_clone submodule update --init &&
git -C multisuper_clone submodule status |cut -c1,43- >actual &&
test_cmp expect2 actual
'

test_done test_done

Loading…
Cancel
Save