Skip to content

Commit c9c63ee

Browse files
committed
Merge branch 'sb/pull-rebase-submodule'
"git pull --rebase --recurse-submodules" learns to rebase the branch in the submodules to an updated base. * sb/pull-rebase-submodule: builtin/fetch cleanup: always set default value for submodule recursing pull: optionally rebase submodules (remote submodule changes only) builtin/fetch: parse recurse-submodules-default at default options parsing builtin/fetch: factor submodule recurse parsing out to submodule config
2 parents 91f6922 + e8906a9 commit c9c63ee

File tree

8 files changed

+193
-41
lines changed

8 files changed

+193
-41
lines changed

Documentation/git-pull.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ OPTIONS
8686

8787
--[no-]recurse-submodules[=yes|on-demand|no]::
8888
This option controls if new commits of all populated submodules should
89-
be fetched too (see linkgit:git-config[1] and linkgit:gitmodules[5]).
90-
That might be necessary to get the data needed for merging submodule
91-
commits, a feature Git learned in 1.7.3. Notice that the result of a
92-
merge will not be checked out in the submodule, "git submodule update"
93-
has to be called afterwards to bring the work tree up to date with the
94-
merge result.
89+
be fetched and updated, too (see linkgit:git-config[1] and
90+
linkgit:gitmodules[5]).
91+
+
92+
If the checkout is done via rebase, local submodule commits are rebased as well.
93+
+
94+
If the update is done via merge, the submodule conflicts are resolved and checked out.
9595

9696
Options related to merging
9797
~~~~~~~~~~~~~~~~~~~~~~~~~~

builtin/fetch.c

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ static int prune = -1; /* unspecified */
3737
#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
3838

3939
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
40-
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
40+
static int progress = -1;
4141
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
4242
static int max_children = -1;
4343
static enum transport_family family;
@@ -49,25 +49,12 @@ static struct strbuf default_rla = STRBUF_INIT;
4949
static struct transport *gtransport;
5050
static struct transport *gsecondary;
5151
static const char *submodule_prefix = "";
52-
static const char *recurse_submodules_default;
52+
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
53+
static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
5354
static int shown_url = 0;
5455
static int refmap_alloc, refmap_nr;
5556
static const char **refmap_array;
5657

57-
static int option_parse_recurse_submodules(const struct option *opt,
58-
const char *arg, int unset)
59-
{
60-
if (unset) {
61-
recurse_submodules = RECURSE_SUBMODULES_OFF;
62-
} else {
63-
if (arg)
64-
recurse_submodules = parse_fetch_recurse_submodules_arg(opt->long_name, arg);
65-
else
66-
recurse_submodules = RECURSE_SUBMODULES_ON;
67-
}
68-
return 0;
69-
}
70-
7158
static int git_fetch_config(const char *k, const char *v, void *cb)
7259
{
7360
if (!strcmp(k, "fetch.prune")) {
@@ -116,9 +103,9 @@ static struct option builtin_fetch_options[] = {
116103
N_("number of submodules fetched in parallel")),
117104
OPT_BOOL('p', "prune", &prune,
118105
N_("prune remote-tracking branches no longer on remote")),
119-
{ OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"),
106+
{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
120107
N_("control recursive fetching of submodules"),
121-
PARSE_OPT_OPTARG, option_parse_recurse_submodules },
108+
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
122109
OPT_BOOL(0, "dry-run", &dry_run,
123110
N_("dry run")),
124111
OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
@@ -138,9 +125,11 @@ static struct option builtin_fetch_options[] = {
138125
PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
139126
{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
140127
N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
141-
{ OPTION_STRING, 0, "recurse-submodules-default",
142-
&recurse_submodules_default, NULL,
143-
N_("default mode for recursion"), PARSE_OPT_HIDDEN },
128+
{ OPTION_CALLBACK, 0, "recurse-submodules-default",
129+
&recurse_submodules_default, N_("on-demand"),
130+
N_("default for recursive fetching of submodules "
131+
"(lower priority than config files)"),
132+
PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules },
144133
OPT_BOOL(0, "update-shallow", &update_shallow,
145134
N_("accept refs that update .git/shallow")),
146135
{ OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
@@ -1350,10 +1339,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
13501339
deepen = 1;
13511340

13521341
if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
1353-
if (recurse_submodules_default) {
1354-
int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
1355-
set_config_fetch_recurse_submodules(arg);
1356-
}
1342+
set_config_fetch_recurse_submodules(recurse_submodules_default);
13571343
gitmodules_config();
13581344
git_config(submodule_config, NULL);
13591345
}

builtin/pull.c

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include "dir.h"
1717
#include "refs.h"
1818
#include "revision.h"
19+
#include "submodule.h"
20+
#include "submodule-config.h"
1921
#include "tempfile.h"
2022
#include "lockfile.h"
2123
#include "wt-status.h"
@@ -78,6 +80,7 @@ static const char * const pull_usage[] = {
7880
/* Shared options */
7981
static int opt_verbosity;
8082
static char *opt_progress;
83+
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
8184

8285
/* Options passed to git-merge or git-rebase */
8386
static enum rebase_type opt_rebase = -1;
@@ -102,7 +105,6 @@ static char *opt_upload_pack;
102105
static int opt_force;
103106
static char *opt_tags;
104107
static char *opt_prune;
105-
static char *opt_recurse_submodules;
106108
static char *max_children;
107109
static int opt_dry_run;
108110
static char *opt_keep;
@@ -117,6 +119,10 @@ static struct option pull_options[] = {
117119
OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
118120
N_("force progress reporting"),
119121
PARSE_OPT_NOARG),
122+
{ OPTION_CALLBACK, 0, "recurse-submodules",
123+
&recurse_submodules, N_("on-demand"),
124+
N_("control for recursive fetching of submodules"),
125+
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
120126

121127
/* Options passed to git-merge or git-rebase */
122128
OPT_GROUP(N_("Options related to merging")),
@@ -188,10 +194,6 @@ static struct option pull_options[] = {
188194
OPT_PASSTHRU('p', "prune", &opt_prune, NULL,
189195
N_("prune remote-tracking branches no longer on remote"),
190196
PARSE_OPT_NOARG),
191-
OPT_PASSTHRU(0, "recurse-submodules", &opt_recurse_submodules,
192-
N_("on-demand"),
193-
N_("control recursive fetching of submodules"),
194-
PARSE_OPT_OPTARG),
195197
OPT_PASSTHRU('j', "jobs", &max_children, N_("n"),
196198
N_("number of submodules pulled in parallel"),
197199
PARSE_OPT_OPTARG),
@@ -484,8 +486,20 @@ static int run_fetch(const char *repo, const char **refspecs)
484486
argv_array_push(&args, opt_tags);
485487
if (opt_prune)
486488
argv_array_push(&args, opt_prune);
487-
if (opt_recurse_submodules)
488-
argv_array_push(&args, opt_recurse_submodules);
489+
if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
490+
switch (recurse_submodules) {
491+
case RECURSE_SUBMODULES_ON:
492+
argv_array_push(&args, "--recurse-submodules=on");
493+
break;
494+
case RECURSE_SUBMODULES_OFF:
495+
argv_array_push(&args, "--recurse-submodules=no");
496+
break;
497+
case RECURSE_SUBMODULES_ON_DEMAND:
498+
argv_array_push(&args, "--recurse-submodules=on-demand");
499+
break;
500+
default:
501+
BUG("submodule recursion option not understood");
502+
}
489503
if (max_children)
490504
argv_array_push(&args, max_children);
491505
if (opt_dry_run)
@@ -532,6 +546,30 @@ static int pull_into_void(const struct object_id *merge_head,
532546
return 0;
533547
}
534548

549+
static int rebase_submodules(void)
550+
{
551+
struct child_process cp = CHILD_PROCESS_INIT;
552+
553+
cp.git_cmd = 1;
554+
cp.no_stdin = 1;
555+
argv_array_pushl(&cp.args, "submodule", "update",
556+
"--recursive", "--rebase", NULL);
557+
558+
return run_command(&cp);
559+
}
560+
561+
static int update_submodules(void)
562+
{
563+
struct child_process cp = CHILD_PROCESS_INIT;
564+
565+
cp.git_cmd = 1;
566+
cp.no_stdin = 1;
567+
argv_array_pushl(&cp.args, "submodule", "update",
568+
"--recursive", "--checkout", NULL);
569+
570+
return run_command(&cp);
571+
}
572+
535573
/**
536574
* Runs git-merge, returning its exit status.
537575
*/
@@ -863,6 +901,11 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
863901
die(_("Cannot rebase onto multiple branches."));
864902

865903
if (opt_rebase) {
904+
int ret = 0;
905+
if ((recurse_submodules == RECURSE_SUBMODULES_ON ||
906+
recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) &&
907+
submodule_touches_in_range(&rebase_fork_point, &curr_head))
908+
die(_("cannot rebase with locally recorded submodule modifications"));
866909
if (!autostash) {
867910
struct commit_list *list = NULL;
868911
struct commit *merge_head, *head;
@@ -873,11 +916,21 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
873916
if (is_descendant_of(merge_head, list)) {
874917
/* we can fast-forward this without invoking rebase */
875918
opt_ff = "--ff-only";
876-
return run_merge();
919+
ret = run_merge();
877920
}
878921
}
879-
return run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
922+
ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
923+
924+
if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
925+
recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
926+
ret = rebase_submodules();
927+
928+
return ret;
880929
} else {
881-
return run_merge();
930+
int ret = run_merge();
931+
if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
932+
recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
933+
ret = update_submodules();
934+
return ret;
882935
}
883936
}

submodule-config.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "submodule-config.h"
55
#include "submodule.h"
66
#include "strbuf.h"
7+
#include "parse-options.h"
78

89
/*
910
* submodule cache lookup structure
@@ -252,6 +253,27 @@ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
252253
return parse_fetch_recurse(opt, arg, 1);
253254
}
254255

256+
int option_fetch_parse_recurse_submodules(const struct option *opt,
257+
const char *arg, int unset)
258+
{
259+
int *v;
260+
261+
if (!opt->value)
262+
return -1;
263+
264+
v = opt->value;
265+
266+
if (unset) {
267+
*v = RECURSE_SUBMODULES_OFF;
268+
} else {
269+
if (arg)
270+
*v = parse_fetch_recurse_submodules_arg(opt->long_name, arg);
271+
else
272+
*v = RECURSE_SUBMODULES_ON;
273+
}
274+
return 0;
275+
}
276+
255277
static int parse_update_recurse(const char *opt, const char *arg,
256278
int die_on_error)
257279
{

submodule-config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ struct repository;
2828
extern void submodule_cache_free(struct submodule_cache *cache);
2929

3030
extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
31+
struct option;
32+
extern int option_fetch_parse_recurse_submodules(const struct option *opt,
33+
const char *arg, int unset);
3134
extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
3235
extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
3336
extern int parse_submodule_config_option(const char *var, const char *value);

submodule.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,32 @@ static void calculate_changed_submodule_paths(void)
11381138
initialized_fetch_ref_tips = 0;
11391139
}
11401140

1141+
int submodule_touches_in_range(struct object_id *excl_oid,
1142+
struct object_id *incl_oid)
1143+
{
1144+
struct string_list subs = STRING_LIST_INIT_DUP;
1145+
struct argv_array args = ARGV_ARRAY_INIT;
1146+
int ret;
1147+
1148+
gitmodules_config();
1149+
/* No need to check if there are no submodules configured */
1150+
if (!submodule_from_path(NULL, NULL))
1151+
return 0;
1152+
1153+
argv_array_push(&args, "--"); /* args[0] program name */
1154+
argv_array_push(&args, oid_to_hex(incl_oid));
1155+
argv_array_push(&args, "--not");
1156+
argv_array_push(&args, oid_to_hex(excl_oid));
1157+
1158+
collect_changed_submodules(&subs, &args);
1159+
ret = subs.nr;
1160+
1161+
argv_array_clear(&args);
1162+
1163+
free_submodules_oids(&subs);
1164+
return ret;
1165+
}
1166+
11411167
struct submodule_parallel_fetch {
11421168
int count;
11431169
struct argv_array args;

submodule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ extern int merge_submodule(struct object_id *result, const char *path,
9999
const struct object_id *base,
100100
const struct object_id *a,
101101
const struct object_id *b, int search);
102+
103+
/* Checks if there are submodule changes in a..b. */
104+
extern int submodule_touches_in_range(struct object_id *a,
105+
struct object_id *b);
102106
extern int find_unpushed_submodules(struct oid_array *commits,
103107
const char *remotes_name,
104108
struct string_list *needs_pushing);

t/t5572-pull-submodule.sh

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,62 @@ KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
4242
KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
4343
test_submodule_switch "git_pull_noff"
4444

45+
test_expect_success 'pull --recurse-submodule setup' '
46+
test_create_repo child &&
47+
test_commit -C child bar &&
48+
49+
test_create_repo parent &&
50+
test_commit -C child foo &&
51+
52+
git -C parent submodule add ../child sub &&
53+
git -C parent commit -m "add submodule" &&
54+
55+
git clone --recurse-submodules parent super
56+
'
57+
58+
test_expect_success 'recursive pull updates working tree' '
59+
test_commit -C child merge_strategy &&
60+
git -C parent submodule update --remote &&
61+
git -C parent add sub &&
62+
git -C parent commit -m "update submodule" &&
63+
64+
git -C super pull --no-rebase --recurse-submodules &&
65+
test_path_is_file super/sub/merge_strategy.t
66+
'
67+
68+
test_expect_success 'recursive rebasing pull' '
69+
# change upstream
70+
test_commit -C child rebase_strategy &&
71+
git -C parent submodule update --remote &&
72+
git -C parent add sub &&
73+
git -C parent commit -m "update submodule" &&
74+
75+
# also have local commits
76+
test_commit -C super/sub local_stuff &&
77+
78+
git -C super pull --rebase --recurse-submodules &&
79+
test_path_is_file super/sub/rebase_strategy.t &&
80+
test_path_is_file super/sub/local_stuff.t
81+
'
82+
83+
test_expect_success 'pull rebase recursing fails with conflicts' '
84+
85+
# local changes in submodule recorded in superproject:
86+
test_commit -C super/sub local_stuff_2 &&
87+
git -C super add sub &&
88+
git -C super commit -m "local update submodule" &&
89+
90+
# and in the remote as well:
91+
test_commit -C child important_upstream_work &&
92+
git -C parent submodule update --remote &&
93+
git -C parent add sub &&
94+
git -C parent commit -m "remote update submodule" &&
95+
96+
# Unfortunately we fail here, despite no conflict in the
97+
# submodule itself, but the merge strategy in submodules
98+
# does not support rebase:
99+
test_must_fail git -C super pull --rebase --recurse-submodules 2>err &&
100+
test_i18ngrep "locally recorded submodule modifications" err
101+
'
102+
45103
test_done

0 commit comments

Comments
 (0)