diff --git a/builtin/clone.c b/builtin/clone.c index bad1b70ce82551..5d7f112125ad22 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -926,6 +926,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) struct ref *mapped_refs = NULL; const struct ref *ref; struct strbuf key = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; const char *src_ref_prefix = "refs/heads/"; @@ -1125,6 +1126,50 @@ int cmd_clone(int argc, const char **argv, const char *prefix) git_dir = real_git_dir; } + /* + * We have a chicken-and-egg situation between initializing the refdb + * and spawning transport helpers: + * + * - Initializing the refdb requires us to know about the object + * format. We thus have to spawn the transport helper to learn + * about it. + * + * - The transport helper may want to access the Git repository. But + * because the refdb has not been initialized, we don't have "HEAD" + * or "refs/". Thus, the helper cannot find the Git repository. + * + * Ideally, we would have structured the helper protocol such that it's + * mandatory for the helper to first announce its capabilities without + * yet assuming a fully initialized repository. Like that, we could + * have added a "lazy-refdb-init" capability that announces whether the + * helper is ready to handle not-yet-initialized refdbs. If any helper + * didn't support them, we would have fully initialized the refdb with + * the SHA1 object format, but later on bailed out if we found out that + * the remote repository used a different object format. + * + * But we didn't, and thus we use the following workaround to partially + * initialize the repository's refdb such that it can be discovered by + * Git commands. To do so, we: + * + * - Create an invalid HEAD ref pointing at "refs/heads/.invalid". + * + * - Create the "refs/" directory. + * + * - Set up the ref storage format and repository version as + * required. + * + * This is sufficient for Git commands to discover the Git directory. + */ + initialize_repository_version(GIT_HASH_UNKNOWN, + the_repository->ref_storage_format, 1); + + strbuf_addf(&buf, "%s/HEAD", git_dir); + write_file(buf.buf, "ref: refs/heads/.invalid"); + + strbuf_reset(&buf); + strbuf_addf(&buf, "%s/refs", git_dir); + safe_create_dir(buf.buf, 1); + /* * additional config can be injected with -c, make sure it's included * after init_db, which clears the entire config environment. @@ -1453,6 +1498,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) free(remote_name); strbuf_release(&reflog_msg); strbuf_release(&branch_top); + strbuf_release(&buf); strbuf_release(&key); free_refs(mapped_refs); free_refs(remote_head_points_at); diff --git a/setup.c b/setup.c index 251281ad09feae..ffb76478dac5c5 100644 --- a/setup.c +++ b/setup.c @@ -1898,6 +1898,13 @@ void initialize_repository_version(int hash_algo, char repo_version_string[10]; int repo_version = GIT_REPO_VERSION; + /* + * Note that we initialize the repository version to 1 when the ref + * storage format is unknown. This is on purpose so that we can add the + * correct object format to the config during git-clone(1). The format + * version will get adjusted by git-clone(1) once it has learned about + * the remote repository's format. + */ if (hash_algo != GIT_HASH_SHA1 || ref_storage_format != REF_STORAGE_FORMAT_FILES) repo_version = GIT_REPO_VERSION_READ; @@ -1907,7 +1914,7 @@ void initialize_repository_version(int hash_algo, "%d", repo_version); git_config_set("core.repositoryformatversion", repo_version_string); - if (hash_algo != GIT_HASH_SHA1) + if (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN) git_config_set("extensions.objectformat", hash_algos[hash_algo].name); else if (reinit) diff --git a/t/t5801/git-remote-testgit b/t/t5801/git-remote-testgit index 1544d6dc6ba191..bcfb358c51cc08 100755 --- a/t/t5801/git-remote-testgit +++ b/t/t5801/git-remote-testgit @@ -12,6 +12,11 @@ url=$2 dir="$GIT_DIR/testgit/$alias" +if ! git rev-parse --is-inside-git-dir +then + exit 1 +fi + h_refspec="refs/heads/*:refs/testgit/$alias/heads/*" t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"