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>

Patrick Steinhardt committed Aug 8, 2024 at 09:35 UTC 5ac781ad624a32ca4136eae40b4f416b21f0af96
4 files changed +84 -1
Documentation/git-submodule.txt
+4 -1
@@ -136,7 +136,7 @@ If you really want to remove a submodule from the repository and commit
136 that use linkgit:git-rm[1] instead. See linkgit:gitsubmodules[7] for removal
137 options.
138
139 -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>...]::
139 +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>...]::
140 +
141 --
142 Update the registered submodules to match what the superproject
@@ -185,6 +185,9 @@ submodule with the `--init` option.
185 If `--recursive` is specified, this command will recurse into the
186 registered submodules, and update any nested submodules within.
187
188 +If `--ref-format <format>` is specified, the ref storage format of newly
189 +cloned submodules will be set accordingly.
190 +
191 If `--filter <filter-spec>` is specified, the given partial clone filter will be
192 applied to the submodule. See linkgit:git-rev-list[1] for details on filter
193 specifications.
builtin/submodule--helper.c
+30
@@ -1532,6 +1532,7 @@ struct module_clone_data {
1532 const char *url;
1533 const char *depth;
1534 struct list_objects_filter_options *filter_options;
1535 + enum ref_storage_format ref_storage_format;
1536 unsigned int quiet: 1;
1537 unsigned int progress: 1;
1538 unsigned int dissociate: 1;
@@ -1540,6 +1541,7 @@ struct module_clone_data {
1541 };
1542 #define MODULE_CLONE_DATA_INIT { \
1543 .single_branch = -1, \
1544 + .ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN, \
1545 }
1546
1547 struct submodule_alternate_setup {
@@ -1738,6 +1740,9 @@ static int clone_submodule(const struct module_clone_data *clone_data,
1740 strvec_pushl(&cp.args, "--reference",
1741 item->string, NULL);
1742 }
1743 + if (clone_data->ref_storage_format != REF_STORAGE_FORMAT_UNKNOWN)
1744 + strvec_pushf(&cp.args, "--ref-format=%s",
1745 + ref_storage_format_to_name(clone_data->ref_storage_format));
1746 if (clone_data->dissociate)
1747 strvec_push(&cp.args, "--dissociate");
1748 if (sm_gitdir && *sm_gitdir)
@@ -1832,6 +1837,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
1837 struct string_list reference = STRING_LIST_INIT_NODUP;
1838 struct list_objects_filter_options filter_options =
1839 LIST_OBJECTS_FILTER_INIT;
1840 + const char *ref_storage_format = NULL;
1841
1842 struct option module_clone_options[] = {
1843 OPT_STRING(0, "prefix", &clone_data.prefix,
@@ -1849,6 +1855,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
1855 OPT_STRING_LIST(0, "reference", &reference,
1856 N_("repo"),
1857 N_("reference repository")),
1858 + OPT_STRING(0, "ref-format", &ref_storage_format, N_("format"),
1859 + N_("specify the reference format to use")),
1860 OPT_BOOL(0, "dissociate", &dissociate,
1861 N_("use --reference only while cloning")),
1862 OPT_STRING(0, "depth", &clone_data.depth,
@@ -1875,6 +1883,11 @@ static int module_clone(int argc, const char **argv, const char *prefix)
1883 argc = parse_options(argc, argv, prefix, module_clone_options,
1884 git_submodule_helper_usage, 0);
1885
1886 + if (ref_storage_format) {
1887 + clone_data.ref_storage_format = ref_storage_format_by_name(ref_storage_format);
1888 + if (clone_data.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
1889 + die(_("unknown ref storage format '%s'"), ref_storage_format);
1890 + }
1891 clone_data.dissociate = !!dissociate;
1892 clone_data.quiet = !!quiet;
1893 clone_data.progress = !!progress;
@@ -1974,6 +1987,7 @@ struct update_data {
1987 struct submodule_update_strategy update_strategy;
1988 struct list_objects_filter_options *filter_options;
1989 struct module_list list;
1990 + enum ref_storage_format ref_storage_format;
1991 int depth;
1992 int max_jobs;
1993 int single_branch;
@@ -1997,6 +2011,7 @@ struct update_data {
2011 #define UPDATE_DATA_INIT { \
2012 .update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT, \
2013 .list = MODULE_LIST_INIT, \
2014 + .ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN, \
2015 .recommend_shallow = -1, \
2016 .references = STRING_LIST_INIT_DUP, \
2017 .single_branch = -1, \
@@ -2132,6 +2147,9 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
2147 expand_list_objects_filter_spec(suc->update_data->filter_options));
2148 if (suc->update_data->require_init)
2149 strvec_push(&child->args, "--require-init");
2150 + if (suc->update_data->ref_storage_format != REF_STORAGE_FORMAT_UNKNOWN)
2151 + strvec_pushf(&child->args, "--ref-format=%s",
2152 + ref_storage_format_to_name(suc->update_data->ref_storage_format));
2153 strvec_pushl(&child->args, "--path", sub->path, NULL);
2154 strvec_pushl(&child->args, "--name", sub->name, NULL);
2155 strvec_pushl(&child->args, "--url", url, NULL);
@@ -2562,6 +2580,9 @@ static void update_data_to_args(const struct update_data *update_data,
2580 for_each_string_list_item(item, &update_data->references)
2581 strvec_pushl(args, "--reference", item->string, NULL);
2582 }
2583 + if (update_data->ref_storage_format != REF_STORAGE_FORMAT_UNKNOWN)
2584 + strvec_pushf(args, "--ref-format=%s",
2585 + ref_storage_format_to_name(update_data->ref_storage_format));
2586 if (update_data->filter_options && update_data->filter_options->choice)
2587 strvec_pushf(args, "--filter=%s",
2588 expand_list_objects_filter_spec(
@@ -2737,6 +2758,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
2758 struct update_data opt = UPDATE_DATA_INIT;
2759 struct list_objects_filter_options filter_options =
2760 LIST_OBJECTS_FILTER_INIT;
2761 + const char *ref_storage_format = NULL;
2762 int ret;
2763 struct option module_update_options[] = {
2764 OPT__SUPER_PREFIX(&opt.super_prefix),
@@ -2760,6 +2782,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
2782 SM_UPDATE_REBASE),
2783 OPT_STRING_LIST(0, "reference", &opt.references, N_("repo"),
2784 N_("reference repository")),
2785 + OPT_STRING(0, "ref-format", &ref_storage_format, N_("format"),
2786 + N_("specify the reference format to use")),
2787 OPT_BOOL(0, "dissociate", &opt.dissociate,
2788 N_("use --reference only while cloning")),
2789 OPT_INTEGER(0, "depth", &opt.depth,
@@ -2803,6 +2827,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
2827 module_update_options);
2828 }
2829
2830 + if (ref_storage_format) {
2831 + opt.ref_storage_format = ref_storage_format_by_name(ref_storage_format);
2832 + if (opt.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
2833 + die(_("unknown ref storage format '%s'"), ref_storage_format);
2834 + }
2835 +
2836 opt.filter_options = &filter_options;
2837 opt.prefix = prefix;
2838
git-submodule.sh
+9
@@ -290,6 +290,14 @@ cmd_update()
290 -r|--rebase)
291 rebase=1
292 ;;
293 + --ref-format)
294 + case "$2" in '') usage ;; esac
295 + ref_format="--ref-format=$2"
296 + shift
297 + ;;
298 + --ref-format=*)
299 + ref_format="$1"
300 + ;;
301 --reference)
302 case "$2" in '') usage ;; esac
303 reference="--reference=$2"
@@ -371,6 +379,7 @@ cmd_update()
379 ${rebase:+--rebase} \
380 ${merge:+--merge} \
381 ${checkout:+--checkout} \
382 + ${ref_format:+"$ref_format"} \
383 ${reference:+"$reference"} \
384 ${dissociate:+"--dissociate"} \
385 ${depth:+"$depth"} \
t/t7424-submodule-mixed-ref-formats.sh new
+41
@@ -0,0 +1,41 @@
1 +#!/bin/sh
2 +
3 +test_description='submodules handle mixed ref storage formats'
4 +
5 +. ./test-lib.sh
6 +
7 +test_ref_format () {
8 + echo "$2" >expect &&
9 + git -C "$1" rev-parse --show-ref-format >actual &&
10 + test_cmp expect actual
11 +}
12 +
13 +for OTHER_FORMAT in files reftable
14 +do
15 + if test "$OTHER_FORMAT" = "$GIT_DEFAULT_REF_FORMAT"
16 + then
17 + continue
18 + fi
19 +
20 +test_expect_success 'setup' '
21 + git config set --global protocol.file.allow always
22 +'
23 +
24 +test_expect_success 'clone submodules with different ref storage format' '
25 + test_when_finished "rm -rf submodule upstream downstream" &&
26 +
27 + git init submodule &&
28 + test_commit -C submodule submodule-initial &&
29 + git init upstream &&
30 + git -C upstream submodule add "file://$(pwd)/submodule" &&
31 + git -C upstream commit -m "upstream submodule" &&
32 +
33 + git clone --no-recurse-submodules "file://$(pwd)/upstream" downstream &&
34 + test_ref_format downstream "$GIT_DEFAULT_REF_FORMAT" &&
35 + git -C downstream submodule update --init --ref-format=$OTHER_FORMAT &&
36 + test_ref_format downstream/submodule "$OTHER_FORMAT"
37 +'
38 +
39 +done
40 +
41 +test_done