diff options
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Answer dumb-clone HEAD from the ref store
A reftable repository keeps only a placeholder in its HEAD file, so a dumb clone was told HEAD named refs/heads/.invalid and checked nothing out. HEAD is now answered the way git's http-backend answers it.
Diffstat (limited to '')
-rw-r--r--source/ui-clone.c32
-rwxr-xr-xtests/t0303-robustness.sh26
2 files changed, 53 insertions, 5 deletions
diff --git a/source/ui-clone.c b/source/ui-clone.c
index fcab846..610aaa6 100644
--- a/source/ui-clone.c
+++ b/source/ui-clone.c
@@ -186,11 +186,33 @@ err:
cgit_print_error_page(400, "Bad Request", "Bad request");
}
+/*
+ * HEAD is answered from the ref store, the way git's own http-backend answers
+ * it, rather than read off the disk, where a reftable repository keeps only a
+ * placeholder. The target need not exist yet, so an empty repository still
+ * names the branch a first push will create.
+ */
void cgit_clone_head(void)
{
- char *path;
-
- path = repo_git_path(the_repository, "HEAD");
- send_file(path);
- free(path);
+ struct strbuf head = STRBUF_INIT;
+ struct object_id oid;
+ const char *target;
+ int flags;
+
+ target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, &oid,
+ &flags);
+ if (!target) {
+ cgit_print_error_page(404, "Not Found", "Not found");
+ return;
+ }
+ if (flags & REF_ISSYMREF)
+ strbuf_addf(&head, "ref: %s\n", target);
+ else
+ strbuf_addf(&head, "%s\n", oid_to_hex(&oid));
+ ctx.page.mimetype = "text/plain";
+ ctx.page.filename = "HEAD";
+ ctx.page.size = head.len;
+ cgit_print_http_headers();
+ html_raw(head.buf, head.len);
+ strbuf_release(&head);
}
diff --git a/tests/t0303-robustness.sh b/tests/t0303-robustness.sh
index da5e209..14bdca7 100755
--- a/tests/t0303-robustness.sh
+++ b/tests/t0303-robustness.sh
@@ -590,6 +590,32 @@ test_expect_success 'the dumb transport withholds the alternates file' '
grep "^Status: 200" tmp
'
+# A reftable repository keeps only a placeholder in its HEAD file, so the
+# branch a client is to check out has to come from the refs instead.
+test_expect_success 'the dumb transport answers HEAD from the refs' '
+ robq "url=rob/HEAD" >tmp &&
+ grep "^Status: 200" tmp &&
+ strip_headers <tmp >head &&
+ echo "ref: refs/heads/master" >expect &&
+ test_cmp expect head &&
+ git init -q --bare --ref-format=reftable -b trunk rt.git &&
+ git -C repos/rob push -q "$PWD/rt.git" master:trunk &&
+ {
+ echo "virtual-root=/" &&
+ echo "cache-size=0" &&
+ echo "repo.url=rt" &&
+ echo "repo.path=$PWD/rt.git"
+ } >rtrc &&
+ CGIT_CONFIG="$PWD/rtrc" QUERY_STRING="url=rt/HEAD" cgit >tmp &&
+ strip_headers <tmp >head &&
+ echo "ref: refs/heads/trunk" >expect &&
+ test_cmp expect head &&
+ CGIT_CONFIG="$PWD/emptyrc" QUERY_STRING="url=empty/HEAD" cgit >tmp &&
+ strip_headers <tmp >head &&
+ echo "ref: refs/heads/master" >expect &&
+ test_cmp expect head
+'
+
# A client that goes away mid-page leaves nobody to send an error page to,
# so the request ends quietly instead of dying twice into the log. The page
# has to outgrow the pipe for the write after the reader has gone to fail.