Skip to content

Commit bb62e0a

Browse files
bmwillgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3e7eaed commit bb62e0a

File tree

3 files changed

+120
-12
lines changed

3 files changed

+120
-12
lines changed

Documentation/git-clone.txt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ SYNOPSIS
1414
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
1515
[--dissociate] [--separate-git-dir <git dir>]
1616
[--depth <depth>] [--[no-]single-branch]
17-
[--recursive | --recurse-submodules] [--[no-]shallow-submodules]
17+
[--recurse-submodules] [--[no-]shallow-submodules]
1818
[--jobs <n>] [--] <repository> [<directory>]
1919

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

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

builtin/clone.c

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ static const char * const builtin_clone_usage[] = {
3939
};
4040

4141
static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
42-
static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
42+
static int option_local = -1, option_no_hardlinks, option_shared;
4343
static int option_shallow_submodules;
4444
static int deepen;
4545
static char *option_template, *option_depth, *option_since;
@@ -56,6 +56,21 @@ static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
5656
static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
5757
static int option_dissociate;
5858
static int max_jobs = -1;
59+
static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
60+
61+
static int recurse_submodules_cb(const struct option *opt,
62+
const char *arg, int unset)
63+
{
64+
if (unset)
65+
string_list_clear((struct string_list *)opt->value, 0);
66+
else if (arg)
67+
string_list_append((struct string_list *)opt->value, arg);
68+
else
69+
string_list_append((struct string_list *)opt->value,
70+
(const char *)opt->defval);
71+
72+
return 0;
73+
}
5974

6075
static struct option builtin_clone_options[] = {
6176
OPT__VERBOSITY(&option_verbosity),
@@ -74,10 +89,13 @@ static struct option builtin_clone_options[] = {
7489
N_("don't use local hardlinks, always copy")),
7590
OPT_BOOL('s', "shared", &option_shared,
7691
N_("setup as shared repository")),
77-
OPT_BOOL(0, "recursive", &option_recursive,
78-
N_("initialize submodules in the clone")),
79-
OPT_BOOL(0, "recurse-submodules", &option_recursive,
80-
N_("initialize submodules in the clone")),
92+
{ OPTION_CALLBACK, 0, "recursive", &option_recurse_submodules,
93+
N_("pathspec"), N_("initialize submodules in the clone"),
94+
PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, recurse_submodules_cb,
95+
(intptr_t)"." },
96+
{ OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
97+
N_("pathspec"), N_("initialize submodules in the clone"),
98+
PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
8199
OPT_INTEGER('j', "jobs", &max_jobs,
82100
N_("number of submodules cloned in parallel")),
83101
OPT_STRING(0, "template", &option_template, N_("template-directory"),
@@ -733,7 +751,7 @@ static int checkout(int submodule_progress)
733751
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
734752
sha1_to_hex(sha1), "1", NULL);
735753

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

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

960-
if (option_recursive) {
978+
if (option_recurse_submodules.nr > 0) {
979+
struct string_list_item *item;
980+
struct strbuf sb = STRBUF_INIT;
981+
982+
/* remove duplicates */
983+
string_list_sort(&option_recurse_submodules);
984+
string_list_remove_duplicates(&option_recurse_submodules, 0);
985+
986+
/*
987+
* NEEDSWORK: In a multi-working-tree world, this needs to be
988+
* set in the per-worktree config.
989+
*/
990+
for_each_string_list_item(item, &option_recurse_submodules) {
991+
strbuf_addf(&sb, "submodule.active=%s",
992+
item->string);
993+
string_list_append(&option_config,
994+
strbuf_detach(&sb, NULL));
995+
}
996+
961997
if (option_required_reference.nr &&
962998
option_optional_reference.nr)
963999
die(_("clone --recursive is not compatible with "

t/t7400-submodule-basic.sh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,4 +1188,72 @@ test_expect_success 'submodule update and setting submodule.<name>.active' '
11881188
test_cmp expect actual
11891189
'
11901190

1191+
test_expect_success 'clone --recurse-submodules with a pathspec works' '
1192+
test_when_finished "rm -rf multisuper_clone" &&
1193+
cat >expected <<-\EOF &&
1194+
sub0 (test2)
1195+
-sub1
1196+
-sub2
1197+
-sub3
1198+
EOF
1199+
1200+
git clone --recurse-submodules="sub0" multisuper multisuper_clone &&
1201+
git -C multisuper_clone submodule status |cut -c1,43- >actual &&
1202+
test_cmp actual expected
1203+
'
1204+
1205+
test_expect_success 'clone with multiple --recurse-submodules options' '
1206+
test_when_finished "rm -rf multisuper_clone" &&
1207+
cat >expect <<-\EOF &&
1208+
-sub0
1209+
sub1 (test2)
1210+
-sub2
1211+
sub3 (test2)
1212+
EOF
1213+
1214+
git clone --recurse-submodules="." \
1215+
--recurse-submodules=":(exclude)sub0" \
1216+
--recurse-submodules=":(exclude)sub2" \
1217+
multisuper multisuper_clone &&
1218+
git -C multisuper_clone submodule status |cut -c1,43- >actual &&
1219+
test_cmp expect actual
1220+
'
1221+
1222+
test_expect_success 'clone and subsequent updates correctly auto-initialize submodules' '
1223+
test_when_finished "rm -rf multisuper_clone" &&
1224+
cat <<-\EOF >expect &&
1225+
-sub0
1226+
sub1 (test2)
1227+
-sub2
1228+
sub3 (test2)
1229+
EOF
1230+
1231+
cat <<-\EOF >expect2 &&
1232+
-sub0
1233+
sub1 (test2)
1234+
-sub2
1235+
sub3 (test2)
1236+
-sub4
1237+
sub5 (test2)
1238+
EOF
1239+
1240+
git clone --recurse-submodules="." \
1241+
--recurse-submodules=":(exclude)sub0" \
1242+
--recurse-submodules=":(exclude)sub2" \
1243+
--recurse-submodules=":(exclude)sub4" \
1244+
multisuper multisuper_clone &&
1245+
1246+
git -C multisuper_clone submodule status |cut -c1,43- >actual &&
1247+
test_cmp expect actual &&
1248+
1249+
git -C multisuper submodule add ../sub1 sub4 &&
1250+
git -C multisuper submodule add ../sub1 sub5 &&
1251+
git -C multisuper commit -m "add more submodules" &&
1252+
# obtain the new superproject
1253+
git -C multisuper_clone pull &&
1254+
git -C multisuper_clone submodule update --init &&
1255+
git -C multisuper_clone submodule status |cut -c1,43- >actual &&
1256+
test_cmp expect2 actual
1257+
'
1258+
11911259
test_done

0 commit comments

Comments
 (0)