Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

builtin/clone: allow remote helpers to detect repo #4908

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions builtin/clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -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/";
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 8 additions & 1 deletion setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions t/t5801/git-remote-testgit
Original file line number Diff line number Diff line change
Expand Up @@ -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/*"

Expand Down