Skip to content

Commit e394fa0

Browse files
committed
Merge branch 'sb/checkout-recurse-submodules'
"git checkout" is taught the "--recurse-submodules" option. * sb/checkout-recurse-submodules: builtin/read-tree: add --recurse-submodules switch builtin/checkout: add --recurse-submodules switch entry.c: create submodules when interesting unpack-trees: check if we can perform the operation for submodules unpack-trees: pass old oid to verify_clean_submodule update submodules: add submodule_move_head submodule.c: get_super_prefix_or_empty update submodules: move up prepare_submodule_repo_env submodules: introduce check to see whether to touch a submodule update submodules: add a config option to determine if submodules are updated update submodules: add submodule config parsing make is_submodule_populated gently lib-submodule-update.sh: define tests for recursing into submodules lib-submodule-update.sh: replace sha1 by hash lib-submodule-update: teach test_submodule_content the -C <dir> flag lib-submodule-update.sh: do not use ./. as submodule remote lib-submodule-update.sh: reorder create_lib_submodule_repo submodule--helper.c: remove duplicate code connect_work_tree_and_git_dir: safely create leading directories
2 parents ff8b7e6 + 2580491 commit e394fa0

17 files changed

+1084
-113
lines changed

Documentation/git-checkout.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,13 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
256256
out anyway. In other words, the ref can be held by more than one
257257
worktree.
258258

259+
--[no-]recurse-submodules::
260+
Using --recurse-submodules will update the content of all initialized
261+
submodules according to the commit recorded in the superproject. If
262+
local modifications in a submodule would be overwritten the checkout
263+
will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
264+
is used, the work trees of submodules will not be updated.
265+
259266
<branch>::
260267
Branch to checkout; if it refers to a branch (i.e., a name that,
261268
when prepended with "refs/heads/", is a valid ref), then that

Documentation/git-read-tree.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ OPTIONS
115115
directories the index file and index output file are
116116
located in.
117117

118+
--[no-]recurse-submodules::
119+
Using --recurse-submodules will update the content of all initialized
120+
submodules according to the commit recorded in the superproject by
121+
calling read-tree recursively, also setting the submodules HEAD to be
122+
detached at that commit.
123+
118124
--no-sparse-checkout::
119125
Disable sparse checkout support even if `core.sparseCheckout`
120126
is true.

builtin/checkout.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,31 @@
2121
#include "submodule-config.h"
2222
#include "submodule.h"
2323

24+
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
25+
2426
static const char * const checkout_usage[] = {
2527
N_("git checkout [<options>] <branch>"),
2628
N_("git checkout [<options>] [<branch>] -- <file>..."),
2729
NULL,
2830
};
2931

32+
static int option_parse_recurse_submodules(const struct option *opt,
33+
const char *arg, int unset)
34+
{
35+
if (unset) {
36+
recurse_submodules = RECURSE_SUBMODULES_OFF;
37+
return 0;
38+
}
39+
if (arg)
40+
recurse_submodules =
41+
parse_update_recurse_submodules_arg(opt->long_name,
42+
arg);
43+
else
44+
recurse_submodules = RECURSE_SUBMODULES_ON;
45+
46+
return 0;
47+
}
48+
3049
struct checkout_opts {
3150
int patch_mode;
3251
int quiet;
@@ -1163,6 +1182,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
11631182
N_("second guess 'git checkout <no-such-branch>'")),
11641183
OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
11651184
N_("do not check if another worktree is holding the given ref")),
1185+
{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
1186+
"checkout", "control recursive updating of submodules",
1187+
PARSE_OPT_OPTARG, option_parse_recurse_submodules },
11661188
OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
11671189
OPT_END(),
11681190
};
@@ -1193,6 +1215,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
11931215
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
11941216
}
11951217

1218+
if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
1219+
git_config(submodule_config, NULL);
1220+
if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
1221+
set_config_update_recurse_submodules(recurse_submodules);
1222+
}
1223+
11961224
if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
11971225
die(_("-b, -B and --orphan are mutually exclusive"));
11981226

builtin/grep.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1,
618618
{
619619
if (!is_submodule_initialized(path))
620620
return 0;
621-
if (!is_submodule_populated(path)) {
621+
if (!is_submodule_populated_gently(path, NULL)) {
622622
/*
623623
* If searching history, check for the presense of the
624624
* submodule's gitdir before skipping the submodule.

builtin/read-tree.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@
1515
#include "builtin.h"
1616
#include "parse-options.h"
1717
#include "resolve-undo.h"
18+
#include "submodule.h"
19+
#include "submodule-config.h"
1820

1921
static int nr_trees;
2022
static int read_empty;
2123
static struct tree *trees[MAX_UNPACK_TREES];
24+
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
2225

2326
static int list_tree(unsigned char *sha1)
2427
{
@@ -96,6 +99,23 @@ static int debug_merge(const struct cache_entry * const *stages,
9699
return 0;
97100
}
98101

102+
static int option_parse_recurse_submodules(const struct option *opt,
103+
const char *arg, int unset)
104+
{
105+
if (unset) {
106+
recurse_submodules = RECURSE_SUBMODULES_OFF;
107+
return 0;
108+
}
109+
if (arg)
110+
recurse_submodules =
111+
parse_update_recurse_submodules_arg(opt->long_name,
112+
arg);
113+
else
114+
recurse_submodules = RECURSE_SUBMODULES_ON;
115+
116+
return 0;
117+
}
118+
99119
static struct lock_file lock_file;
100120

101121
int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
@@ -137,6 +157,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
137157
N_("skip applying sparse checkout filter")),
138158
OPT_BOOL(0, "debug-unpack", &opts.debug_unpack,
139159
N_("debug unpack-trees")),
160+
{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
161+
"checkout", "control recursive updating of submodules",
162+
PARSE_OPT_OPTARG, option_parse_recurse_submodules },
140163
OPT_END()
141164
};
142165

@@ -152,6 +175,12 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
152175

153176
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
154177

178+
if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) {
179+
gitmodules_config();
180+
git_config(submodule_config, NULL);
181+
set_config_update_recurse_submodules(RECURSE_SUBMODULES_ON);
182+
}
183+
155184
prefix_set = opts.prefix ? 1 : 0;
156185
if (1 < opts.merge + opts.reset + prefix_set)
157186
die("Which one? -m, --reset, or --prefix?");

builtin/submodule--helper.c

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -577,9 +577,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
577577
const char *name = NULL, *url = NULL, *depth = NULL;
578578
int quiet = 0;
579579
int progress = 0;
580-
FILE *submodule_dot_git;
581580
char *p, *path = NULL, *sm_gitdir;
582-
struct strbuf rel_path = STRBUF_INIT;
583581
struct strbuf sb = STRBUF_INIT;
584582
struct string_list reference = STRING_LIST_INIT_NODUP;
585583
char *sm_alternate = NULL, *error_strategy = NULL;
@@ -651,27 +649,12 @@ static int module_clone(int argc, const char **argv, const char *prefix)
651649
strbuf_reset(&sb);
652650
}
653651

654-
/* Write a .git file in the submodule to redirect to the superproject. */
655-
strbuf_addf(&sb, "%s/.git", path);
656-
if (safe_create_leading_directories_const(sb.buf) < 0)
657-
die(_("could not create leading directories of '%s'"), sb.buf);
658-
submodule_dot_git = fopen(sb.buf, "w");
659-
if (!submodule_dot_git)
660-
die_errno(_("cannot open file '%s'"), sb.buf);
661-
662-
fprintf_or_die(submodule_dot_git, "gitdir: %s\n",
663-
relative_path(sm_gitdir, path, &rel_path));
664-
if (fclose(submodule_dot_git))
665-
die(_("could not close file %s"), sb.buf);
666-
strbuf_reset(&sb);
667-
strbuf_reset(&rel_path);
652+
/* Connect module worktree and git dir */
653+
connect_work_tree_and_git_dir(path, sm_gitdir);
668654

669-
/* Redirect the worktree of the submodule in the superproject's config */
670655
p = git_pathdup_submodule(path, "config");
671656
if (!p)
672657
die(_("could not get submodule directory for '%s'"), path);
673-
git_config_set_in_file(p, "core.worktree",
674-
relative_path(path, sm_gitdir, &rel_path));
675658

676659
/* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
677660
git_config_get_string("submodule.alternateLocation", &sm_alternate);
@@ -687,7 +670,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
687670
free(error_strategy);
688671

689672
strbuf_release(&sb);
690-
strbuf_release(&rel_path);
691673
free(sm_gitdir);
692674
free(path);
693675
free(p);

dir.c

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2765,23 +2765,33 @@ void untracked_cache_add_to_index(struct index_state *istate,
27652765
/* Update gitfile and core.worktree setting to connect work tree and git dir */
27662766
void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
27672767
{
2768-
struct strbuf file_name = STRBUF_INIT;
2768+
struct strbuf gitfile_sb = STRBUF_INIT;
2769+
struct strbuf cfg_sb = STRBUF_INIT;
27692770
struct strbuf rel_path = STRBUF_INIT;
2770-
char *git_dir = real_pathdup(git_dir_, 1);
2771-
char *work_tree = real_pathdup(work_tree_, 1);
2771+
char *git_dir, *work_tree;
27722772

2773-
/* Update gitfile */
2774-
strbuf_addf(&file_name, "%s/.git", work_tree);
2775-
write_file(file_name.buf, "gitdir: %s",
2776-
relative_path(git_dir, work_tree, &rel_path));
2773+
/* Prepare .git file */
2774+
strbuf_addf(&gitfile_sb, "%s/.git", work_tree_);
2775+
if (safe_create_leading_directories_const(gitfile_sb.buf))
2776+
die(_("could not create directories for %s"), gitfile_sb.buf);
2777+
2778+
/* Prepare config file */
2779+
strbuf_addf(&cfg_sb, "%s/config", git_dir_);
2780+
if (safe_create_leading_directories_const(cfg_sb.buf))
2781+
die(_("could not create directories for %s"), cfg_sb.buf);
27772782

2783+
git_dir = real_pathdup(git_dir_, 1);
2784+
work_tree = real_pathdup(work_tree_, 1);
2785+
2786+
/* Write .git file */
2787+
write_file(gitfile_sb.buf, "gitdir: %s",
2788+
relative_path(git_dir, work_tree, &rel_path));
27782789
/* Update core.worktree setting */
2779-
strbuf_reset(&file_name);
2780-
strbuf_addf(&file_name, "%s/config", git_dir);
2781-
git_config_set_in_file(file_name.buf, "core.worktree",
2790+
git_config_set_in_file(cfg_sb.buf, "core.worktree",
27822791
relative_path(work_tree, git_dir, &rel_path));
27832792

2784-
strbuf_release(&file_name);
2793+
strbuf_release(&gitfile_sb);
2794+
strbuf_release(&cfg_sb);
27852795
strbuf_release(&rel_path);
27862796
free(work_tree);
27872797
free(git_dir);

entry.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "blob.h"
33
#include "dir.h"
44
#include "streaming.h"
5+
#include "submodule.h"
56

67
static void create_directories(const char *path, int path_len,
78
const struct checkout *state)
@@ -146,6 +147,7 @@ static int write_entry(struct cache_entry *ce,
146147
unsigned long size;
147148
size_t wrote, newsize = 0;
148149
struct stat st;
150+
const struct submodule *sub;
149151

150152
if (ce_mode_s_ifmt == S_IFREG) {
151153
struct stream_filter *filter = get_stream_filter(ce->name,
@@ -203,6 +205,10 @@ static int write_entry(struct cache_entry *ce,
203205
return error("cannot create temporary submodule %s", path);
204206
if (mkdir(path, 0777) < 0)
205207
return error("cannot create submodule directory %s", path);
208+
sub = submodule_from_ce(ce);
209+
if (sub)
210+
return submodule_move_head(ce->name,
211+
NULL, oid_to_hex(&ce->oid), SUBMODULE_MOVE_HEAD_FORCE);
206212
break;
207213
default:
208214
return error("unknown file mode for %s in index", path);
@@ -259,7 +265,31 @@ int checkout_entry(struct cache_entry *ce,
259265
strbuf_add(&path, ce->name, ce_namelen(ce));
260266

261267
if (!check_path(path.buf, path.len, &st, state->base_dir_len)) {
268+
const struct submodule *sub;
262269
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
270+
/*
271+
* Needs to be checked before !changed returns early,
272+
* as the possibly empty directory was not changed
273+
*/
274+
sub = submodule_from_ce(ce);
275+
if (sub) {
276+
int err;
277+
if (!is_submodule_populated_gently(ce->name, &err)) {
278+
struct stat sb;
279+
if (lstat(ce->name, &sb))
280+
die(_("could not stat file '%s'"), ce->name);
281+
if (!(st.st_mode & S_IFDIR))
282+
unlink_or_warn(ce->name);
283+
284+
return submodule_move_head(ce->name,
285+
NULL, oid_to_hex(&ce->oid),
286+
SUBMODULE_MOVE_HEAD_FORCE);
287+
} else
288+
return submodule_move_head(ce->name,
289+
"HEAD", oid_to_hex(&ce->oid),
290+
SUBMODULE_MOVE_HEAD_FORCE);
291+
}
292+
263293
if (!changed)
264294
return 0;
265295
if (!state->force) {

submodule-config.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,26 @@ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
234234
return parse_fetch_recurse(opt, arg, 1);
235235
}
236236

237+
static int parse_update_recurse(const char *opt, const char *arg,
238+
int die_on_error)
239+
{
240+
switch (git_config_maybe_bool(opt, arg)) {
241+
case 1:
242+
return RECURSE_SUBMODULES_ON;
243+
case 0:
244+
return RECURSE_SUBMODULES_OFF;
245+
default:
246+
if (die_on_error)
247+
die("bad %s argument: %s", opt, arg);
248+
return RECURSE_SUBMODULES_ERROR;
249+
}
250+
}
251+
252+
int parse_update_recurse_submodules_arg(const char *opt, const char *arg)
253+
{
254+
return parse_update_recurse(opt, arg, 1);
255+
}
256+
237257
static int parse_push_recurse(const char *opt, const char *arg,
238258
int die_on_error)
239259
{

submodule-config.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@ struct submodule {
2222
int recommend_shallow;
2323
};
2424

25-
int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
26-
int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
27-
int parse_submodule_config_option(const char *var, const char *value);
28-
const struct submodule *submodule_from_name(const unsigned char *commit_or_tree,
29-
const char *name);
30-
const struct submodule *submodule_from_path(const unsigned char *commit_or_tree,
31-
const char *path);
25+
extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
26+
extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
27+
extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
28+
extern int parse_submodule_config_option(const char *var, const char *value);
29+
extern const struct submodule *submodule_from_name(
30+
const unsigned char *commit_or_tree, const char *name);
31+
extern const struct submodule *submodule_from_path(
32+
const unsigned char *commit_or_tree, const char *path);
3233
extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
3334
unsigned char *gitmodules_sha1,
3435
struct strbuf *rev);
35-
void submodule_free(void);
36+
extern void submodule_free(void);
3637

3738
#endif /* SUBMODULE_CONFIG_H */

0 commit comments

Comments
 (0)