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
This commit is contained in:
Junio C Hamano 2017-02-03 11:25:18 -08:00
commit 5348021c67
4 changed files with 105 additions and 41 deletions

View File

@ -507,9 +507,12 @@ extern int is_nonbare_repository_dir(struct strbuf *path);
#define READ_GITFILE_ERR_NO_PATH 6
#define READ_GITFILE_ERR_NOT_A_REPO 7
#define READ_GITFILE_ERR_TOO_LARGE 8
extern void read_gitfile_error_die(int error_code, const char *path, const char *dir);
extern const char *read_gitfile_gently(const char *path, int *return_error_code);
#define read_gitfile(path) read_gitfile_gently((path), NULL)
extern const char *resolve_gitdir(const char *suspect);
extern const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
extern void set_git_work_tree(const char *tree);
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"

52
setup.c
View File

@ -486,6 +486,30 @@ int verify_repository_format(const struct repository_format *format,
return 0;
}
void read_gitfile_error_die(int error_code, const char *path, const char *dir)
{
switch (error_code) {
case READ_GITFILE_ERR_STAT_FAILED:
case READ_GITFILE_ERR_NOT_A_FILE:
/* non-fatal; follow return path */
break;
case READ_GITFILE_ERR_OPEN_FAILED:
die_errno("Error opening '%s'", path);
case READ_GITFILE_ERR_TOO_LARGE:
die("Too large to be a .git file: '%s'", path);
case READ_GITFILE_ERR_READ_FAILED:
die("Error reading %s", path);
case READ_GITFILE_ERR_INVALID_FORMAT:
die("Invalid gitfile format: %s", path);
case READ_GITFILE_ERR_NO_PATH:
die("No path in gitfile: %s", path);
case READ_GITFILE_ERR_NOT_A_REPO:
die("Not a git repository: %s", dir);
default:
die("BUG: unknown error code");
}
}
/*
* Try to read the location of the git directory from the .git file,
* return path to git directory if found.
@ -559,28 +583,8 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
cleanup_return:
if (return_error_code)
*return_error_code = error_code;
else if (error_code) {
switch (error_code) {
case READ_GITFILE_ERR_STAT_FAILED:
case READ_GITFILE_ERR_NOT_A_FILE:
/* non-fatal; follow return path */
break;
case READ_GITFILE_ERR_OPEN_FAILED:
die_errno("Error opening '%s'", path);
case READ_GITFILE_ERR_TOO_LARGE:
die("Too large to be a .git file: '%s'", path);
case READ_GITFILE_ERR_READ_FAILED:
die("Error reading %s", path);
case READ_GITFILE_ERR_INVALID_FORMAT:
die("Invalid gitfile format: %s", path);
case READ_GITFILE_ERR_NO_PATH:
die("No path in gitfile: %s", path);
case READ_GITFILE_ERR_NOT_A_REPO:
die("Not a git repository: %s", dir);
default:
assert(0);
}
}
else if (error_code)
read_gitfile_error_die(error_code, path, dir);
free(buf);
return error_code ? NULL : path;
@ -1017,11 +1021,11 @@ const char *setup_git_directory(void)
return setup_git_directory_gently(NULL);
}
const char *resolve_gitdir(const char *suspect)
const char *resolve_gitdir_gently(const char *suspect, int *return_error_code)
{
if (is_git_directory(suspect))
return suspect;
return read_gitfile(suspect);
return read_gitfile_gently(suspect, return_error_code);
}
/* if any standard file descriptor is missing open it to /dev/null */

View File

@ -1437,22 +1437,57 @@ void absorb_git_dir_into_superproject(const char *prefix,
const char *path,
unsigned flags)
{
const char *sub_git_dir, *v;
char *real_sub_git_dir = NULL, *real_common_git_dir = NULL;
int err_code;
const char *sub_git_dir;
struct strbuf gitdir = STRBUF_INIT;
strbuf_addf(&gitdir, "%s/.git", path);
sub_git_dir = resolve_gitdir(gitdir.buf);
sub_git_dir = resolve_gitdir_gently(gitdir.buf, &err_code);
/* Not populated? */
if (!sub_git_dir)
goto out;
if (!sub_git_dir) {
char *real_new_git_dir;
const char *new_git_dir;
const struct submodule *sub;
/* Is it already absorbed into the superprojects git dir? */
real_sub_git_dir = real_pathdup(sub_git_dir);
real_common_git_dir = real_pathdup(get_git_common_dir());
if (!skip_prefix(real_sub_git_dir, real_common_git_dir, &v))
relocate_single_git_dir_into_superproject(prefix, path);
if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
/* unpopulated as expected */
strbuf_release(&gitdir);
return;
}
if (err_code != READ_GITFILE_ERR_NOT_A_REPO)
/* We don't know what broke here. */
read_gitfile_error_die(err_code, path, NULL);
/*
* Maybe populated, but no git directory was found?
* This can happen if the superproject is a submodule
* itself and was just absorbed. The absorption of the
* superproject did not rewrite the git file links yet,
* fix it now.
*/
sub = submodule_from_path(null_sha1, path);
if (!sub)
die(_("could not lookup name for submodule '%s'"), path);
new_git_dir = git_path("modules/%s", sub->name);
if (safe_create_leading_directories_const(new_git_dir) < 0)
die(_("could not create directory '%s'"), new_git_dir);
real_new_git_dir = real_pathdup(new_git_dir);
connect_work_tree_and_git_dir(path, real_new_git_dir);
free(real_new_git_dir);
} else {
/* Is it already absorbed into the superprojects git dir? */
char *real_sub_git_dir = real_pathdup(sub_git_dir);
char *real_common_git_dir = real_pathdup(get_git_common_dir());
if (!starts_with(real_sub_git_dir, real_common_git_dir))
relocate_single_git_dir_into_superproject(prefix, path);
free(real_sub_git_dir);
free(real_common_git_dir);
}
strbuf_release(&gitdir);
if (flags & ABSORB_GITDIR_RECURSE_SUBMODULES) {
struct child_process cp = CHILD_PROCESS_INIT;
@ -1478,9 +1513,4 @@ void absorb_git_dir_into_superproject(const char *prefix,
strbuf_release(&sb);
}
out:
strbuf_release(&gitdir);
free(real_sub_git_dir);
free(real_common_git_dir);
}

View File

@ -64,6 +64,33 @@ test_expect_success 'absorb the git dir in a nested submodule' '
test_cmp expect.2 actual.2
'
test_expect_success 're-setup nested submodule' '
# un-absorb the direct submodule, to test if the nested submodule
# is still correct (needs a rewrite of the gitfile only)
rm -rf sub1/.git &&
mv .git/modules/sub1 sub1/.git &&
GIT_WORK_TREE=. git -C sub1 config --unset core.worktree &&
# fixup the nested submodule
echo "gitdir: ../.git/modules/nested" >sub1/nested/.git &&
GIT_WORK_TREE=../../../nested git -C sub1/.git/modules/nested config \
core.worktree "../../../nested" &&
# make sure this re-setup is correct
git status --ignore-submodules=none
'
test_expect_success 'absorb the git dir in a nested submodule' '
git status >expect.1 &&
git -C sub1/nested rev-parse HEAD >expect.2 &&
git submodule absorbgitdirs &&
test -f sub1/.git &&
test -f sub1/nested/.git &&
test -d .git/modules/sub1/modules/nested &&
git status >actual.1 &&
git -C sub1/nested rev-parse HEAD >actual.2 &&
test_cmp expect.1 actual.1 &&
test_cmp expect.2 actual.2
'
test_expect_success 'setup a gitlink with missing .gitmodules entry' '
git init sub2 &&
test_commit -C sub2 first &&