Merge branch 'ab/no-more-git-global-super-prefix'

Stop using "git --super-prefix" and narrow the scope of its use to
the submodule--helper.

* ab/no-more-git-global-super-prefix:
  read-tree: add "--super-prefix" option, eliminate global
  submodule--helper: convert "{update,clone}" to their own "--super-prefix"
  submodule--helper: convert "status" to its own "--super-prefix"
  submodule--helper: convert "sync" to its own "--super-prefix"
  submodule--helper: convert "foreach" to its own "--super-prefix"
  submodule--helper: don't use global --super-prefix in "absorbgitdirs"
  submodule.c & submodule--helper: pass along "super_prefix" param
  read-tree + fetch tests: test failing "--super-prefix" interaction
  submodule absorbgitdirs tests: add missing "Migrating git..." tests
This commit is contained in:
Junio C Hamano 2023-01-05 15:07:22 +09:00
commit d4c5400865
20 changed files with 230 additions and 180 deletions

View File

@ -13,8 +13,7 @@ SYNOPSIS
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
[--super-prefix=<path>] [--config-env=<name>=<envvar>]
<command> [<args>]
[--config-env=<name>=<envvar>] <command> [<args>]
DESCRIPTION
-----------
@ -169,11 +168,6 @@ If you just want to run git as if it was started in `<path>` then use
details. Equivalent to setting the `GIT_NAMESPACE` environment
variable.
--super-prefix=<path>::
Currently for internal use only. Set a prefix which gives a path from
above a repository down to its root. One use is to give submodules
context about the superproject that invoked it.
--bare::
Treat the repository as a bare repository. If GIT_DIR
environment is not set, it is set to the current working

View File

@ -51,10 +51,6 @@
* on bare repositories.
* This only makes sense when `RUN_SETUP` is also set.
*
* `SUPPORT_SUPER_PREFIX`:
*
* The built-in supports `--super-prefix`.
*
* `DELAY_PAGER_CONFIG`:
*
* If RUN_SETUP or RUN_SETUP_GENTLY is set, git.c normally handles

View File

@ -232,7 +232,7 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
pos++;
}
if (!overlay_mode) {
unlink_entry(ce);
unlink_entry(ce, NULL);
return 0;
}
if (stage == 2)

View File

@ -114,6 +114,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
int prefix_set = 0;
struct lock_file lock_file = LOCK_INIT;
const struct option read_tree_options[] = {
OPT__SUPER_PREFIX(&opts.super_prefix),
OPT_CALLBACK_F(0, "index-output", NULL, N_("file"),
N_("write resulting index to <file>"),
PARSE_OPT_NONEG, index_output_cb),

View File

@ -86,7 +86,7 @@ static void submodules_absorb_gitdir_if_needed(void)
continue;
if (!submodule_uses_gitfile(name))
absorb_git_dir_into_superproject(name);
absorb_git_dir_into_superproject(name, NULL);
}
}

View File

@ -113,10 +113,9 @@ static char *resolve_relative_url(const char *rel_url, const char *up_path, int
}
/* the result should be freed by the caller. */
static char *get_submodule_displaypath(const char *path, const char *prefix)
static char *get_submodule_displaypath(const char *path, const char *prefix,
const char *super_prefix)
{
const char *super_prefix = get_super_prefix();
if (prefix && super_prefix) {
BUG("cannot have prefix '%s' and superprefix '%s'",
prefix, super_prefix);
@ -279,6 +278,7 @@ struct foreach_cb {
int argc;
const char **argv;
const char *prefix;
const char *super_prefix;
int quiet;
int recursive;
};
@ -294,7 +294,8 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
struct child_process cp = CHILD_PROCESS_INIT;
char *displaypath;
displaypath = get_submodule_displaypath(path, info->prefix);
displaypath = get_submodule_displaypath(path, info->prefix,
info->super_prefix);
sub = submodule_from_path(the_repository, null_oid(), path);
@ -364,10 +365,10 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
cpr.dir = path;
prepare_submodule_repo_env(&cpr.env);
strvec_pushl(&cpr.args, "--super-prefix", NULL);
strvec_pushf(&cpr.args, "%s/", displaypath);
strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
NULL);
strvec_pushl(&cpr.args, "--super-prefix", NULL);
strvec_pushf(&cpr.args, "%s/", displaypath);
if (info->quiet)
strvec_push(&cpr.args, "--quiet");
@ -391,6 +392,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
struct pathspec pathspec = { 0 };
struct module_list list = MODULE_LIST_INIT;
struct option module_foreach_options[] = {
OPT__SUPER_PREFIX(&info.super_prefix),
OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
OPT_BOOL(0, "recursive", &info.recursive,
N_("recurse into nested submodules")),
@ -435,11 +437,13 @@ static int starts_with_dot_dot_slash(const char *const path)
struct init_cb {
const char *prefix;
const char *super_prefix;
unsigned int flags;
};
#define INIT_CB_INIT { 0 }
static void init_submodule(const char *path, const char *prefix,
const char *super_prefix,
unsigned int flags)
{
const struct submodule *sub;
@ -447,7 +451,7 @@ static void init_submodule(const char *path, const char *prefix,
const char *upd;
char *url = NULL, *displaypath;
displaypath = get_submodule_displaypath(path, prefix);
displaypath = get_submodule_displaypath(path, prefix, super_prefix);
sub = submodule_from_path(the_repository, null_oid(), path);
@ -523,7 +527,8 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
{
struct init_cb *info = cb_data;
init_submodule(list_item->name, info->prefix, info->flags);
init_submodule(list_item->name, info->prefix, info->super_prefix,
info->flags);
}
static int module_init(int argc, const char **argv, const char *prefix)
@ -570,6 +575,7 @@ cleanup:
struct status_cb {
const char *prefix;
const char *super_prefix;
unsigned int flags;
};
#define STATUS_CB_INIT { 0 }
@ -608,7 +614,7 @@ static int handle_submodule_head_ref(const char *refname UNUSED,
static void status_submodule(const char *path, const struct object_id *ce_oid,
unsigned int ce_flags, const char *prefix,
unsigned int flags)
const char *super_prefix, unsigned int flags)
{
char *displaypath;
struct strvec diff_files_args = STRVEC_INIT;
@ -624,7 +630,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
die(_("no submodule mapping found in .gitmodules for path '%s'"),
path);
displaypath = get_submodule_displaypath(path, prefix);
displaypath = get_submodule_displaypath(path, prefix, super_prefix);
if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
print_status(flags, 'U', path, null_oid(), displaypath);
@ -682,10 +688,10 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
cpr.dir = path;
prepare_submodule_repo_env(&cpr.env);
strvec_push(&cpr.args, "--super-prefix");
strvec_pushf(&cpr.args, "%s/", displaypath);
strvec_pushl(&cpr.args, "submodule--helper", "status",
"--recursive", NULL);
strvec_push(&cpr.args, "--super-prefix");
strvec_pushf(&cpr.args, "%s/", displaypath);
if (flags & OPT_CACHED)
strvec_push(&cpr.args, "--cached");
@ -709,7 +715,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
struct status_cb *info = cb_data;
status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
info->prefix, info->flags);
info->prefix, info->super_prefix, info->flags);
}
static int module_status(int argc, const char **argv, const char *prefix)
@ -719,6 +725,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
struct module_list list = MODULE_LIST_INIT;
int quiet = 0;
struct option module_status_options[] = {
OPT__SUPER_PREFIX(&info.super_prefix),
OPT__QUIET(&quiet, N_("suppress submodule status output")),
OPT_BIT(0, "cached", &info.flags, N_("use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
@ -787,6 +794,7 @@ struct summary_cb {
int argc;
const char **argv;
const char *prefix;
const char *super_prefix;
unsigned int cached: 1;
unsigned int for_status: 1;
unsigned int files: 1;
@ -948,7 +956,8 @@ static void generate_submodule_summary(struct summary_cb *info,
dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
}
displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
displaypath = get_submodule_displaypath(p->sm_path, info->prefix,
info->super_prefix);
if (!missing_src && !missing_dst) {
struct child_process cp_rev_list = CHILD_PROCESS_INIT;
@ -1203,12 +1212,13 @@ static int module_summary(int argc, const char **argv, const char *prefix)
struct sync_cb {
const char *prefix;
const char *super_prefix;
unsigned int flags;
};
#define SYNC_CB_INIT { 0 }
static void sync_submodule(const char *path, const char *prefix,
unsigned int flags)
const char *super_prefix, unsigned int flags)
{
const struct submodule *sub;
char *remote_key = NULL;
@ -1239,7 +1249,7 @@ static void sync_submodule(const char *path, const char *prefix,
super_config_url = xstrdup("");
}
displaypath = get_submodule_displaypath(path, prefix);
displaypath = get_submodule_displaypath(path, prefix, super_prefix);
if (!(flags & OPT_QUIET))
printf(_("Synchronizing submodule url for '%s'\n"),
@ -1276,10 +1286,11 @@ static void sync_submodule(const char *path, const char *prefix,
cpr.dir = path;
prepare_submodule_repo_env(&cpr.env);
strvec_push(&cpr.args, "--super-prefix");
strvec_pushf(&cpr.args, "%s/", displaypath);
strvec_pushl(&cpr.args, "submodule--helper", "sync",
"--recursive", NULL);
strvec_push(&cpr.args, "--super-prefix");
strvec_pushf(&cpr.args, "%s/", displaypath);
if (flags & OPT_QUIET)
strvec_push(&cpr.args, "--quiet");
@ -1302,7 +1313,8 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
{
struct sync_cb *info = cb_data;
sync_submodule(list_item->name, info->prefix, info->flags);
sync_submodule(list_item->name, info->prefix, info->super_prefix,
info->flags);
}
static int module_sync(int argc, const char **argv, const char *prefix)
@ -1313,6 +1325,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
int quiet = 0;
int recursive = 0;
struct option module_sync_options[] = {
OPT__SUPER_PREFIX(&info.super_prefix),
OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
OPT_BOOL(0, "recursive", &recursive,
N_("recurse into nested submodules")),
@ -1365,7 +1378,7 @@ static void deinit_submodule(const char *path, const char *prefix,
if (!sub || !sub->name)
goto cleanup;
displaypath = get_submodule_displaypath(path, prefix);
displaypath = get_submodule_displaypath(path, prefix, NULL);
/* remove the submodule work tree (unless the user already did it) */
if (is_directory(path)) {
@ -1379,7 +1392,7 @@ static void deinit_submodule(const char *path, const char *prefix,
".git file by using absorbgitdirs."),
displaypath);
absorb_git_dir_into_superproject(path);
absorb_git_dir_into_superproject(path, NULL);
}
@ -1883,6 +1896,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
struct update_data {
const char *prefix;
const char *super_prefix;
char *displaypath;
enum submodule_update_type update_default;
struct object_id suboid;
@ -1958,7 +1972,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
enum submodule_update_type update_type;
char *key;
const struct update_data *ud = suc->update_data;
char *displaypath = get_submodule_displaypath(ce->name, ud->prefix);
char *displaypath = get_submodule_displaypath(ce->name, ud->prefix,
ud->super_prefix);
struct strbuf sb = STRBUF_INIT;
int needs_cloning = 0;
int need_free_url = 0;
@ -2438,11 +2453,11 @@ static void update_data_to_args(const struct update_data *update_data,
{
enum submodule_update_type update_type = update_data->update_default;
strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
if (update_data->displaypath) {
strvec_push(args, "--super-prefix");
strvec_pushf(args, "%s/", update_data->displaypath);
}
strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
strvec_pushf(args, "--jobs=%d", update_data->max_jobs);
if (update_data->quiet)
strvec_push(args, "--quiet");
@ -2608,7 +2623,8 @@ static int update_submodules(struct update_data *update_data)
goto fail;
update_data->displaypath = get_submodule_displaypath(
update_data->sm_path, update_data->prefix);
update_data->sm_path, update_data->prefix,
update_data->super_prefix);
code = update_submodule(update_data);
FREE_AND_NULL(update_data->displaypath);
fail:
@ -2634,6 +2650,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
LIST_OBJECTS_FILTER_INIT;
int ret;
struct option module_update_options[] = {
OPT__SUPER_PREFIX(&opt.super_prefix),
OPT__FORCE(&opt.force, N_("force checkout updates"), 0),
OPT_BOOL(0, "init", &opt.init,
N_("initialize uninitialized submodules before update")),
@ -2730,6 +2747,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
module_list_active(&list);
info.prefix = opt.prefix;
info.super_prefix = opt.super_prefix;
if (opt.quiet)
info.flags |= OPT_QUIET;
@ -2828,7 +2846,9 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
int i;
struct pathspec pathspec = { 0 };
struct module_list list = MODULE_LIST_INIT;
const char *super_prefix = NULL;
struct option embed_gitdir_options[] = {
OPT__SUPER_PREFIX(&super_prefix),
OPT_END()
};
const char *const git_submodule_helper_usage[] = {
@ -2844,7 +2864,8 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
goto cleanup;
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);
ret = 0;
cleanup:
@ -2876,7 +2897,7 @@ static int module_set_url(int argc, const char **argv, const char *prefix)
config_name = xstrfmt("submodule.%s.url", path);
config_set_in_gitmodules_file_gently(config_name, newurl);
sync_submodule(path, prefix, quiet ? OPT_QUIET : 0);
sync_submodule(path, prefix, NULL, quiet ? OPT_QUIET : 0);
free(config_name);
@ -3353,8 +3374,6 @@ cleanup:
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
{
const char *cmd = argv[0];
const char *subcmd;
parse_opt_subcommand_fn *fn = NULL;
const char *const usage[] = {
N_("git submodule--helper <command>"),
@ -3378,18 +3397,6 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
OPT_END()
};
argc = parse_options(argc, argv, prefix, options, usage, 0);
subcmd = argv[0];
if (strcmp(subcmd, "clone") && strcmp(subcmd, "update") &&
strcmp(subcmd, "foreach") && strcmp(subcmd, "status") &&
strcmp(subcmd, "sync") && strcmp(subcmd, "absorbgitdirs") &&
get_super_prefix())
/*
* xstrfmt() rather than "%s %s" to keep the translated
* string identical to git.c's.
*/
die(_("%s doesn't support --super-prefix"),
xstrfmt("'%s %s'", cmd, subcmd));
return fn(argc, argv, prefix);
}

View File

@ -480,7 +480,6 @@ static inline enum object_type object_type(unsigned int mode)
#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@ -566,7 +565,6 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
int get_common_dir(struct strbuf *sb, const char *gitdir);
const char *get_git_namespace(void);
const char *strip_namespace(const char *namespaced_ref);
const char *get_super_prefix(void);
const char *get_git_work_tree(void);
/*

12
entry.c
View File

@ -383,7 +383,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
return error("cannot create submodule directory %s", path);
sub = submodule_from_ce(ce);
if (sub)
return submodule_move_head(ce->name,
return submodule_move_head(ce->name, state->super_prefix,
NULL, oid_to_hex(&ce->oid),
state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
break;
@ -476,7 +476,7 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
* no pathname to return.
*/
BUG("Can't remove entry to a path");
unlink_entry(ce);
unlink_entry(ce, state->super_prefix);
return 0;
}
@ -510,10 +510,10 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
if (!(st.st_mode & S_IFDIR))
unlink_or_warn(ce->name);
return submodule_move_head(ce->name,
return submodule_move_head(ce->name, state->super_prefix,
NULL, oid_to_hex(&ce->oid), 0);
} else
return submodule_move_head(ce->name,
return submodule_move_head(ce->name, state->super_prefix,
"HEAD", oid_to_hex(&ce->oid),
state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
}
@ -560,12 +560,12 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
return write_entry(ce, path.buf, ca, state, 0, nr_checkouts);
}
void unlink_entry(const struct cache_entry *ce)
void unlink_entry(const struct cache_entry *ce, const char *super_prefix)
{
const struct submodule *sub = submodule_from_ce(ce);
if (sub) {
/* state.force is set at the caller. */
submodule_move_head(ce->name, "HEAD", NULL,
submodule_move_head(ce->name, super_prefix, "HEAD", NULL,
SUBMODULE_MOVE_HEAD_FORCE);
}
if (check_leading_path(ce->name, ce_namelen(ce), 1) >= 0)

View File

@ -8,6 +8,7 @@ struct checkout {
struct index_state *istate;
const char *base_dir;
int base_dir_len;
const char *super_prefix;
struct delayed_checkout *delayed_checkout;
struct checkout_metadata meta;
unsigned force:1,
@ -48,8 +49,11 @@ int finish_delayed_checkout(struct checkout *state, int show_progress);
/*
* Unlink the last component and schedule the leading directories for
* removal, such that empty directories get removed.
*
* The "super_prefix" is either NULL, or the "--super-prefix" passed
* down from "read-tree" et al.
*/
void unlink_entry(const struct cache_entry *ce);
void unlink_entry(const struct cache_entry *ce, const char *super_prefix);
void *read_blob_entry(const struct cache_entry *ce, size_t *size);
int fstat_checkout_output(int fd, const struct checkout *state, struct stat *st);

View File

@ -102,8 +102,6 @@ char *git_work_tree_cfg;
static char *git_namespace;
static char *super_prefix;
/*
* Repository-local GIT_* environment variables; see cache.h for details.
*/
@ -121,7 +119,6 @@ const char * const local_repo_env[] = {
NO_REPLACE_OBJECTS_ENVIRONMENT,
GIT_REPLACE_REF_BASE_ENVIRONMENT,
GIT_PREFIX_ENVIRONMENT,
GIT_SUPER_PREFIX_ENVIRONMENT,
GIT_SHALLOW_FILE_ENVIRONMENT,
GIT_COMMON_DIR_ENVIRONMENT,
NULL
@ -234,16 +231,6 @@ const char *strip_namespace(const char *namespaced_ref)
return NULL;
}
const char *get_super_prefix(void)
{
static int initialized;
if (!initialized) {
super_prefix = xstrdup_or_null(getenv(GIT_SUPER_PREFIX_ENVIRONMENT));
initialized = 1;
}
return super_prefix;
}
static int git_work_tree_initialized;
/*

41
git.c
View File

@ -14,9 +14,8 @@
* RUN_SETUP for reading from the configuration file.
*/
#define NEED_WORK_TREE (1<<3)
#define SUPPORT_SUPER_PREFIX (1<<4)
#define DELAY_PAGER_CONFIG (1<<5)
#define NO_PARSEOPT (1<<6) /* parse-options is not used */
#define DELAY_PAGER_CONFIG (1<<4)
#define NO_PARSEOPT (1<<5) /* parse-options is not used */
struct cmd_struct {
const char *cmd;
@ -29,8 +28,7 @@ const char git_usage_string[] =
" [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
" [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
" [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
" [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
" <command> [<args>]");
" [--config-env=<name>=<envvar>] <command> [<args>]");
const char git_more_info_string[] =
N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@ -226,20 +224,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--super-prefix")) {
if (*argc < 2) {
fprintf(stderr, _("no prefix given for --super-prefix\n" ));
usage(git_usage_string);
}
setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
if (envchanged)
*envchanged = 1;
(*argv)++;
(*argc)--;
} else if (skip_prefix(cmd, "--super-prefix=", &cmd)) {
setenv(GIT_SUPER_PREFIX_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--bare")) {
char *cwd = xgetcwd();
is_bare_repository_cfg = 1;
@ -449,11 +433,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
trace_repo_setup(prefix);
commit_pager_choice();
if (!help && get_super_prefix()) {
if (!(p->option & SUPPORT_SUPER_PREFIX))
die(_("%s doesn't support --super-prefix"), p->cmd);
}
if (!help && p->option & NEED_WORK_TREE)
setup_work_tree();
@ -504,7 +483,7 @@ static struct cmd_struct commands[] = {
{ "check-ref-format", cmd_check_ref_format, NO_PARSEOPT },
{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
{ "checkout--worker", cmd_checkout__worker,
RUN_SETUP | NEED_WORK_TREE | SUPPORT_SUPER_PREFIX },
RUN_SETUP | NEED_WORK_TREE },
{ "checkout-index", cmd_checkout_index,
RUN_SETUP | NEED_WORK_TREE},
{ "cherry", cmd_cherry, RUN_SETUP },
@ -539,7 +518,7 @@ static struct cmd_struct commands[] = {
{ "format-patch", cmd_format_patch, RUN_SETUP },
{ "fsck", 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 },
{ "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
{ "grep", cmd_grep, RUN_SETUP_GENTLY },
@ -583,7 +562,7 @@ static struct cmd_struct commands[] = {
{ "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
{ "push", cmd_push, RUN_SETUP },
{ "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
{ "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
{ "read-tree", cmd_read_tree, RUN_SETUP },
{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
@ -610,7 +589,7 @@ static struct cmd_struct commands[] = {
{ "stash", cmd_stash, RUN_SETUP | NEED_WORK_TREE },
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
{ "stripspace", cmd_stripspace },
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX },
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP },
{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
@ -727,9 +706,6 @@ static void execv_dashed_external(const char **argv)
struct child_process cmd = CHILD_PROCESS_INIT;
int status;
if (get_super_prefix())
die(_("%s doesn't support --super-prefix"), argv[0]);
if (use_pager == -1 && !is_builtin(argv[0]))
use_pager = check_pager_config(argv[0]);
commit_pager_choice();
@ -799,9 +775,6 @@ static int run_argv(int *argcp, const char ***argv)
*/
trace2_cmd_name("_run_git_alias_");
if (get_super_prefix())
die("%s doesn't support --super-prefix", **argv);
commit_pager_choice();
strvec_push(&cmd.args, "git");

View File

@ -369,6 +369,10 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);
{ OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
N_("use <n> digits to display object names"), \
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) \
OPT_COLOR_FLAG(0, "color", (var), (h))
#define OPT_COLUMN(s, l, v, h) \

View File

@ -2054,14 +2054,6 @@ void submodule_unset_core_worktree(const struct submodule *sub)
strbuf_release(&config_path);
}
static const char *get_super_prefix_or_empty(void)
{
const char *s = get_super_prefix();
if (!s)
s = "";
return s;
}
static int submodule_has_dirty_index(const struct submodule *sub)
{
struct child_process cp = CHILD_PROCESS_INIT;
@ -2080,7 +2072,7 @@ static int submodule_has_dirty_index(const struct submodule *sub)
return finish_command(&cp);
}
static void submodule_reset_index(const char *path)
static void submodule_reset_index(const char *path, const char *super_prefix)
{
struct child_process cp = CHILD_PROCESS_INIT;
prepare_submodule_repo_env(&cp.env);
@ -2089,10 +2081,10 @@ static void submodule_reset_index(const char *path)
cp.no_stdin = 1;
cp.dir = path;
strvec_pushf(&cp.args, "--super-prefix=%s%s/",
get_super_prefix_or_empty(), path);
/* TODO: determine if this might overwright untracked files */
strvec_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
strvec_pushf(&cp.args, "--super-prefix=%s%s/",
(super_prefix ? super_prefix : ""), path);
strvec_push(&cp.args, empty_tree_oid_hex());
@ -2105,10 +2097,9 @@ static void submodule_reset_index(const char *path)
* For edge cases (a submodule coming into existence or removing a submodule)
* pass NULL for old or new respectively.
*/
int submodule_move_head(const char *path,
const char *old_head,
const char *new_head,
unsigned flags)
int submodule_move_head(const char *path, const char *super_prefix,
const char *old_head, const char *new_head,
unsigned flags)
{
int ret = 0;
struct child_process cp = CHILD_PROCESS_INIT;
@ -2145,7 +2136,8 @@ int submodule_move_head(const char *path,
if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
if (old_head) {
if (!submodule_uses_gitfile(path))
absorb_git_dir_into_superproject(path);
absorb_git_dir_into_superproject(path,
super_prefix);
} else {
struct strbuf gitdir = STRBUF_INIT;
submodule_name_to_gitdir(&gitdir, the_repository,
@ -2154,7 +2146,7 @@ int submodule_move_head(const char *path,
strbuf_release(&gitdir);
/* make sure the index is clean as well */
submodule_reset_index(path);
submodule_reset_index(path, super_prefix);
}
if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
@ -2172,9 +2164,9 @@ int submodule_move_head(const char *path,
cp.no_stdin = 1;
cp.dir = path;
strvec_pushf(&cp.args, "--super-prefix=%s%s/",
get_super_prefix_or_empty(), path);
strvec_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
strvec_pushf(&cp.args, "--super-prefix=%s%s/",
(super_prefix ? super_prefix : ""), path);
if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
strvec_push(&cp.args, "-n");
@ -2274,7 +2266,8 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
* Embeds a single submodules git directory into the superprojects git dir,
* 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;
struct strbuf new_gitdir = STRBUF_INIT;
@ -2304,7 +2297,7 @@ static void relocate_single_git_dir_into_superproject(const char *path)
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"),
get_super_prefix_or_empty(), path,
super_prefix ? super_prefix : "", path,
real_old_git_dir, real_new_git_dir);
relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
@ -2315,7 +2308,8 @@ static void relocate_single_git_dir_into_superproject(const char *path)
strbuf_release(&new_gitdir);
}
static void absorb_git_dir_into_superproject_recurse(const char *path)
static void absorb_git_dir_into_superproject_recurse(const char *path,
const char *super_prefix)
{
struct child_process cp = CHILD_PROCESS_INIT;
@ -2323,10 +2317,11 @@ static void absorb_git_dir_into_superproject_recurse(const char *path)
cp.dir = path;
cp.git_cmd = 1;
cp.no_stdin = 1;
strvec_pushf(&cp.args, "--super-prefix=%s%s/",
get_super_prefix_or_empty(), path);
strvec_pushl(&cp.args, "submodule--helper",
"absorbgitdirs", NULL);
strvec_pushf(&cp.args, "--super-prefix=%s%s/", super_prefix ?
super_prefix : "", path);
prepare_submodule_repo_env(&cp.env);
if (run_command(&cp))
die(_("could not recurse into submodule '%s'"), path);
@ -2337,7 +2332,8 @@ static void absorb_git_dir_into_superproject_recurse(const char *path)
* having its git directory within the working tree to the git dir nested
* in its superprojects git dir under modules/.
*/
void absorb_git_dir_into_superproject(const char *path)
void absorb_git_dir_into_superproject(const char *path,
const char *super_prefix)
{
int err_code;
const char *sub_git_dir;
@ -2379,14 +2375,14 @@ void absorb_git_dir_into_superproject(const char *path)
char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
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_common_git_dir);
}
strbuf_release(&gitdir);
absorb_git_dir_into_superproject_recurse(path);
absorb_git_dir_into_superproject_recurse(path, super_prefix);
}
int get_superproject_working_tree(struct strbuf *buf)

View File

@ -150,9 +150,8 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
#define SUBMODULE_MOVE_HEAD_FORCE (1<<1)
int submodule_move_head(const char *path,
const char *old,
const char *new_head,
int submodule_move_head(const char *path, const char *super_prefix,
const char *old_head, const char *new_head,
unsigned flags);
void submodule_unset_core_worktree(const struct submodule *sub);
@ -164,7 +163,8 @@ void submodule_unset_core_worktree(const struct submodule *sub);
*/
void prepare_submodule_repo_env(struct strvec *env);
void absorb_git_dir_into_superproject(const char *path);
void absorb_git_dir_into_superproject(const char *path,
const char *super_prefix);
/*
* Return the absolute path of the working tree of the superproject, which this

View File

@ -370,7 +370,7 @@ test_expect_success 'read-tree supports the super-prefix' '
cat <<-EOF >expect &&
error: Updating '\''fictional/a'\'' would lose untracked files in it
EOF
test_must_fail git --super-prefix fictional/ read-tree -u -m "$treeH" "$treeM" 2>actual &&
test_must_fail git read-tree --super-prefix fictional/ -u -m "$treeH" "$treeM" 2>actual &&
test_cmp expect actual
'

View File

@ -644,6 +644,49 @@ test_expect_success 'repack does not loosen promisor objects' '
grep "loosen_unused_packed_objects/loosened:0" trace
'
test_expect_success 'lazy-fetch in submodule succeeds' '
# setup
test_config_global protocol.file.allow always &&
test_when_finished "rm -rf src-sub" &&
git init src-sub &&
git -C src-sub config uploadpack.allowfilter 1 &&
git -C src-sub config uploadpack.allowanysha1inwant 1 &&
# This blob must be missing in the subsequent commit.
echo foo >src-sub/file &&
git -C src-sub add file &&
git -C src-sub commit -m "submodule one" &&
SUB_ONE=$(git -C src-sub rev-parse HEAD) &&
echo bar >src-sub/file &&
git -C src-sub add file &&
git -C src-sub commit -m "submodule two" &&
SUB_TWO=$(git -C src-sub rev-parse HEAD) &&
test_when_finished "rm -rf src-super" &&
git init src-super &&
git -C src-super config uploadpack.allowfilter 1 &&
git -C src-super config uploadpack.allowanysha1inwant 1 &&
git -C src-super submodule add ../src-sub src-sub &&
git -C src-super/src-sub checkout $SUB_ONE &&
git -C src-super add src-sub &&
git -C src-super commit -m "superproject one" &&
git -C src-super/src-sub checkout $SUB_TWO &&
git -C src-super add src-sub &&
git -C src-super commit -m "superproject two" &&
# the fetch
test_when_finished "rm -rf client" &&
git clone --filter=blob:none --also-filter-submodules \
--recurse-submodules "file://$(pwd)/src-super" client &&
# Trigger lazy-fetch from the superproject
git -C client restore --recurse-submodules --source=HEAD^ :/
'
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd

View File

@ -10,6 +10,7 @@ TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a real submodule' '
cwd="$(pwd)" &&
git init sub1 &&
test_commit -C sub1 first &&
git submodule add ./sub1 &&
@ -18,13 +19,21 @@ test_expect_success 'setup a real submodule' '
'
test_expect_success 'absorb the git dir' '
>expect &&
>actual &&
>expect.1 &&
>expect.2 &&
>actual.1 &&
>actual.2 &&
git status >expect.1 &&
git -C sub1 rev-parse HEAD >expect.2 &&
git submodule absorbgitdirs &&
cat >expect <<-EOF &&
Migrating git directory of '\''sub1'\'' from
'\''$cwd/sub1/.git'\'' to
'\''$cwd/.git/modules/sub1'\''
EOF
git submodule absorbgitdirs 2>actual &&
test_cmp expect actual &&
git fsck &&
test -f sub1/.git &&
test -d .git/modules/sub1 &&
@ -37,7 +46,8 @@ test_expect_success 'absorb the git dir' '
test_expect_success 'absorbing does not fail for deinitialized submodules' '
test_when_finished "git submodule update --init" &&
git submodule deinit --all &&
git submodule absorbgitdirs &&
git submodule absorbgitdirs 2>err &&
test_must_be_empty err &&
test -d .git/modules/sub1 &&
test -d sub1 &&
! test -e sub1/.git
@ -56,7 +66,13 @@ test_expect_success 'setup nested submodule' '
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 &&
cat >expect <<-EOF &&
Migrating git directory of '\''sub1/nested'\'' from
'\''$cwd/sub1/nested/.git'\'' to
'\''$cwd/.git/modules/sub1/modules/nested'\''
EOF
git submodule absorbgitdirs 2>actual &&
test_cmp expect actual &&
test -f sub1/nested/.git &&
test -d .git/modules/sub1/modules/nested &&
git status >actual.1 &&
@ -87,7 +103,13 @@ test_expect_success 're-setup nested submodule' '
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 &&
cat >expect <<-EOF &&
Migrating git directory of '\''sub1'\'' from
'\''$cwd/sub1/.git'\'' to
'\''$cwd/.git/modules/sub1'\''
EOF
git submodule absorbgitdirs 2>actual &&
test_cmp expect actual &&
test -f sub1/.git &&
test -f sub1/nested/.git &&
test -d .git/modules/sub1/modules/nested &&
@ -97,6 +119,27 @@ test_expect_success 'absorb the git dir in a nested submodule' '
test_cmp expect.2 actual.2
'
test_expect_success 'absorb the git dir outside of primary worktree' '
test_when_finished "rm -rf repo-bare.git" &&
git clone --bare . repo-bare.git &&
test_when_finished "rm -rf repo-wt" &&
git -C repo-bare.git worktree add ../repo-wt &&
test_when_finished "rm -f .gitconfig" &&
test_config_global protocol.file.allow always &&
git -C repo-wt submodule update --init &&
git init repo-wt/sub2 &&
test_commit -C repo-wt/sub2 A &&
git -C repo-wt submodule add ./sub2 sub2 &&
cat >expect <<-EOF &&
Migrating git directory of '\''sub2'\'' from
'\''$cwd/repo-wt/sub2/.git'\'' to
'\''$cwd/repo-bare.git/worktrees/repo-wt/modules/sub2'\''
EOF
git -C repo-wt submodule absorbgitdirs 2>actual &&
test_cmp expect actual
'
test_expect_success 'setup a gitlink with missing .gitmodules entry' '
git init sub2 &&
test_commit -C sub2 first &&
@ -107,7 +150,11 @@ test_expect_success 'setup a gitlink with missing .gitmodules entry' '
test_expect_success 'absorbing the git dir fails for incomplete submodules' '
git status >expect.1 &&
git -C sub2 rev-parse HEAD >expect.2 &&
test_must_fail git submodule absorbgitdirs &&
cat >expect <<-\EOF &&
fatal: could not lookup name for submodule '\''sub2'\''
EOF
test_must_fail git submodule absorbgitdirs 2>actual &&
test_cmp expect actual &&
git -C sub2 fsck &&
test -d sub2/.git &&
git status >actual &&
@ -127,8 +174,11 @@ test_expect_success 'setup a submodule with multiple worktrees' '
'
test_expect_success 'absorbing fails for a submodule with multiple worktrees' '
test_must_fail git submodule absorbgitdirs sub3 2>error &&
test_i18ngrep "not supported" error
cat >expect <<-\EOF &&
fatal: could not lookup name for submodule '\''sub2'\''
EOF
test_must_fail git submodule absorbgitdirs 2>actual &&
test_cmp expect actual
'
test_done

View File

@ -866,27 +866,9 @@ test_expect_success 'submodule always visited' '
# the submodule, and someone does a `git submodule absorbgitdirs`
# in the super, Git will recursively invoke `git submodule--helper`
# 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
# 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.
# try to start the daemon in the submodule.
have_t2_error_event () {
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_expect_success "submodule absorbgitdirs implicitly starts daemon" '
test_when_finished "rm -rf super; \
rm -rf sub; \
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 &&
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 -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

View File

@ -70,7 +70,7 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_WARNING_TYPES] = {
? ((o)->msgs[(type)]) \
: (unpack_plumbing_errors[(type)]) )
static const char *super_prefixed(const char *path)
static const char *super_prefixed(const char *path, const char *super_prefix)
{
/*
* It is necessary and sufficient to have two static buffers
@ -82,7 +82,6 @@ static const char *super_prefixed(const char *path)
static unsigned idx = ARRAY_SIZE(buf) - 1;
if (super_prefix_len < 0) {
const char *super_prefix = get_super_prefix();
if (!super_prefix) {
super_prefix_len = 0;
} else {
@ -235,7 +234,8 @@ static int add_rejected_path(struct unpack_trees_options *o,
return -1;
if (!o->show_all_errors)
return error(ERRORMSG(o, e), super_prefixed(path));
return error(ERRORMSG(o, e), super_prefixed(path,
o->super_prefix));
/*
* Otherwise, insert in a list for future display by
@ -262,7 +262,8 @@ static void display_error_msgs(struct unpack_trees_options *o)
error_displayed = 1;
for (i = 0; i < rejects->nr; i++)
strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
error(ERRORMSG(o, e), super_prefixed(path.buf));
error(ERRORMSG(o, e), super_prefixed(path.buf,
o->super_prefix));
strbuf_release(&path);
}
string_list_clear(rejects, 0);
@ -289,7 +290,8 @@ static void display_warning_msgs(struct unpack_trees_options *o)
warning_displayed = 1;
for (i = 0; i < rejects->nr; i++)
strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
warning(ERRORMSG(o, e), super_prefixed(path.buf));
warning(ERRORMSG(o, e), super_prefixed(path.buf,
o->super_prefix));
strbuf_release(&path);
}
string_list_clear(rejects, 0);
@ -311,7 +313,8 @@ static int check_submodule_move_head(const struct cache_entry *ce,
if (o->reset)
flags |= SUBMODULE_MOVE_HEAD_FORCE;
if (submodule_move_head(ce->name, old_id, new_id, flags))
if (submodule_move_head(ce->name, o->super_prefix, old_id, new_id,
flags))
return add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
return 0;
}
@ -414,6 +417,7 @@ static int check_updates(struct unpack_trees_options *o,
int i, pc_workers, pc_threshold;
trace_performance_enter();
state.super_prefix = o->super_prefix;
state.force = 1;
state.quiet = 1;
state.refresh_cache = 1;
@ -444,7 +448,7 @@ static int check_updates(struct unpack_trees_options *o,
if (ce->ce_flags & CE_WT_REMOVE) {
display_progress(progress, ++cnt);
unlink_entry(ce);
unlink_entry(ce, o->super_prefix);
}
}
@ -2958,8 +2962,8 @@ int bind_merge(const struct cache_entry * const *src,
if (a && old)
return o->quiet ? -1 :
error(ERRORMSG(o, ERROR_BIND_OVERLAP),
super_prefixed(a->name),
super_prefixed(old->name));
super_prefixed(a->name, o->super_prefix),
super_prefixed(old->name, o->super_prefix));
if (!a)
return keep_entry(old, o);
else
@ -3020,7 +3024,7 @@ int stash_worktree_untracked_merge(const struct cache_entry * const *src,
if (worktree && untracked)
return error(_("worktree and untracked commit have duplicate entries: %s"),
super_prefixed(worktree->name));
super_prefixed(worktree->name, o->super_prefix));
return merged_entry(worktree ? worktree : untracked, NULL, o);
}

View File

@ -75,6 +75,7 @@ struct unpack_trees_options {
skip_cache_tree_update;
enum unpack_trees_reset_type reset;
const char *prefix;
const char *super_prefix;
int cache_bottom;
struct pathspec *pathspec;
merge_fn_t fn;