submodule--helper: don't use global --super-prefix in "absorbgitdirs"

The "--super-prefix" facility was introduced in [1] has always been a
transitory hack, which is why we've made it an error to supply it as
an option to "git" to commands that don't know about it.

That's been a good goal, as it has a global effect we haven't wanted
calls to get_super_prefix() from built-ins we didn't expect.

But it has meant that when we've had chains of different built-ins
using it all of the processes in that "chain" have needed to support
it, and worse processes that don't need it have needed to ask for
"SUPPORT_SUPER_PREFIX" because their parent process needs it.

That's how "fsmonitor--daemon" ended up with it, per [2] it's called
from (among other things) "submodule--helper absorbgitdirs", but as we
declared "submodule--helper" as "SUPPORT_SUPER_PREFIX" we needed to
declare "fsmonitor--daemon" as accepting it too, even though it
doesn't care about it.

But in the case of "absorbgitdirs" it only needed "--super-prefix" to
invoke itself recursively, and we'd never have another "in-between"
process in the chain. So we didn't need the bigger hammer of "git
--super-prefix", and the "setenv(GIT_SUPER_PREFIX_ENVIRONMENT, ...)"
that it entails.

Let's instead accept a hidden "--super-prefix" option to
"submodule--helper absorbgitdirs" itself.

Eventually (as with all other "--super-prefix" users) we'll want to
clean this code up so that this all happens in-process. I.e. needing
any variant of "--super-prefix" is itself a hack around our various
global state, and implicit reliance on "the_repository". This stepping
stone makes such an eventual change easier, as we'll need to deal with
less global state at that point.

The "fsmonitor--daemon" test adjusted here was added in [3]. To assert
that it didn't run into the "--super-prefix" message it was asserting
the output it didn't have. Let's instead assert the full output that
we *do* have, using the same pattern as a preceding change to
"t/t7412-submodule-absorbgitdirs.sh" used.

We could also remove the test entirely (as [4] did), but even though
the initial reason for having it is gone we're still getting some
marginal benefit from testing the "fsmonitor" and "submodule
absorbgitdirs" interaction, so let's keep it.

The change here to have either a NULL or non-"" string as a
"super_prefix" instead of the previous arrangement of "" or non-"" is
somewhat arbitrary. We could also decide to never have to check for
NULL.

As we'll be changing the rest of the "git --super-prefix" users to the
same pattern, leaving them all consistent makes sense. Why not pick ""
over NULL? Because that's how the "prefix" works[5], and having
"prefix" and "super_prefix" work the same way will be less
confusing. That "prefix" picked NULL instead of "" is itself
arbitrary, but as it's easy to make this small bit of our overall API
consistent, let's go with that.

1. 74866d7579 (git: make super-prefix option, 2016-10-07)
2. 53fcfbc84f (fsmonitor--daemon: allow --super-prefix argument,
   2022-05-26)
3. 53fcfbc84f (fsmonitor--daemon: allow --super-prefix argument,
   2022-05-26)
4. https://lore.kernel.org/git/20221109004708.97668-5-chooglen@google.com/
5. 9725c8dda2 (built-ins: trust the "prefix" from run_builtin(),
   2022-02-16)

Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Ævar Arnfjörð Bjarmason 2022-12-20 13:39:51 +01:00 committed by Junio C Hamano
parent f0a5e5ad57
commit bb61a962d2
5 changed files with 29 additions and 32 deletions

View File

@ -2834,8 +2834,9 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
int i; int i;
struct pathspec pathspec = { 0 }; struct pathspec pathspec = { 0 };
struct module_list list = MODULE_LIST_INIT; struct module_list list = MODULE_LIST_INIT;
const char *super_prefix; const char *super_prefix = NULL;
struct option embed_gitdir_options[] = { struct option embed_gitdir_options[] = {
OPT__SUPER_PREFIX(&super_prefix),
OPT_END() OPT_END()
}; };
const char *const git_submodule_helper_usage[] = { const char *const git_submodule_helper_usage[] = {
@ -2850,7 +2851,6 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
if (module_list_compute(argv, prefix, &pathspec, &list) < 0) if (module_list_compute(argv, prefix, &pathspec, &list) < 0)
goto cleanup; goto cleanup;
super_prefix = get_super_prefix();
for (i = 0; i < list.nr; i++) for (i = 0; i < list.nr; i++)
absorb_git_dir_into_superproject(list.entries[i]->name, absorb_git_dir_into_superproject(list.entries[i]->name,
super_prefix); super_prefix);
@ -3391,8 +3391,7 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
if (strcmp(subcmd, "clone") && strcmp(subcmd, "update") && if (strcmp(subcmd, "clone") && strcmp(subcmd, "update") &&
strcmp(subcmd, "foreach") && strcmp(subcmd, "status") && strcmp(subcmd, "foreach") && strcmp(subcmd, "status") &&
strcmp(subcmd, "sync") && strcmp(subcmd, "absorbgitdirs") && strcmp(subcmd, "sync") && get_super_prefix())
get_super_prefix())
/* /*
* xstrfmt() rather than "%s %s" to keep the translated * xstrfmt() rather than "%s %s" to keep the translated
* string identical to git.c's. * string identical to git.c's.

2
git.c
View File

@ -539,7 +539,7 @@ static struct cmd_struct commands[] = {
{ "format-patch", cmd_format_patch, RUN_SETUP }, { "format-patch", cmd_format_patch, RUN_SETUP },
{ "fsck", cmd_fsck, RUN_SETUP }, { "fsck", cmd_fsck, RUN_SETUP },
{ "fsck-objects", cmd_fsck, RUN_SETUP }, { "fsck-objects", cmd_fsck, RUN_SETUP },
{ "fsmonitor--daemon", cmd_fsmonitor__daemon, SUPPORT_SUPER_PREFIX | RUN_SETUP }, { "fsmonitor--daemon", cmd_fsmonitor__daemon, RUN_SETUP },
{ "gc", cmd_gc, RUN_SETUP }, { "gc", cmd_gc, RUN_SETUP },
{ "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT }, { "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
{ "grep", cmd_grep, RUN_SETUP_GENTLY }, { "grep", cmd_grep, RUN_SETUP_GENTLY },

View File

@ -369,6 +369,10 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);
{ OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \ { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
N_("use <n> digits to display object names"), \ N_("use <n> digits to display object names"), \
PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
#define OPT__SUPER_PREFIX(var) \
OPT_STRING_F(0, "super-prefix", (var), N_("prefix"), \
N_("prefixed path to initial superproject"), PARSE_OPT_HIDDEN)
#define OPT__COLOR(var, h) \ #define OPT__COLOR(var, h) \
OPT_COLOR_FLAG(0, "color", (var), (h)) OPT_COLOR_FLAG(0, "color", (var), (h))
#define OPT_COLUMN(s, l, v, h) \ #define OPT_COLUMN(s, l, v, h) \

View File

@ -2275,7 +2275,8 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
* Embeds a single submodules git directory into the superprojects git dir, * Embeds a single submodules git directory into the superprojects git dir,
* non recursively. * non recursively.
*/ */
static void relocate_single_git_dir_into_superproject(const char *path) static void relocate_single_git_dir_into_superproject(const char *path,
const char *super_prefix)
{ {
char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL; char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
struct strbuf new_gitdir = STRBUF_INIT; struct strbuf new_gitdir = STRBUF_INIT;
@ -2305,7 +2306,7 @@ static void relocate_single_git_dir_into_superproject(const char *path)
real_new_git_dir = real_pathdup(new_gitdir.buf, 1); real_new_git_dir = real_pathdup(new_gitdir.buf, 1);
fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"), fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
get_super_prefix_or_empty(), path, super_prefix ? super_prefix : "", path,
real_old_git_dir, real_new_git_dir); real_old_git_dir, real_new_git_dir);
relocate_gitdir(path, real_old_git_dir, real_new_git_dir); relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
@ -2325,10 +2326,11 @@ static void absorb_git_dir_into_superproject_recurse(const char *path,
cp.dir = path; cp.dir = path;
cp.git_cmd = 1; cp.git_cmd = 1;
cp.no_stdin = 1; cp.no_stdin = 1;
strvec_pushf(&cp.args, "--super-prefix=%s%s/", super_prefix ?
super_prefix : "", path);
strvec_pushl(&cp.args, "submodule--helper", strvec_pushl(&cp.args, "submodule--helper",
"absorbgitdirs", NULL); "absorbgitdirs", NULL);
strvec_pushf(&cp.args, "--super-prefix=%s%s/", super_prefix ?
super_prefix : "", path);
prepare_submodule_repo_env(&cp.env); prepare_submodule_repo_env(&cp.env);
if (run_command(&cp)) if (run_command(&cp))
die(_("could not recurse into submodule '%s'"), path); die(_("could not recurse into submodule '%s'"), path);
@ -2382,7 +2384,7 @@ void absorb_git_dir_into_superproject(const char *path,
char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1); char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
if (!starts_with(real_sub_git_dir, real_common_git_dir)) if (!starts_with(real_sub_git_dir, real_common_git_dir))
relocate_single_git_dir_into_superproject(path); relocate_single_git_dir_into_superproject(path, super_prefix);
free(real_sub_git_dir); free(real_sub_git_dir);
free(real_common_git_dir); free(real_common_git_dir);

View File

@ -866,27 +866,9 @@ test_expect_success 'submodule always visited' '
# the submodule, and someone does a `git submodule absorbgitdirs` # the submodule, and someone does a `git submodule absorbgitdirs`
# in the super, Git will recursively invoke `git submodule--helper` # in the super, Git will recursively invoke `git submodule--helper`
# to do the work and this may try to read the index. This will # to do the work and this may try to read the index. This will
# try to start the daemon in the submodule *and* pass (either # try to start the daemon in the submodule.
# directly or via inheritance) the `--super-prefix` arg to the
# `git fsmonitor--daemon start` command inside the submodule.
# This causes a warning because fsmonitor--daemon does take that
# global arg (see the table in git.c)
#
# This causes a warning when trying to start the daemon that is
# somewhat confusing. It does not seem to hurt anything because
# the fsmonitor code maps the query failure into a trivial response
# and does the work anyway.
#
# It would be nice to silence the warning, however.
have_t2_error_event () { test_expect_success "submodule absorbgitdirs implicitly starts daemon" '
log=$1
msg="fsmonitor--daemon doesnQt support --super-prefix" &&
tr '\047' Q <$1 | grep -e "$msg"
}
test_expect_success "stray submodule super-prefix warning" '
test_when_finished "rm -rf super; \ test_when_finished "rm -rf super; \
rm -rf sub; \ rm -rf sub; \
rm super-sub.trace" && rm super-sub.trace" &&
@ -904,10 +886,20 @@ test_expect_success "stray submodule super-prefix warning" '
test_path_is_dir super/dir_1/dir_2/sub/.git && test_path_is_dir super/dir_1/dir_2/sub/.git &&
cwd="$(cd super && pwd)" &&
cat >expect <<-EOF &&
Migrating git directory of '\''dir_1/dir_2/sub'\'' from
'\''$cwd/dir_1/dir_2/sub/.git'\'' to
'\''$cwd/.git/modules/dir_1/dir_2/sub'\''
EOF
GIT_TRACE2_EVENT="$PWD/super-sub.trace" \ GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
git -C super submodule absorbgitdirs && git -C super submodule absorbgitdirs >out 2>actual &&
test_cmp expect actual &&
test_must_be_empty out &&
! have_t2_error_event super-sub.trace # Confirm that the trace2 log contains a record of the
# daemon starting.
test_subcommand git fsmonitor--daemon start <super-sub.trace
' '
# On a case-insensitive file system, confirm that the daemon # On a case-insensitive file system, confirm that the daemon