Skip to content

Commit db81e1c

Browse files
committed
Merge branch 'sb/reset-recurse-submodules' into pu
"git reset" learned "--recurse-submodules" option. * sb/reset-recurse-submodules: builtin/reset: add --recurse-submodules switch submodule.c: submodule_move_head works with broken submodules submodule.c: uninitialized submodules are ignored in recursive commands entry.c: submodule recursing: respect force flag correctly
2 parents 9c9bf98 + 35b96d1 commit db81e1c

File tree

6 files changed

+96
-12
lines changed

6 files changed

+96
-12
lines changed

builtin/reset.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,27 @@
2121
#include "parse-options.h"
2222
#include "unpack-trees.h"
2323
#include "cache-tree.h"
24+
#include "submodule.h"
25+
#include "submodule-config.h"
26+
27+
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
28+
29+
static int option_parse_recurse_submodules(const struct option *opt,
30+
const char *arg, int unset)
31+
{
32+
if (unset) {
33+
recurse_submodules = RECURSE_SUBMODULES_OFF;
34+
return 0;
35+
}
36+
if (arg)
37+
recurse_submodules =
38+
parse_update_recurse_submodules_arg(opt->long_name,
39+
arg);
40+
else
41+
recurse_submodules = RECURSE_SUBMODULES_ON;
42+
43+
return 0;
44+
}
2445

2546
static const char * const git_reset_usage[] = {
2647
N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
@@ -283,6 +304,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
283304
N_("reset HEAD, index and working tree"), MERGE),
284305
OPT_SET_INT(0, "keep", &reset_type,
285306
N_("reset HEAD but keep local changes"), KEEP),
307+
{ OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
308+
"reset", "control recursive updating of submodules",
309+
PARSE_OPT_OPTARG, option_parse_recurse_submodules },
286310
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
287311
OPT_BOOL('N', "intent-to-add", &intent_to_add,
288312
N_("record only the fact that removed paths will be added later")),
@@ -295,6 +319,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
295319
PARSE_OPT_KEEP_DASHDASH);
296320
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
297321

322+
if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) {
323+
gitmodules_config();
324+
git_config(submodule_config, NULL);
325+
set_config_update_recurse_submodules(RECURSE_SUBMODULES_ON);
326+
}
327+
298328
unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", oid.hash);
299329
if (unborn) {
300330
/* reset on unborn branch: treat as reset to empty tree */

entry.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ static int write_entry(struct cache_entry *ce,
204204
sub = submodule_from_ce(ce);
205205
if (sub)
206206
return submodule_move_head(ce->name,
207-
NULL, oid_to_hex(&ce->oid), SUBMODULE_MOVE_HEAD_FORCE);
207+
NULL, oid_to_hex(&ce->oid),
208+
state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
208209
break;
209210
default:
210211
return error("unknown file mode for %s in index", path);
@@ -278,12 +279,11 @@ int checkout_entry(struct cache_entry *ce,
278279
unlink_or_warn(ce->name);
279280

280281
return submodule_move_head(ce->name,
281-
NULL, oid_to_hex(&ce->oid),
282-
SUBMODULE_MOVE_HEAD_FORCE);
282+
NULL, oid_to_hex(&ce->oid), 0);
283283
} else
284284
return submodule_move_head(ce->name,
285285
"HEAD", oid_to_hex(&ce->oid),
286-
SUBMODULE_MOVE_HEAD_FORCE);
286+
state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
287287
}
288288

289289
if (!changed)

submodule.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,23 @@ int submodule_move_head(const char *path,
14091409
int ret = 0;
14101410
struct child_process cp = CHILD_PROCESS_INIT;
14111411
const struct submodule *sub;
1412+
int *error_code_ptr, error_code;
1413+
1414+
if (!is_submodule_initialized(path))
1415+
return 0;
1416+
1417+
if (flags & SUBMODULE_MOVE_HEAD_FORCE)
1418+
/*
1419+
* Pass non NULL pointer to is_submodule_populated_gently
1420+
* to prevent die()-ing. We'll use connect_work_tree_and_git_dir
1421+
* to fixup the submodule in the force case later.
1422+
*/
1423+
error_code_ptr = &error_code;
1424+
else
1425+
error_code_ptr = NULL;
1426+
1427+
if (old && !is_submodule_populated_gently(path, error_code_ptr))
1428+
return 0;
14121429

14131430
sub = submodule_from_path(null_sha1, path);
14141431

@@ -1427,15 +1444,21 @@ int submodule_move_head(const char *path,
14271444
absorb_git_dir_into_superproject("", path,
14281445
ABSORB_GITDIR_RECURSE_SUBMODULES);
14291446
} else {
1430-
struct strbuf sb = STRBUF_INIT;
1431-
strbuf_addf(&sb, "%s/modules/%s",
1447+
char *gitdir = xstrfmt("%s/modules/%s",
14321448
get_git_common_dir(), sub->name);
1433-
connect_work_tree_and_git_dir(path, sb.buf);
1434-
strbuf_release(&sb);
1449+
connect_work_tree_and_git_dir(path, gitdir);
1450+
free(gitdir);
14351451

14361452
/* make sure the index is clean as well */
14371453
submodule_reset_index(path);
14381454
}
1455+
1456+
if (old && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
1457+
char *gitdir = xstrfmt("%s/modules/%s",
1458+
get_git_common_dir(), sub->name);
1459+
connect_work_tree_and_git_dir(path, gitdir);
1460+
free(gitdir);
1461+
}
14391462
}
14401463

14411464
prepare_submodule_repo_env_no_git_dir(&cp.env_array);

t/lib-submodule-update.sh

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ create_lib_submodule_repo () {
7373

7474
git checkout -b "add_sub1" &&
7575
git submodule add ../submodule_update_sub1 sub1 &&
76+
git submodule add ../submodule_update_sub1 uninitialized_sub &&
7677
git config -f .gitmodules submodule.sub1.ignore all &&
7778
git config submodule.sub1.ignore all &&
7879
git add .gitmodules &&
@@ -1212,14 +1213,31 @@ test_submodule_forced_switch_recursing () {
12121213
)
12131214
'
12141215
# Updating a submodule from an invalid sha1 updates
1215-
test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" '
1216+
test_expect_success "$command: modified submodule does update submodule work tree from invalid commit" '
12161217
prolog &&
12171218
reset_work_tree_to_interested invalid_sub1 &&
12181219
(
12191220
cd submodule_update &&
12201221
git branch -t valid_sub1 origin/valid_sub1 &&
1221-
test_must_fail $command valid_sub1 &&
1222-
test_superproject_content origin/invalid_sub1
1222+
$command valid_sub1 &&
1223+
test_superproject_content origin/valid_sub1 &&
1224+
test_submodule_content sub1 origin/valid_sub1
1225+
)
1226+
'
1227+
1228+
# Old versions of Git were buggy writing the .git link file
1229+
# (e.g. before f8eaa0ba98b and then moving the superproject repo
1230+
# whose submodules contained absolute paths)
1231+
test_expect_success "$command: updating submodules fixes .git links" '
1232+
prolog &&
1233+
reset_work_tree_to_interested add_sub1 &&
1234+
(
1235+
cd submodule_update &&
1236+
git branch -t modify_sub1 origin/modify_sub1 &&
1237+
echo "gitdir: bogus/path" >sub1/.git &&
1238+
$command modify_sub1 &&
1239+
test_superproject_content origin/modify_sub1 &&
1240+
test_submodule_content sub1 origin/modify_sub1
12231241
)
12241242
'
12251243
}

t/t7112-reset-submodule.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ test_description='reset can handle submodules'
55
. ./test-lib.sh
66
. "$TEST_DIRECTORY"/lib-submodule-update.sh
77

8+
KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1
9+
KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
10+
KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1
11+
12+
test_submodule_switch_recursing "git reset --recurse-submodules --keep"
13+
14+
test_submodule_forced_switch_recursing "git reset --hard --recurse-submodules"
15+
816
test_submodule_switch "git reset --keep"
917

1018
test_submodule_switch "git reset --merge"

unpack-trees.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,14 +252,18 @@ static int check_submodule_move_head(const struct cache_entry *ce,
252252
const char *new_id,
253253
struct unpack_trees_options *o)
254254
{
255+
unsigned flags = SUBMODULE_MOVE_HEAD_DRY_RUN;
255256
const struct submodule *sub = submodule_from_ce(ce);
256257
if (!sub)
257258
return 0;
258259

260+
if (o->reset)
261+
flags |= SUBMODULE_MOVE_HEAD_FORCE;
262+
259263
switch (sub->update_strategy.type) {
260264
case SM_UPDATE_UNSPECIFIED:
261265
case SM_UPDATE_CHECKOUT:
262-
if (submodule_move_head(ce->name, old_id, new_id, SUBMODULE_MOVE_HEAD_DRY_RUN))
266+
if (submodule_move_head(ce->name, old_id, new_id, flags))
263267
return o->gently ? -1 :
264268
add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
265269
return 0;
@@ -308,6 +312,7 @@ static void unlink_entry(const struct cache_entry *ce)
308312
case SM_UPDATE_CHECKOUT:
309313
case SM_UPDATE_REBASE:
310314
case SM_UPDATE_MERGE:
315+
/* state.force is set at the caller. */
311316
submodule_move_head(ce->name, "HEAD", NULL,
312317
SUBMODULE_MOVE_HEAD_FORCE);
313318
break;

0 commit comments

Comments
 (0)