1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
From a6b8305ef4f10bb1f9929536e656dca7b5805119 Mon Sep 17 00:00:00 2001
From: tslil <tslil@l-3.space>
Date: Sun, 30 Aug 2026 14:34:19 +0000
Subject: [PATCH] Add default-tab option and explicit summary page URLs
A bare repository URL (/repo/) is currently always served as the
summary page. With a readme configured, however, the about page is
often a better landing page. Add a new global cgitrc option,
default-tab, which configures the page that /repo/ should land on:
default-tab=about
When default-tab is set to anything other than the summary page, a
request for the bare repository URL is redirected to /repo/<tab>/.
Repository name links (header breadcrumb, repository list on the
index page) keep pointing at the bare repository URL, so they follow
the redirect.
The summary tab in the page header, and the summary button on the
index page, previously pointed at the bare repository URL as well,
which made them land on whatever the bare URL redirected to. They
now explicitly point at /repo/summary/. A new cgit_repo_link()
emits the bare repository URL for the links that should follow the
default-tab redirect, while cgit_summary_link() now always points at
the summary page.
Finally, the fallback in about_fn() for repositories without a
readme and without a homepage used to redirect to the repository
URL with ../ appended (e.g. /repo/about../), relying on the web
server to mangle that back to the summary page. It now redirects
straight to /repo/summary/.
Signed-off-by: tslil <tslil@l-3.space>
---
cgit.c | 31 +++++++++++++++++++++++++++++++
cgit.h | 1 +
cgitrc.5.txt | 14 ++++++++++++++
cmd.c | 11 ++++++-----
ui-repolist.c | 2 +-
ui-shared.c | 8 +++++++-
ui-shared.h | 2 ++
7 files changed, 62 insertions(+), 7 deletions(-)
diff --git a/cgit.c b/cgit.c
index ca318e8..bf62d40 100644
--- a/cgit.c
+++ b/cgit.c
@@ -163,6 +163,25 @@ static void config_cb(const char *name, const char *value)
ctx.cfg.logo_link = strdup_first_line(value);
else if (!strcmp(name, "module-link"))
ctx.cfg.module_link = strdup_first_line(value);
+ else if (!strcmp(name, "default-tab")) {
+ const char *tabs[] = {
+ "summary", "about", "log", "tree", "refs",
+ "commit", "stats"
+ };
+ const char *valid = NULL;
+ size_t i;
+ for (i = 0; i < sizeof(tabs)/sizeof(*tabs); i++)
+ if (!strcmp(value, tabs[i])) {
+ valid = tabs[i];
+ break;
+ }
+ if (valid)
+ ctx.cfg.default_tab = strdup_first_line(valid);
+ else
+ fprintf(stderr,
+ "[cgit] ignoring invalid default-tab value '%s'\n",
+ value);
+ }
else if (!strcmp(name, "strict-export"))
ctx.cfg.strict_export = strdup_first_line(value);
else if (!strcmp(name, "virtual-root"))
@@ -734,6 +753,18 @@ static void process_request(void)
if (ctx.repo)
prepare_repo_env(&nongit);
+ /* Redirect a bare repository URL to the configured default tab, if
+ * one is set. The summary page is the native handler for the bare
+ * URL, so no redirect is needed for it. */
+ if (ctx.repo && !ctx.qry.page &&
+ ctx.cfg.default_tab && strcmp(ctx.cfg.default_tab, "summary")) {
+ char *url = cgit_pageurl(ctx.repo->url, ctx.cfg.default_tab,
+ NULL);
+ cgit_redirect(url, false);
+ free(url);
+ return;
+ }
+
cmd = cgit_get_cmd();
if (!cmd) {
ctx.page.title = "cgit error";
diff --git a/cgit.h b/cgit.h
index 7d7ece7..48dfc17 100644
--- a/cgit.h
+++ b/cgit.h
@@ -198,6 +198,7 @@ struct cgit_config {
char *cache_root;
char *clone_prefix;
char *clone_url;
+ char *default_tab;
char *favicon;
char *footer;
char *head_include;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 7c39bf9..c488478 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -129,6 +129,20 @@ css::
Default value: "/cgit.css". May be given multiple times, each
css URL path is added in the head section of the document in turn.
+default-tab::
+ The page to use as the default landing page for a repository. When
+ set to a value other than "summary", a request for the bare repository
+ URL (e.g. /repo/) is redirected to /repo/<tab>/, so the named tab
+ becomes the repository's landing page. Repository name links (the
+ header breadcrumb and the repository list on the index page) point at
+ the bare repository URL and therefore follow this redirect. The value
+ must be one of: "summary", "about", "log", "tree", "refs", "commit",
+ or "stats". Default value: "summary" (the bare repository URL serves
+ the summary page directly, as in stock cgit). Note that an about page
+ only exists for repositories which have a configured readme file (see
+ "repo.readme"); for repositories without one, /repo/about/ redirects
+ to /repo/summary/. See also: "REPOSITORY SETTINGS".
+
email-filter::
Specifies a command which will be invoked to format names and email
address of committers, authors, and taggers, as represented in various
diff --git a/cmd.c b/cmd.c
index 0eb75b1..6c1777e 100644
--- a/cmd.c
+++ b/cmd.c
@@ -54,11 +54,12 @@ static void about_fn(void)
else if (ctx.repo->homepage)
cgit_redirect(ctx.repo->homepage, false);
else {
- char *currenturl = cgit_currenturl();
- char *redirect = fmtalloc("%s../", currenturl);
- cgit_redirect(redirect, false);
- free(currenturl);
- free(redirect);
+ /* No readme and no homepage: fall back to the
+ * summary page. */
+ char *url = cgit_pageurl(ctx.repo->url, "summary",
+ NULL);
+ cgit_redirect(url, false);
+ free(url);
}
} else
cgit_print_site_readme();
diff --git a/ui-repolist.c b/ui-repolist.c
index 1b224cf..e5ec5a9 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -321,7 +321,7 @@ void cgit_print_repolist(void)
}
htmlf("<tr><td class='%s'>",
!sorted && section ? "sublevel-repo" : "toplevel-repo");
- cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
+ cgit_repo_link(ctx.repo->name, NULL, NULL, NULL);
html("</td><td>");
repourl = cgit_repourl(ctx.repo->url);
html_link_open(repourl, NULL, NULL);
diff --git a/ui-shared.c b/ui-shared.c
index df52a9b..da78b67 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -332,6 +332,12 @@ static void reporevlink(const char *page, const char *name, const char *title,
void cgit_summary_link(const char *name, const char *title, const char *class,
const char *head)
+{
+ reporevlink("summary", name, title, class, head, NULL, NULL);
+}
+
+void cgit_repo_link(const char *name, const char *title, const char *class,
+ const char *head)
{
reporevlink(NULL, name, title, class, head, NULL, NULL);
}
@@ -1041,7 +1047,7 @@ static void print_header(void)
if (ctx.repo) {
cgit_index_link("index", NULL, NULL, NULL, NULL, 0, 1);
html(" : ");
- cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
+ cgit_repo_link(ctx.repo->name, NULL, NULL, NULL);
if (ctx.env.authenticated) {
html("</td><td class='form'>");
html("<form method='get'>\n");
diff --git a/ui-shared.h b/ui-shared.h
index 2a3a7f5..99ae667 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -19,6 +19,8 @@ extern void cgit_index_link(const char *name, const char *title,
const char *class, const char *pattern, const char *sort, int ofs, int always_root);
extern void cgit_summary_link(const char *name, const char *title,
const char *class, const char *head);
+extern void cgit_repo_link(const char *name, const char *title,
+ const char *class, const char *head);
extern void cgit_tag_link(const char *name, const char *title,
const char *class, const char *tag);
extern void cgit_tree_link(const char *name, const char *title,
--
2.55.0
|