Merge branch 'ps/submodule-ref-format'

Support to specify ref backend for submodules has been enhanced.

* ps/submodule-ref-format:
  object: fix leaking packfiles when closing object store
  submodule: fix leaking seen submodule names
  submodule: fix leaking fetch tasks
  builtin/submodule: allow "add" to use different ref storage format
  refs: fix ref storage format for submodule ref stores
  builtin/clone: propagate ref storage format to submodules
  builtin/submodule: allow cloning with different ref storage format
  git-submodule.sh: break overly long command lines
This commit is contained in:
Junio C Hamano
2024-08-15 13:22:14 -07:00
10 changed files with 298 additions and 25 deletions

View File

@ -34,7 +34,7 @@ COMMANDS
With no arguments, shows the status of existing submodules. Several
subcommands are available to perform operations on the submodules.
add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]::
add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--ref-format <format>] [--depth <depth>] [--] <repository> [<path>]::
Add the given repository as a submodule at the given path
to the changeset to be committed next to the current
project: the current project is termed the "superproject".
@ -71,6 +71,9 @@ submodule repositories will be kept together in the same relative
location, and only the superproject's URL needs to be provided.
git-submodule will correctly locate the submodule using the relative
URL in `.gitmodules`.
+
If `--ref-format <format>` is specified, the ref storage format of newly
cloned submodules will be set accordingly.
status [--cached] [--recursive] [--] [<path>...]::
Show the status of the submodules. This will print the SHA-1 of the
@ -136,7 +139,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
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
@ -185,6 +188,9 @@ submodule with the `--init` option.
If `--recursive` is specified, this command will recurse into the
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
applied to the submodule. See linkgit:git-rev-list[1] for details on filter
specifications.

View File

@ -729,7 +729,8 @@ static int git_sparse_checkout_init(const char *repo)
return result;
}
static int checkout(int submodule_progress, int filter_submodules)
static int checkout(int submodule_progress, int filter_submodules,
enum ref_storage_format ref_storage_format)
{
struct object_id oid;
char *head;
@ -813,6 +814,10 @@ static int checkout(int submodule_progress, int filter_submodules)
strvec_push(&cmd.args, "--no-fetch");
}
if (ref_storage_format != REF_STORAGE_FORMAT_UNKNOWN)
strvec_pushf(&cmd.args, "--ref-format=%s",
ref_storage_format_to_name(ref_storage_format));
if (filter_submodules && filter_options.choice)
strvec_pushf(&cmd.args, "--filter=%s",
expand_list_objects_filter_spec(&filter_options));
@ -1536,7 +1541,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
return 1;
junk_mode = JUNK_LEAVE_REPO;
err = checkout(submodule_progress, filter_submodules);
err = checkout(submodule_progress, filter_submodules,
ref_storage_format);
free(remote_name);
strbuf_release(&reflog_msg);

View File

@ -1532,6 +1532,7 @@ struct module_clone_data {
const char *url;
int depth;
struct list_objects_filter_options *filter_options;
enum ref_storage_format ref_storage_format;
unsigned int quiet: 1;
unsigned int progress: 1;
unsigned int dissociate: 1;
@ -1540,6 +1541,7 @@ struct module_clone_data {
};
#define MODULE_CLONE_DATA_INIT { \
.single_branch = -1, \
.ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN, \
}
struct submodule_alternate_setup {
@ -1738,6 +1740,9 @@ static int clone_submodule(const struct module_clone_data *clone_data,
strvec_pushl(&cp.args, "--reference",
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)
strvec_push(&cp.args, "--dissociate");
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 list_objects_filter_options filter_options =
LIST_OBJECTS_FILTER_INIT;
const char *ref_storage_format = NULL;
struct option module_clone_options[] = {
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,
N_("repo"),
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,
N_("use --reference only while cloning")),
OPT_INTEGER(0, "depth", &clone_data.depth,
@ -1874,6 +1882,11 @@ static int module_clone(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, module_clone_options,
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.quiet = !!quiet;
clone_data.progress = !!progress;
@ -1973,6 +1986,7 @@ struct update_data {
struct submodule_update_strategy update_strategy;
struct list_objects_filter_options *filter_options;
struct module_list list;
enum ref_storage_format ref_storage_format;
int depth;
int max_jobs;
int single_branch;
@ -1996,6 +2010,7 @@ struct update_data {
#define UPDATE_DATA_INIT { \
.update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT, \
.list = MODULE_LIST_INIT, \
.ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN, \
.recommend_shallow = -1, \
.references = STRING_LIST_INIT_DUP, \
.single_branch = -1, \
@ -2131,6 +2146,9 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
expand_list_objects_filter_spec(suc->update_data->filter_options));
if (suc->update_data->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, "--name", sub->name, NULL);
strvec_pushl(&child->args, "--url", url, NULL);
@ -2565,6 +2583,9 @@ static void update_data_to_args(const struct update_data *update_data,
for_each_string_list_item(item, &update_data->references)
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)
strvec_pushf(args, "--filter=%s",
expand_list_objects_filter_spec(
@ -2740,6 +2761,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
struct update_data opt = UPDATE_DATA_INIT;
struct list_objects_filter_options filter_options =
LIST_OBJECTS_FILTER_INIT;
const char *ref_storage_format = NULL;
int ret;
struct option module_update_options[] = {
OPT__SUPER_PREFIX(&opt.super_prefix),
@ -2763,6 +2785,8 @@ static int module_update(int argc, const char **argv, const char *prefix)
SM_UPDATE_REBASE),
OPT_STRING_LIST(0, "reference", &opt.references, N_("repo"),
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,
N_("use --reference only while cloning")),
OPT_INTEGER(0, "depth", &opt.depth,
@ -2806,6 +2830,12 @@ static int module_update(int argc, const char **argv, const char *prefix)
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.prefix = prefix;
@ -3101,13 +3131,17 @@ struct add_data {
const char *sm_name;
const char *repo;
const char *realrepo;
enum ref_storage_format ref_storage_format;
int depth;
unsigned int force: 1;
unsigned int quiet: 1;
unsigned int progress: 1;
unsigned int dissociate: 1;
};
#define ADD_DATA_INIT { .depth = -1 }
#define ADD_DATA_INIT { \
.depth = -1, \
.ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN, \
}
static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path)
{
@ -3201,6 +3235,7 @@ static int add_submodule(const struct add_data *add_data)
string_list_append(&reference, p)->util = p;
}
clone_data.ref_storage_format = add_data->ref_storage_format;
clone_data.dissociate = add_data->dissociate;
if (add_data->depth >= 0)
clone_data.depth = add_data->depth;
@ -3366,6 +3401,7 @@ static int module_add(int argc, const char **argv, const char *prefix)
{
int force = 0, quiet = 0, progress = 0, dissociate = 0;
struct add_data add_data = ADD_DATA_INIT;
const char *ref_storage_format = NULL;
char *to_free = NULL;
struct option options[] = {
OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
@ -3376,6 +3412,8 @@ static int module_add(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
OPT_STRING(0, "reference", &add_data.reference_path, N_("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, N_("borrow the objects from reference repositories")),
OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
N_("sets the submodule's name to the given string "
@ -3402,6 +3440,12 @@ static int module_add(int argc, const char **argv, const char *prefix)
if (argc == 0 || argc > 2)
usage_with_options(usage, options);
if (ref_storage_format) {
add_data.ref_storage_format = ref_storage_format_by_name(ref_storage_format);
if (add_data.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
die(_("unknown ref storage format '%s'"), ref_storage_format);
}
add_data.repo = argv[0];
if (argc == 1)
add_data.sm_path = git_url_basename(add_data.repo, 0, 0);

View File

@ -94,6 +94,14 @@ cmd_add()
--reference=*)
reference_path="${1#--reference=}"
;;
--ref-format)
case "$2" in '') usage ;; esac
ref_format="--ref-format=$2"
shift
;;
--ref-format=*)
ref_format="$1"
;;
--dissociate)
dissociate=1
;;
@ -129,7 +137,18 @@ cmd_add()
usage
fi
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper add ${quiet:+--quiet} ${force:+--force} ${progress:+"--progress"} ${branch:+--branch "$branch"} ${reference_path:+--reference "$reference_path"} ${dissociate:+--dissociate} ${custom_name:+--name "$custom_name"} ${depth:+"$depth"} -- "$@"
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper add \
${quiet:+--quiet} \
${force:+--force} \
${progress:+"--progress"} \
${branch:+--branch "$branch"} \
${reference_path:+--reference "$reference_path"} \
${ref_format:+"$ref_format"} \
${dissociate:+--dissociate} \
${custom_name:+--name "$custom_name"} \
${depth:+"$depth"} \
-- \
"$@"
}
#
@ -160,7 +179,11 @@ cmd_foreach()
shift
done
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper foreach ${quiet:+--quiet} ${recursive:+--recursive} -- "$@"
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper foreach \
${quiet:+--quiet} \
${recursive:+--recursive} \
-- \
"$@"
}
#
@ -191,7 +214,10 @@ cmd_init()
shift
done
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper init ${quiet:+--quiet} -- "$@"
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper init \
${quiet:+--quiet} \
-- \
"$@"
}
#
@ -227,7 +253,12 @@ cmd_deinit()
shift
done
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${quiet:+--quiet} ${force:+--force} ${deinit_all:+--all} -- "$@"
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit \
${quiet:+--quiet} \
${force:+--force} \
${deinit_all:+--all} \
-- \
"$@"
}
#
@ -268,6 +299,14 @@ cmd_update()
-r|--rebase)
rebase=1
;;
--ref-format)
case "$2" in '') usage ;; esac
ref_format="--ref-format=$2"
shift
;;
--ref-format=*)
ref_format="$1"
;;
--reference)
case "$2" in '') usage ;; esac
reference="--reference=$2"
@ -349,6 +388,7 @@ cmd_update()
${rebase:+--rebase} \
${merge:+--merge} \
${checkout:+--checkout} \
${ref_format:+"$ref_format"} \
${reference:+"$reference"} \
${dissociate:+"--dissociate"} \
${depth:+"$depth"} \
@ -399,7 +439,12 @@ cmd_set_branch() {
shift
done
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper set-branch ${quiet:+--quiet} ${branch:+--branch "$branch"} ${default:+--default} -- "$@"
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper set-branch \
${quiet:+--quiet} \
${branch:+--branch "$branch"} \
${default:+--default} \
-- \
"$@"
}
#
@ -428,7 +473,10 @@ cmd_set_url() {
shift
done
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper set-url ${quiet:+--quiet} -- "$@"
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper set-url \
${quiet:+--quiet} \
-- \
"$@"
}
#
@ -480,7 +528,13 @@ cmd_summary() {
shift
done
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper summary ${files:+--files} ${cached:+--cached} ${for_status:+--for-status} ${summary_limit:+-n $summary_limit} -- "$@"
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper summary \
${files:+--files} \
${cached:+--cached} \
${for_status:+--for-status} \
${summary_limit:+-n $summary_limit} \
-- \
"$@"
}
#
# List all submodules, prefixed with:
@ -521,8 +575,14 @@ cmd_status()
shift
done
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper status ${quiet:+--quiet} ${cached:+--cached} ${recursive:+--recursive} -- "$@"
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper status \
${quiet:+--quiet} \
${cached:+--cached} \
${recursive:+--recursive} \
-- \
"$@"
}
#
# Sync remote urls for submodules
# This makes the value for remote.$remote.url match the value
@ -554,7 +614,11 @@ cmd_sync()
esac
done
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper sync ${quiet:+--quiet} ${recursive:+--recursive} -- "$@"
git ${wt_prefix:+-C "$wt_prefix"} submodule--helper sync \
${quiet:+--quiet} \
${recursive:+--recursive} \
-- \
"$@"
}
cmd_absorbgitdirs()

View File

@ -614,6 +614,15 @@ void raw_object_store_clear(struct raw_object_store *o)
INIT_LIST_HEAD(&o->packed_git_mru);
close_object_store(o);
/*
* `close_object_store()` only closes the packfiles, but doesn't free
* them. We thus have to do this manually.
*/
for (struct packed_git *p = o->packed_git, *next; p; p = next) {
next = p->next;
free(p);
}
o->packed_git = NULL;
hashmap_clear(&o->pack_map);

2
refs.c
View File

@ -2009,7 +2009,7 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
free(subrepo);
goto done;
}
refs = ref_store_init(subrepo, repo->ref_storage_format,
refs = ref_store_init(subrepo, subrepo->ref_storage_format,
submodule_sb.buf,
REF_STORE_READ | REF_STORE_ODB);
register_ref_store_map(&repo->submodule_ref_stores, "submodule",

View File

@ -1496,7 +1496,7 @@ static const struct submodule *get_non_gitmodules_submodule(const char *path)
return (const struct submodule *) ret;
}
static void fetch_task_release(struct fetch_task *p)
static void fetch_task_free(struct fetch_task *p)
{
if (p->free_sub)
free((void*)p->sub);
@ -1508,6 +1508,7 @@ static void fetch_task_release(struct fetch_task *p)
FREE_AND_NULL(p->repo);
strvec_clear(&p->git_args);
free(p);
}
static struct repository *get_submodule_repo_for(struct repository *r,
@ -1576,8 +1577,7 @@ static struct fetch_task *fetch_task_create(struct submodule_parallel_fetch *spf
return task;
cleanup:
fetch_task_release(task);
free(task);
fetch_task_free(task);
return NULL;
}
@ -1607,8 +1607,7 @@ get_fetch_task_from_index(struct submodule_parallel_fetch *spf,
} else {
struct strbuf empty_submodule_path = STRBUF_INIT;
fetch_task_release(task);
free(task);
fetch_task_free(task);
/*
* An empty directory is normal,
@ -1654,8 +1653,7 @@ get_fetch_task_from_changed(struct submodule_parallel_fetch *spf,
cs_data->path,
repo_find_unique_abbrev(the_repository, cs_data->super_oid, DEFAULT_ABBREV));
fetch_task_release(task);
free(task);
fetch_task_free(task);
continue;
}
@ -1763,7 +1761,7 @@ static int fetch_start_failure(struct strbuf *err UNUSED,
spf->result = 1;
fetch_task_release(task);
fetch_task_free(task);
return 0;
}
@ -1828,8 +1826,7 @@ static int fetch_finish(int retvalue, struct strbuf *err UNUSED,
}
out:
fetch_task_release(task);
fetch_task_free(task);
return 0;
}
@ -1883,6 +1880,7 @@ int fetch_submodules(struct repository *r,
strvec_clear(&spf.args);
out:
free_submodules_data(&spf.changed_submodule_names);
string_list_clear(&spf.seen_submodule_names, 0);
return spf.result;
}

View File

@ -5,6 +5,7 @@ test_description='pull can handle submodules'
GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh

View File

@ -15,6 +15,7 @@ also by committing .gitmodules and then just removing it from the filesystem.
GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '

View File

@ -0,0 +1,144 @@
#!/bin/sh
test_description='submodules handle mixed ref storage formats'
TEST_PASSES_SANITIZE_LEAK=true
. ./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 &&
# Some tests migrate the ref storage format, which does not work with
# reflogs at the time of writing these tests.
git config set --global core.logAllRefUpdates false
'
test_expect_success 'add existing repository with different ref storage format' '
test_when_finished "rm -rf parent" &&
git init parent &&
(
cd parent &&
test_commit parent &&
git init --ref-format=$OTHER_FORMAT submodule &&
test_commit -C submodule submodule &&
git submodule add ./submodule
)
'
test_expect_success 'add submodules with different ref storage format' '
test_when_finished "rm -rf submodule upstream" &&
git init submodule &&
test_commit -C submodule submodule-initial &&
git init upstream &&
test_ref_format upstream "$GIT_DEFAULT_REF_FORMAT" &&
git -C upstream submodule add --ref-format="$OTHER_FORMAT" "file://$(pwd)/submodule" &&
test_ref_format upstream/submodule "$OTHER_FORMAT"
'
test_expect_success 'recursive clone propagates 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 -am "add submodule" &&
# The upstream repository and its submodule should be using the default
# ref format.
test_ref_format upstream "$GIT_DEFAULT_REF_FORMAT" &&
test_ref_format upstream/submodule "$GIT_DEFAULT_REF_FORMAT" &&
# The cloned repositories should use the other ref format that we have
# specified via `--ref-format`. The option should propagate to cloned
# submodules.
git clone --ref-format=$OTHER_FORMAT --recurse-submodules \
upstream downstream &&
test_ref_format downstream "$OTHER_FORMAT" &&
test_ref_format downstream/submodule "$OTHER_FORMAT"
'
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"
'
test_expect_success 'status with mixed submodule ref storages' '
test_when_finished "rm -rf submodule main" &&
git init submodule &&
test_commit -C submodule submodule-initial &&
git init main &&
git -C main submodule add "file://$(pwd)/submodule" &&
git -C main commit -m "add submodule" &&
git -C main/submodule refs migrate --ref-format=$OTHER_FORMAT &&
# The main repository should use the default ref format now, whereas
# the submodule should use the other format.
test_ref_format main "$GIT_DEFAULT_REF_FORMAT" &&
test_ref_format main/submodule "$OTHER_FORMAT" &&
cat >expect <<-EOF &&
$(git -C main/submodule rev-parse HEAD) submodule (submodule-initial)
EOF
git -C main submodule status >actual &&
test_cmp expect actual
'
test_expect_success 'recursive pull with mixed formats' '
test_when_finished "rm -rf submodule upstream downstream" &&
# Set up the initial structure with an upstream repository that has a
# submodule, as well as a downstream clone of the upstream repository.
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" &&
# Clone the upstream repository such that the main repo and its
# submodules have different formats.
git clone --no-recurse-submodules "file://$(pwd)/upstream" downstream &&
git -C downstream submodule update --init --ref-format=$OTHER_FORMAT &&
test_ref_format downstream "$GIT_DEFAULT_REF_FORMAT" &&
test_ref_format downstream/submodule "$OTHER_FORMAT" &&
# Update the upstream submodule as well as the owning repository such
# that we can do a recursive pull.
test_commit -C submodule submodule-update &&
git -C upstream/submodule pull &&
git -C upstream commit -am "update the submodule" &&
git -C downstream pull --recurse-submodules &&
git -C upstream/submodule rev-parse HEAD >expect &&
git -C downstream/submodule rev-parse HEAD >actual &&
test_cmp expect actual
'
done
test_done