Skip to content

Commit bd455ce

Browse files
pks-tgitster
authored andcommitted
remote-curl: fix parsing of detached SHA256 heads
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 813f17f commit bd455ce

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

remote-curl.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,12 +266,23 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
266266
return list;
267267
}
268268

269+
/*
270+
* Try to detect the hash algorithm used by the remote repository when using
271+
* the dumb HTTP transport. As dumb transports cannot tell us the object hash
272+
* directly have to derive it from the advertised ref lengths.
273+
*/
269274
static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
270275
{
271276
const char *p = memchr(heads->buf, '\t', heads->len);
272277
int algo;
278+
279+
/*
280+
* In case the remote has no refs we have no way to reliably determine
281+
* the object hash used by that repository. In that case we simply fall
282+
* back to SHA1, which may or may not be correct.
283+
*/
273284
if (!p)
274-
return the_hash_algo;
285+
return &hash_algos[GIT_HASH_SHA1];
275286

276287
algo = hash_algo_by_length((p - heads->buf) / 2);
277288
if (algo == GIT_HASH_UNKNOWN)
@@ -295,6 +306,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
295306
"is this a git repository?",
296307
transport_anonymize_url(url.buf));
297308

309+
/*
310+
* Set the repository's hash algo to whatever we have just detected.
311+
* This ensures that we can correctly parse the remote references.
312+
*/
313+
repo_set_hash_algo(the_repository, hash_algo_by_ptr(options.hash_algo));
314+
298315
data = heads->buf;
299316
start = NULL;
300317
mid = data;

t/t5550-http-fetch-dumb.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,21 @@ test_expect_success 'list refs from outside any repository' '
5555
test_cmp expect actual
5656
'
5757

58+
59+
test_expect_success 'list detached HEAD from outside any repository' '
60+
git clone --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
61+
"$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" &&
62+
git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" \
63+
update-ref --no-deref HEAD refs/heads/main &&
64+
git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" update-server-info &&
65+
cat >expect <<-EOF &&
66+
$(git rev-parse main) HEAD
67+
$(git rev-parse main) refs/heads/main
68+
EOF
69+
nongit git ls-remote "$HTTPD_URL/dumb/repo-detached.git" >actual &&
70+
test_cmp expect actual
71+
'
72+
5873
test_expect_success 'create password-protected repository' '
5974
mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
6075
cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \

0 commit comments

Comments
 (0)