From 180ecdad0c0d9d2587c70ac893e881f7217a49e8 Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Sun, 1 Nov 2026 17:21:33 -1000 Subject: Answer a submodule path with the submodule The tree page read the gitlink's commit as a blob, which outside the repository is a bad object id and inside it is the commit's raw text. --- source/ui-shared.c | 42 ++++++++++++++++++++++++++++++------------ source/ui-shared.h | 6 ++++++ source/ui-tree.c | 9 +++++++-- tests/t0104-tree.sh | 9 +++++++++ 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/source/ui-shared.c b/source/ui-shared.c index e346cbd..c3eee0b 100644 --- a/source/ui-shared.c +++ b/source/ui-shared.c @@ -1522,24 +1522,21 @@ static void print_docstart(void) html_include(ctx.cfg.header); } -void cgit_print_error_page(int code, const char *msg, const char *fmt, ...) +/* + * Start the page an error is printed into, and answer whether it started. + * Once the status line is out the error can only join the body under way, + * and the page that hit it carries on to its own end. + */ +static int start_error_page(int code, const char *msg) { - va_list ap; - // An error page describes a condition that may not outlast the slot // holding it. A request naming a commit not yet pushed would cache its // 404 under the never-expiring static ttl and keep serving it after // the push, so the error is rendered straight to the visitor instead. html_flush(); cache_abandon_fill(); - va_start(ap, fmt); - // Once the status line is out the error can only join the body under - // way, and the page that hit it carries on to its own end. - if (ctx.page.headers_sent) { - vprint_error(fmt, ap); - va_end(ap); - return; - } + if (ctx.page.headers_sent) + return 0; ctx.page.status = code; ctx.page.statusmsg = msg; // The error stands where a page would name itself, ahead of the @@ -1554,9 +1551,30 @@ void cgit_print_error_page(int code, const char *msg, const char *fmt, ...) ctx.page.charset = PAGE_ENCODING; ctx.page.filename = NULL; cgit_print_layout_start(); + return 1; +} + +void cgit_print_error_page(int code, const char *msg, const char *fmt, ...) +{ + int started = start_error_page(code, msg); + va_list ap; + + va_start(ap, fmt); vprint_error(fmt, ap); va_end(ap); - cgit_print_layout_end(); + if (started) + cgit_print_layout_end(); +} + +void cgit_print_submodule_page(char *path, const char *rev) +{ + int started = start_error_page(404, "Not Found"); + + html("
This path is a submodule: "); + cgit_submodule_link("ls-mod", path, rev); + html("
\n"); + if (started) + cgit_print_layout_end(); } void cgit_print_blob_too_large(size_t size) diff --git a/source/ui-shared.h b/source/ui-shared.h index fa73c83..e52a3ae 100644 --- a/source/ui-shared.h +++ b/source/ui-shared.h @@ -142,6 +142,12 @@ extern void cgit_print_error_page(int code, const char *msg, const char *fmt, .. */ extern void cgit_print_blob_too_large(size_t size); +/* + * Answer with the 404 page for a path naming a submodule, whose commit rev + * lives in another repository, linking it the way a tree listing does. + */ +extern void cgit_print_submodule_page(char *path, const char *rev); + /* * Resolve rev to the parsed commit it names, peeling a tag, with oid set to * what rev itself names. Returns NULL once the 404 page naming rev is out. diff --git a/source/ui-tree.c b/source/ui-tree.c index 63b8984..f1b995a 100644 --- a/source/ui-tree.c +++ b/source/ui-tree.c @@ -470,6 +470,11 @@ static int walk_tree(const struct object_id *oid, struct strbuf *base, strbuf_release(&fullpath); ls_head(); return READ_TREE_RECURSIVE; + } else if (S_ISGITLINK(mode)) { + cgit_print_submodule_page(fullpath.buf, oid_to_hex(oid)); + walk->state = WALK_ERROR_SHOWN; + strbuf_release(&fullpath); + return 0; } else { bool shown = print_object(oid, mode, fullpath.buf, pathname, walk->rev); @@ -524,6 +529,6 @@ void cgit_print_tree(const char *rev, char *path) } else if (walk.state == WALK_LOOKING) { cgit_print_error_page(404, "Not Found", "Path not found"); } - // WALK_ERROR_SHOWN is left alone, since the error page print_object - // put out is already complete. + // WALK_ERROR_SHOWN is left alone, since the error page the walk put + // out is already complete. } diff --git a/tests/t0104-tree.sh b/tests/t0104-tree.sh index ca29bdc..35b1948 100755 --- a/tests/t0104-tree.sh +++ b/tests/t0104-tree.sh @@ -169,6 +169,15 @@ test_expect_success 'a file row still offers blame' ' grep ">blame" tmp ' +# A submodule's commit lives in another repository, so its path points there +# the way its row in the listing does. +test_expect_success 'a submodule path is answered with the submodule' ' + sub=$(git -C repos/submodules rev-parse HEAD~1) && + cgit_rc submodrc "url=submodules/tree/vendor/sub" >tmp && + grep "^Status: 404" tmp && + grep "
This path is a submodule: sub @ " tmp +' + # A link target inside the tree becomes a link, while an absolute one names a # file on whatever machine checks the tree out, not a path in the tree. test_expect_success 'only a link target inside the tree is linked' ' -- cgit 2.12.1