Skip to content

Commit 5348021

Browse files
committed
Merge branch 'sb/submodule-recursive-absorb'
When a submodule "A", which has another submodule "B" nested within it, is "absorbed" into the top-level superproject, the inner submodule "B" used to be left in a strange state. The logic to adjust the .git pointers in these submodules has been corrected. * sb/submodule-recursive-absorb: submodule absorbing: fix worktree/gitdir pointers recursively for non-moves cache.h: expose the dying procedure for reading gitlinks setup: add gentle version of resolve_git_dir
2 parents 2243d22 + ec9629b commit 5348021

File tree

4 files changed

+105
-41
lines changed

4 files changed

+105
-41
lines changed

cache.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,9 +507,12 @@ extern int is_nonbare_repository_dir(struct strbuf *path);
507507
#define READ_GITFILE_ERR_NO_PATH 6
508508
#define READ_GITFILE_ERR_NOT_A_REPO 7
509509
#define READ_GITFILE_ERR_TOO_LARGE 8
510+
extern void read_gitfile_error_die(int error_code, const char *path, const char *dir);
510511
extern const char *read_gitfile_gently(const char *path, int *return_error_code);
511512
#define read_gitfile(path) read_gitfile_gently((path), NULL)
512-
extern const char *resolve_gitdir(const char *suspect);
513+
extern const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
514+
#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
515+
513516
extern void set_git_work_tree(const char *tree);
514517

515518
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"

setup.c

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,30 @@ int verify_repository_format(const struct repository_format *format,
486486
return 0;
487487
}
488488

489+
void read_gitfile_error_die(int error_code, const char *path, const char *dir)
490+
{
491+
switch (error_code) {
492+
case READ_GITFILE_ERR_STAT_FAILED:
493+
case READ_GITFILE_ERR_NOT_A_FILE:
494+
/* non-fatal; follow return path */
495+
break;
496+
case READ_GITFILE_ERR_OPEN_FAILED:
497+
die_errno("Error opening '%s'", path);
498+
case READ_GITFILE_ERR_TOO_LARGE:
499+
die("Too large to be a .git file: '%s'", path);
500+
case READ_GITFILE_ERR_READ_FAILED:
501+
die("Error reading %s", path);
502+
case READ_GITFILE_ERR_INVALID_FORMAT:
503+
die("Invalid gitfile format: %s", path);
504+
case READ_GITFILE_ERR_NO_PATH:
505+
die("No path in gitfile: %s", path);
506+
case READ_GITFILE_ERR_NOT_A_REPO:
507+
die("Not a git repository: %s", dir);
508+
default:
509+
die("BUG: unknown error code");
510+
}
511+
}
512+
489513
/*
490514
* Try to read the location of the git directory from the .git file,
491515
* return path to git directory if found.
@@ -559,28 +583,8 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
559583
cleanup_return:
560584
if (return_error_code)
561585
*return_error_code = error_code;
562-
else if (error_code) {
563-
switch (error_code) {
564-
case READ_GITFILE_ERR_STAT_FAILED:
565-
case READ_GITFILE_ERR_NOT_A_FILE:
566-
/* non-fatal; follow return path */
567-
break;
568-
case READ_GITFILE_ERR_OPEN_FAILED:
569-
die_errno("Error opening '%s'", path);
570-
case READ_GITFILE_ERR_TOO_LARGE:
571-
die("Too large to be a .git file: '%s'", path);
572-
case READ_GITFILE_ERR_READ_FAILED:
573-
die("Error reading %s", path);
574-
case READ_GITFILE_ERR_INVALID_FORMAT:
575-
die("Invalid gitfile format: %s", path);
576-
case READ_GITFILE_ERR_NO_PATH:
577-
die("No path in gitfile: %s", path);
578-
case READ_GITFILE_ERR_NOT_A_REPO:
579-
die("Not a git repository: %s", dir);
580-
default:
581-
assert(0);
582-
}
583-
}
586+
else if (error_code)
587+
read_gitfile_error_die(error_code, path, dir);
584588

585589
free(buf);
586590
return error_code ? NULL : path;
@@ -1017,11 +1021,11 @@ const char *setup_git_directory(void)
10171021
return setup_git_directory_gently(NULL);
10181022
}
10191023

1020-
const char *resolve_gitdir(const char *suspect)
1024+
const char *resolve_gitdir_gently(const char *suspect, int *return_error_code)
10211025
{
10221026
if (is_git_directory(suspect))
10231027
return suspect;
1024-
return read_gitfile(suspect);
1028+
return read_gitfile_gently(suspect, return_error_code);
10251029
}
10261030

10271031
/* if any standard file descriptor is missing open it to /dev/null */

submodule.c

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,22 +1437,57 @@ void absorb_git_dir_into_superproject(const char *prefix,
14371437
const char *path,
14381438
unsigned flags)
14391439
{
1440-
const char *sub_git_dir, *v;
1441-
char *real_sub_git_dir = NULL, *real_common_git_dir = NULL;
1440+
int err_code;
1441+
const char *sub_git_dir;
14421442
struct strbuf gitdir = STRBUF_INIT;
1443-
14441443
strbuf_addf(&gitdir, "%s/.git", path);
1445-
sub_git_dir = resolve_gitdir(gitdir.buf);
1444+
sub_git_dir = resolve_gitdir_gently(gitdir.buf, &err_code);
14461445

14471446
/* Not populated? */
1448-
if (!sub_git_dir)
1449-
goto out;
1447+
if (!sub_git_dir) {
1448+
char *real_new_git_dir;
1449+
const char *new_git_dir;
1450+
const struct submodule *sub;
1451+
1452+
if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
1453+
/* unpopulated as expected */
1454+
strbuf_release(&gitdir);
1455+
return;
1456+
}
1457+
1458+
if (err_code != READ_GITFILE_ERR_NOT_A_REPO)
1459+
/* We don't know what broke here. */
1460+
read_gitfile_error_die(err_code, path, NULL);
1461+
1462+
/*
1463+
* Maybe populated, but no git directory was found?
1464+
* This can happen if the superproject is a submodule
1465+
* itself and was just absorbed. The absorption of the
1466+
* superproject did not rewrite the git file links yet,
1467+
* fix it now.
1468+
*/
1469+
sub = submodule_from_path(null_sha1, path);
1470+
if (!sub)
1471+
die(_("could not lookup name for submodule '%s'"), path);
1472+
new_git_dir = git_path("modules/%s", sub->name);
1473+
if (safe_create_leading_directories_const(new_git_dir) < 0)
1474+
die(_("could not create directory '%s'"), new_git_dir);
1475+
real_new_git_dir = real_pathdup(new_git_dir);
1476+
connect_work_tree_and_git_dir(path, real_new_git_dir);
1477+
1478+
free(real_new_git_dir);
1479+
} else {
1480+
/* Is it already absorbed into the superprojects git dir? */
1481+
char *real_sub_git_dir = real_pathdup(sub_git_dir);
1482+
char *real_common_git_dir = real_pathdup(get_git_common_dir());
14501483

1451-
/* Is it already absorbed into the superprojects git dir? */
1452-
real_sub_git_dir = real_pathdup(sub_git_dir);
1453-
real_common_git_dir = real_pathdup(get_git_common_dir());
1454-
if (!skip_prefix(real_sub_git_dir, real_common_git_dir, &v))
1455-
relocate_single_git_dir_into_superproject(prefix, path);
1484+
if (!starts_with(real_sub_git_dir, real_common_git_dir))
1485+
relocate_single_git_dir_into_superproject(prefix, path);
1486+
1487+
free(real_sub_git_dir);
1488+
free(real_common_git_dir);
1489+
}
1490+
strbuf_release(&gitdir);
14561491

14571492
if (flags & ABSORB_GITDIR_RECURSE_SUBMODULES) {
14581493
struct child_process cp = CHILD_PROCESS_INIT;
@@ -1478,9 +1513,4 @@ void absorb_git_dir_into_superproject(const char *prefix,
14781513

14791514
strbuf_release(&sb);
14801515
}
1481-
1482-
out:
1483-
strbuf_release(&gitdir);
1484-
free(real_sub_git_dir);
1485-
free(real_common_git_dir);
14861516
}

t/t7412-submodule-absorbgitdirs.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,33 @@ test_expect_success 'absorb the git dir in a nested submodule' '
6464
test_cmp expect.2 actual.2
6565
'
6666

67+
test_expect_success 're-setup nested submodule' '
68+
# un-absorb the direct submodule, to test if the nested submodule
69+
# is still correct (needs a rewrite of the gitfile only)
70+
rm -rf sub1/.git &&
71+
mv .git/modules/sub1 sub1/.git &&
72+
GIT_WORK_TREE=. git -C sub1 config --unset core.worktree &&
73+
# fixup the nested submodule
74+
echo "gitdir: ../.git/modules/nested" >sub1/nested/.git &&
75+
GIT_WORK_TREE=../../../nested git -C sub1/.git/modules/nested config \
76+
core.worktree "../../../nested" &&
77+
# make sure this re-setup is correct
78+
git status --ignore-submodules=none
79+
'
80+
81+
test_expect_success 'absorb the git dir in a nested submodule' '
82+
git status >expect.1 &&
83+
git -C sub1/nested rev-parse HEAD >expect.2 &&
84+
git submodule absorbgitdirs &&
85+
test -f sub1/.git &&
86+
test -f sub1/nested/.git &&
87+
test -d .git/modules/sub1/modules/nested &&
88+
git status >actual.1 &&
89+
git -C sub1/nested rev-parse HEAD >actual.2 &&
90+
test_cmp expect.1 actual.1 &&
91+
test_cmp expect.2 actual.2
92+
'
93+
6794
test_expect_success 'setup a gitlink with missing .gitmodules entry' '
6895
git init sub2 &&
6996
test_commit -C sub2 first &&

0 commit comments

Comments
 (0)