]> git.ipfire.org Git - thirdparty/git.git/commitdiff
remote-curl: fix parsing of detached SHA256 heads
authorPatrick Steinhardt <ps@pks.im>
Tue, 7 May 2024 04:53:10 +0000 (06:53 +0200)
committerJunio C Hamano <gitster@pobox.com>
Tue, 7 May 2024 05:50:49 +0000 (22:50 -0700)
The dumb HTTP transport tries to read the remote HEAD reference by
downloading the "HEAD" file and then parsing it via `http_fetch_ref()`.
This function will either parse the file as an object ID in case it is
exactly `the_hash_algo->hexsz` long, or otherwise it will check whether
the reference starts with "ref :" and parse it as a symbolic ref.

This is broken when parsing detached HEADs of a remote SHA256 repository
because we never update `the_hash_algo` to the discovered remote object
hash. Consequently, `the_hash_algo` will always be the fallback SHA1
hash algorithm, which will cause us to fail parsing HEAD altogteher when
it contains a SHA256 object ID.

Fix this issue by setting up `the_hash_algo` via `repo_set_hash_algo()`.
While at it, let's make the expected SHA1 fallback explicit in our code,
which also addresses an upcoming issue where we are going to remove the
SHA1 fallback for `the_hash_algo`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
remote-curl.c
t/t5550-http-fetch-dumb.sh

index 0b6d7815fddd1e36be4aae007d5109cf5ba32cf1..004b707fdf475773764083a2b349850e613f48e0 100644 (file)
@@ -266,12 +266,23 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
        return list;
 }
 
+/*
+ * Try to detect the hash algorithm used by the remote repository when using
+ * the dumb HTTP transport. As dumb transports cannot tell us the object hash
+ * directly have to derive it from the advertised ref lengths.
+ */
 static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
 {
        const char *p = memchr(heads->buf, '\t', heads->len);
        int algo;
+
+       /*
+        * In case the remote has no refs we have no way to reliably determine
+        * the object hash used by that repository. In that case we simply fall
+        * back to SHA1, which may or may not be correct.
+        */
        if (!p)
-               return the_hash_algo;
+               return &hash_algos[GIT_HASH_SHA1];
 
        algo = hash_algo_by_length((p - heads->buf) / 2);
        if (algo == GIT_HASH_UNKNOWN)
@@ -295,6 +306,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
                    "is this a git repository?",
                    transport_anonymize_url(url.buf));
 
+       /*
+        * Set the repository's hash algo to whatever we have just detected.
+        * This ensures that we can correctly parse the remote references.
+        */
+       repo_set_hash_algo(the_repository, hash_algo_by_ptr(options.hash_algo));
+
        data = heads->buf;
        start = NULL;
        mid = data;
index 4c3b32785d580fb32846306df720629aee9b3a4b..5f16cbc58d461a0757a34433f4baf919d6301de2 100755 (executable)
@@ -55,6 +55,21 @@ test_expect_success 'list refs from outside any repository' '
        test_cmp expect actual
 '
 
+
+test_expect_success 'list detached HEAD from outside any repository' '
+       git clone --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+               "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" \
+               update-ref --no-deref HEAD refs/heads/main &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" update-server-info &&
+       cat >expect <<-EOF &&
+       $(git rev-parse main)   HEAD
+       $(git rev-parse main)   refs/heads/main
+       EOF
+       nongit git ls-remote "$HTTPD_URL/dumb/repo-detached.git" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'create password-protected repository' '
        mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
        cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \