Skip to content

Commit

Permalink
builtin/clone: allow remote helpers to detect repo (git-for-windows#641)
Browse files Browse the repository at this point in the history
This is a backport of git-for-windows#4908
to address git-for-windows#4843 where
cloning no longer worked if the user has configured any
`includeIf.*.onbranch` settings in their gitconfig.
  • Loading branch information
dscho authored Apr 16, 2024
2 parents f4f0afe + fd1d5e1 commit 96939fd
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 1 deletion.
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

0 comments on commit 96939fd

Please sign in to comment.