builtin/submodule: allow cloning with different ref storage format

As submodules are proper self-contained repositories, it is perfectly
valid for them to have a different ref storage format than their parent
repository. There is no obvious way for users to ask for the ref storage
format when initializing submodules though. Whether the setup of such
mixed-ref-storage-format constellations is all that useful remains to be
seen. But there is no good reason to not expose such an option, and we
will require it in a subsequent patch.

Introduce a new `--ref-format=` option for git-submodule(1) that allows
the user to pick the ref storage format. This option will also be used
in a subsequent commit, where we start to propagate the same flag from
git-clone(1) to cloning submodules with the `--recursive` switch.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Patrick Steinhardt
2024-08-08 09:35:32 +02:00
committed by Junio C Hamano
parent d9ab8788e1
commit 5ac781ad62
4 changed files with 84 additions and 1 deletions

View File

@ -136,7 +136,7 @@ If you really want to remove a submodule from the repository and commit
that use linkgit:git-rm[1] instead. See linkgit:gitsubmodules[7] for removal that use linkgit:git-rm[1] instead. See linkgit:gitsubmodules[7] for removal
options. options.
update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--[no-]single-branch] [--filter <filter-spec>] [--] [<path>...]:: update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--ref-format <format>] [--depth <depth>] [--recursive] [--jobs <n>] [--[no-]single-branch] [--filter <filter-spec>] [--] [<path>...]::
+ +
-- --
Update the registered submodules to match what the superproject Update the registered submodules to match what the superproject
@ -185,6 +185,9 @@ submodule with the `--init` option.
If `--recursive` is specified, this command will recurse into the If `--recursive` is specified, this command will recurse into the
registered submodules, and update any nested submodules within. registered submodules, and update any nested submodules within.
If `--ref-format <format>` is specified, the ref storage format of newly
cloned submodules will be set accordingly.
If `--filter <filter-spec>` is specified, the given partial clone filter will be If `--filter <filter-spec>` is specified, the given partial clone filter will be
applied to the submodule. See linkgit:git-rev-list[1] for details on filter applied to the submodule. See linkgit:git-rev-list[1] for details on filter
specifications. specifications.

View File

@ -1532,6 +1532,7 @@ struct module_clone_data {
const char *url; const char *url;
const char *depth; const char *depth;
struct list_objects_filter_options *filter_options; struct list_objects_filter_options *filter_options;
enum ref_storage_format ref_storage_format;
unsigned int quiet: 1; unsigned int quiet: 1;
unsigned int progress: 1; unsigned int progress: 1;
unsigned int dissociate: 1; unsigned int dissociate: 1;
@ -1540,6 +1541,7 @@ struct module_clone_data {
}; };
#define MODULE_CLONE_DATA_INIT { \ #define MODULE_CLONE_DATA_INIT { \
.single_branch = -1, \ .single_branch = -1, \
.ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN, \
} }
struct submodule_alternate_setup { struct submodule_alternate_setup {
@ -1738,6 +1740,9 @@ static int clone_submodule(const struct module_clone_data *clone_data,
strvec_pushl(&cp.args, "--reference", strvec_pushl(&cp.args, "--reference",
item->string, NULL); item->string, NULL);
} }
if (clone_data->ref_storage_format != REF_STORAGE_FORMAT_UNKNOWN)
strvec_pushf(&cp.args, "--ref-format=%s",
ref_storage_format_to_name(clone_data->ref_storage_format));
if (clone_data->dissociate) if (clone_data->dissociate)
strvec_push(&cp.args, "--dissociate"); strvec_push(&cp.args, "--dissociate");
if (sm_gitdir && *sm_gitdir) if (sm_gitdir && *sm_gitdir)
@ -1832,6 +1837,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
struct string_list reference = STRING_LIST_INIT_NODUP; struct string_list reference = STRING_LIST_INIT_NODUP;
struct list_objects_filter_options filter_options = struct list_objects_filter_options filter_options =
LIST_OBJECTS_FILTER_INIT; LIST_OBJECTS_FILTER_INIT;
const char *ref_storage_format = NULL;
struct option module_clone_options[] = { struct option module_clone_options[] = {
OPT_STRING(0, "prefix", &clone_data.prefix, OPT_STRING(0, "prefix", &clone_data.prefix,
@ -1849,6 +1855,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
OPT_STRING_LIST(0, "reference", &reference, OPT_STRING_LIST(0, "reference", &reference,
N_("repo"), N_("repo"),
N_("reference repository")), N_("reference repository")),
OPT_STRING(0, "ref-format", &ref_storage_format, N_("format"),
N_("specify the reference format to use")),
OPT_BOOL(0, "dissociate", &dissociate, OPT_BOOL(0, "dissociate", &dissociate,
N_("use --reference only while cloning")), N_("use --reference only while cloning")),
OPT_STRING(0, "depth", &clone_data.depth, OPT_STRING(0, "depth", &clone_data.depth,
@ -1875,6 +1883,11 @@ static int module_clone(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, module_clone_options, argc = parse_options(argc, argv, prefix, module_clone_options,
git_submodule_helper_usage, 0); git_submodule_helper_usage, 0);
if (ref_storage_format) {
clone_data.ref_storage_format = ref_storage_format_by_name(ref_storage_format);
if (clone_data.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
die(_("unknown ref storage format '%s'"), ref_storage_format);
}
clone_data.dissociate = !!dissociate; clone_data.dissociate = !!dissociate;
clone_data.quiet = !!quiet; clone_data.quiet = !!quiet;
clone_data.progress = !!progress; clone_data.progress = !!progress;
@ -1974,6 +1987,7 @@ struct update_data {
struct submodule_update_strategy update_strategy; struct submodule_update_strategy update_strategy;
struct list_objects_filter_options *filter_options; struct list_objects_filter_options *filter_options;
struct module_list list; struct module_list list;
enum ref_storage_format ref_storage_format;
int depth; int depth;
int max_jobs; int max_jobs;
int single_branch; int single_branch;
@ -1997,6 +2011,7 @@ struct update_data {
#define UPDATE_DATA_INIT { \ #define UPDATE_DATA_INIT { \
.update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT, \ .update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT, \
.list = MODULE_LIST_INIT, \ .list = MODULE_LIST_INIT, \
.ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN, \
.recommend_shallow = -1, \ .recommend_shallow = -1, \
.references = STRING_LIST_INIT_DUP, \ .references = STRING_LIST_INIT_DUP, \
.single_branch = -1, \ .single_branch = -1, \
@ -2132,6 +2147,9 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
expand_list_objects_filter_spec(suc->update_data->filter_options)); expand_list_objects_filter_spec(suc->update_data->filter_options));
if (suc->update_data->require_init) if (suc->update_data->require_init)
strvec_push(&child->args, "--require-init"); strvec_push(&child->args, "--require-init");
if (suc->update_data->ref_storage_format != REF_STORAGE_FORMAT_UNKNOWN)
strvec_pushf(&child->args, "--ref-format=%s",
ref_storage_format_to_name(suc->update_data->ref_storage_format));
strvec_pushl(&child->args, "--path", sub->path, NULL); strvec_pushl(&child->args, "--path", sub->path, NULL);
strvec_pushl(&child->args, "--name", sub->name, NULL); strvec_pushl(&child->args, "--name", sub->name, NULL);
strvec_pushl(&child->args, "--url", url, NULL); strvec_pushl(&child->args, "--url", url, NULL);
@ -2562,6 +2580,9 @@ static void update_data_to_args(const struct update_data *update_data,
for_each_string_list_item(item, &update_data->references) for_each_string_list_item(item, &update_data->references)
strvec_pushl(args, "--reference", item->string, NULL); strvec_pushl(args, "--reference", item->string, NULL);
} }
if (update_data->ref_storage_format != REF_STORAGE_FORMAT_UNKNOWN)
strvec_pushf(args, "--ref-format=%s",
ref_storage_format_to_name(update_data->ref_storage_format));
if (update_data->filter_options && update_data->filter_options->choice) if (update_data->filter_options && update_data->filter_options->choice)
strvec_pushf(args, "--filter=%s", strvec_pushf(args, "--filter=%s",
expand_list_objects_filter_spec( expand_list_objects_filter_spec(
@ -2737,6 +2758,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
struct update_data opt = UPDATE_DATA_INIT; struct update_data opt = UPDATE_DATA_INIT;
struct list_objects_filter_options filter_options = struct list_objects_filter_options filter_options =
LIST_OBJECTS_FILTER_INIT; LIST_OBJECTS_FILTER_INIT;
const char *ref_storage_format = NULL;
int ret; int ret;
struct option module_update_options[] = { struct option module_update_options[] = {
OPT__SUPER_PREFIX(&opt.super_prefix), OPT__SUPER_PREFIX(&opt.super_prefix),
@ -2760,6 +2782,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
SM_UPDATE_REBASE), SM_UPDATE_REBASE),
OPT_STRING_LIST(0, "reference", &opt.references, N_("repo"), OPT_STRING_LIST(0, "reference", &opt.references, N_("repo"),
N_("reference repository")), N_("reference repository")),
OPT_STRING(0, "ref-format", &ref_storage_format, N_("format"),
N_("specify the reference format to use")),
OPT_BOOL(0, "dissociate", &opt.dissociate, OPT_BOOL(0, "dissociate", &opt.dissociate,
N_("use --reference only while cloning")), N_("use --reference only while cloning")),
OPT_INTEGER(0, "depth", &opt.depth, OPT_INTEGER(0, "depth", &opt.depth,
@ -2803,6 +2827,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
module_update_options); module_update_options);
} }
if (ref_storage_format) {
opt.ref_storage_format = ref_storage_format_by_name(ref_storage_format);
if (opt.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
die(_("unknown ref storage format '%s'"), ref_storage_format);
}
opt.filter_options = &filter_options; opt.filter_options = &filter_options;
opt.prefix = prefix; opt.prefix = prefix;

View File

@ -290,6 +290,14 @@ cmd_update()
-r|--rebase) -r|--rebase)
rebase=1 rebase=1
;; ;;
--ref-format)
case "$2" in '') usage ;; esac
ref_format="--ref-format=$2"
shift
;;
--ref-format=*)
ref_format="$1"
;;
--reference) --reference)
case "$2" in '') usage ;; esac case "$2" in '') usage ;; esac
reference="--reference=$2" reference="--reference=$2"
@ -371,6 +379,7 @@ cmd_update()
${rebase:+--rebase} \ ${rebase:+--rebase} \
${merge:+--merge} \ ${merge:+--merge} \
${checkout:+--checkout} \ ${checkout:+--checkout} \
${ref_format:+"$ref_format"} \
${reference:+"$reference"} \ ${reference:+"$reference"} \
${dissociate:+"--dissociate"} \ ${dissociate:+"--dissociate"} \
${depth:+"$depth"} \ ${depth:+"$depth"} \

View File

@ -0,0 +1,41 @@
#!/bin/sh
test_description='submodules handle mixed ref storage formats'
. ./test-lib.sh
test_ref_format () {
echo "$2" >expect &&
git -C "$1" rev-parse --show-ref-format >actual &&
test_cmp expect actual
}
for OTHER_FORMAT in files reftable
do
if test "$OTHER_FORMAT" = "$GIT_DEFAULT_REF_FORMAT"
then
continue
fi
test_expect_success 'setup' '
git config set --global protocol.file.allow always
'
test_expect_success 'clone submodules with different ref storage format' '
test_when_finished "rm -rf submodule upstream downstream" &&
git init submodule &&
test_commit -C submodule submodule-initial &&
git init upstream &&
git -C upstream submodule add "file://$(pwd)/submodule" &&
git -C upstream commit -m "upstream submodule" &&
git clone --no-recurse-submodules "file://$(pwd)/upstream" downstream &&
test_ref_format downstream "$GIT_DEFAULT_REF_FORMAT" &&
git -C downstream submodule update --init --ref-format=$OTHER_FORMAT &&
test_ref_format downstream/submodule "$OTHER_FORMAT"
'
done
test_done