diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cgitrc.5.txt6
-rw-r--r--examples/cgitrc5
-rw-r--r--source/cgit.c3
-rw-r--r--source/cgit.h1
-rw-r--r--source/ui-refs.c128
-rwxr-xr-xtests/t0201-limits.sh60
6 files changed, 189 insertions, 14 deletions
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index a07d190..18916f9 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -302,6 +302,12 @@ max-message-length::
Specifies the maximum number of commit message characters to display in
"log" view. Default value: "80".
+max-ref-count::
+ Specifies the number of branches and tags to list per section on the
+ "refs" view, and per page on the dedicated branch and tag pages that
+ its overflow links lead to. Default value: "200". Set to "0" to list
+ every ref on one page.
+
max-repo-count::
Specifies the number of entries to list per page on the repository
index page. The value "0" shows all repositories without limitation.
diff --git a/examples/cgitrc b/examples/cgitrc
index c035742..e86f43c 100644
--- a/examples/cgitrc
+++ b/examples/cgitrc
@@ -341,6 +341,11 @@ max-repo-count=50
# Default is 50.
max-commit-count=50
+# Branches and tags listed per section on the refs page, and per page on the
+# dedicated branch and tag pages. A value of 0 lists everything on one page.
+# Value is an integer. Default is 200.
+max-ref-count=200
+
# Max files considered when detecting renames. A value of -1 uses the git
# compile-time value. Value is an integer. Default is -1.
renamelimit=-1
diff --git a/source/cgit.c b/source/cgit.c
index ce8b340..5fcd17f 100644
--- a/source/cgit.c
+++ b/source/cgit.c
@@ -257,6 +257,8 @@ static void config_cb(const char *name, const char *value)
ctx.cfg.max_repodesc_len = atoi(value);
else if (!strcmp(name, "max-blob-size"))
ctx.cfg.max_blob_size = atoi(value);
+ else if (!strcmp(name, "max-ref-count"))
+ ctx.cfg.max_ref_count = atoi(value);
else if (!strcmp(name, "max-repo-count")) {
ctx.cfg.max_repo_count = atoi(value);
if (ctx.cfg.max_repo_count <= 0)
@@ -417,6 +419,7 @@ static void prepare_context(void)
ctx.cfg.max_repo_count = 50;
ctx.cfg.max_commit_count = 50;
ctx.cfg.max_msg_len = 80;
+ ctx.cfg.max_ref_count = 200; /* refs beyond this paginate */
ctx.cfg.max_repodesc_len = 80;
ctx.cfg.max_blob_size = 10 * 1024; /* 10 MB; bounds per-request memory */
ctx.cfg.max_stats = 0;
diff --git a/source/cgit.h b/source/cgit.h
index d921e76..1b28023 100644
--- a/source/cgit.h
+++ b/source/cgit.h
@@ -250,6 +250,7 @@ struct cgit_config {
int max_repo_count;
int max_commit_count;
int max_msg_len;
+ int max_ref_count;
int max_repodesc_len;
int max_blob_size;
int max_stats;
diff --git a/source/ui-refs.c b/source/ui-refs.c
index 500adfd..a38bfc4 100644
--- a/source/ui-refs.c
+++ b/source/ui-refs.c
@@ -145,23 +145,60 @@ static void print_refs_link(const char *path)
html("</td></tr>");
}
-void cgit_print_branches(int maxcount)
+/* Prev/next links for the dedicated branch and tag pages, so each
+ * category pages independently instead of sharing one endless page. */
+static void print_ref_pager(int ofs, int pagesize, int count, const char *path)
{
- struct reflist list;
- int i;
+ char *url;
+
+ html("<tr class='nohover'><td colspan='5' class='refs-pager'>");
+ if (ofs > 0) {
+ url = cgit_pageurl(ctx.qry.repo, fmt("refs/%s", path),
+ fmt("ofs=%d", ofs > pagesize ? ofs - pagesize : 0));
+ html("<a href='");
+ html_attr(url);
+ html("'>[prev]</a> ");
+ free(url);
+ }
+ htmlf("%d - %d of %d", ofs + 1,
+ ofs + pagesize < count ? ofs + pagesize : count, count);
+ if (ofs + pagesize < count) {
+ url = cgit_pageurl(ctx.qry.repo, fmt("refs/%s", path),
+ fmt("ofs=%d", ofs + pagesize));
+ html(" <a href='");
+ html_attr(url);
+ html("'>[next]</a>");
+ free(url);
+ }
+ html("</td></tr>");
+}
+static void collect_branches(struct reflist *list)
+{
+ list->refs = NULL;
+ list->alloc = list->count = 0;
+ refs_for_each_branch_ref(get_main_ref_store(the_repository),
+ cgit_refs_cb, list);
+ if (ctx.repo->enable_remote_branches)
+ refs_for_each_remote_ref(get_main_ref_store(the_repository),
+ cgit_refs_cb, list);
+}
+
+static void print_branch_header(void)
+{
html("<tr class='nohover'><th class='left'>Branch</th>"
"<th class='left'>Commit message</th>"
"<th class='left'>Author</th>"
"<th class='left' colspan='2'>Age</th></tr>\n");
+}
- list.refs = NULL;
- list.alloc = list.count = 0;
- refs_for_each_branch_ref(get_main_ref_store(the_repository),
- cgit_refs_cb, &list);
- if (ctx.repo->enable_remote_branches)
- refs_for_each_remote_ref(get_main_ref_store(the_repository),
- cgit_refs_cb, &list);
+void cgit_print_branches(int maxcount)
+{
+ struct reflist list;
+ int i;
+
+ print_branch_header();
+ collect_branches(&list);
if (maxcount == 0 || maxcount > list.count)
maxcount = list.count;
@@ -205,19 +242,82 @@ void cgit_print_tags(int maxcount)
cgit_free_reflist_inner(&list);
}
+/* The dedicated branch page lists everything, a page at a time. The
+ * whole list is name-sorted (or age-sorted per branch-sort) so the
+ * order is stable across pages. */
+static void print_branches_page(int pagesize)
+{
+ struct reflist list;
+ int i, ofs, end;
+
+ print_branch_header();
+ collect_branches(&list);
+
+ qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
+ if (ctx.repo->branch_sort == 0)
+ qsort(list.refs, list.count, sizeof(*list.refs), cmp_ref_name);
+
+ if (pagesize <= 0 || pagesize > list.count)
+ pagesize = list.count;
+ ofs = ctx.qry.ofs > 0 ? ctx.qry.ofs : 0;
+ if (ofs > list.count)
+ ofs = list.count;
+ end = ofs + pagesize < list.count ? ofs + pagesize : list.count;
+
+ for (i = ofs; i < end; i++)
+ print_branch(list.refs[i]);
+
+ if (pagesize < list.count)
+ print_ref_pager(ofs, pagesize, list.count, "heads");
+
+ cgit_free_reflist_inner(&list);
+}
+
+static void print_tags_page(int pagesize)
+{
+ struct reflist list;
+ int i, ofs, end;
+
+ list.refs = NULL;
+ list.alloc = list.count = 0;
+ refs_for_each_tag_ref(get_main_ref_store(the_repository),
+ cgit_refs_cb, &list);
+ if (list.count == 0)
+ return;
+ qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
+
+ if (pagesize <= 0 || pagesize > list.count)
+ pagesize = list.count;
+ ofs = ctx.qry.ofs > 0 ? ctx.qry.ofs : 0;
+ if (ofs > list.count)
+ ofs = list.count;
+ end = ofs + pagesize < list.count ? ofs + pagesize : list.count;
+
+ print_tag_header();
+ for (i = ofs; i < end; i++)
+ print_tag(list.refs[i]);
+
+ if (pagesize < list.count)
+ print_ref_pager(ofs, pagesize, list.count, "tags");
+
+ cgit_free_reflist_inner(&list);
+}
+
void cgit_print_refs(void)
{
cgit_print_layout_start();
html("<table class='list nowrap'>");
if (ctx.qry.path && starts_with(ctx.qry.path, "heads"))
- cgit_print_branches(0);
+ print_branches_page(ctx.cfg.max_ref_count);
else if (ctx.qry.path && starts_with(ctx.qry.path, "tags"))
- cgit_print_tags(0);
+ print_tags_page(ctx.cfg.max_ref_count);
else {
- cgit_print_branches(0);
+ /* The combined page caps each section, with the [...] rows
+ * leading to the dedicated pages above. */
+ cgit_print_branches(ctx.cfg.max_ref_count);
html("<tr class='nohover'><td colspan='5'>&nbsp;</td></tr>");
- cgit_print_tags(0);
+ cgit_print_tags(ctx.cfg.max_ref_count);
}
html("</table>");
cgit_print_layout_end();
diff --git a/tests/t0201-limits.sh b/tests/t0201-limits.sh
new file mode 100755
index 0000000..e7f74c6
--- /dev/null
+++ b/tests/t0201-limits.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='Check the ref listing limits'
+. ./setup.sh
+
+# A repo with several branches and tags, and a config that pins a tiny limit.
+test_expect_success 'set up limit fixtures' '
+ mkrepo repos/limits 1 &&
+ (
+ cd repos/limits &&
+ seq 1 60 >big.c &&
+ printf "int x;\n" >small.c &&
+ git add -A &&
+ git commit -m sources &&
+ seq 301 360 >big.c &&
+ printf "int y;\n" >small.c &&
+ git commit -am change &&
+ for i in 1 2 3; do git branch branch-$i || exit 1; done &&
+ for i in 1 2 3; do git tag tag-$i || exit 1; done
+ ) &&
+ {
+ echo "virtual-root=/" &&
+ echo "cache-size=0" &&
+ echo "max-ref-count=2" &&
+ echo "repo.url=limits" &&
+ echo "repo.path=$PWD/repos/limits/.git"
+ } >limitrc
+'
+
+limitq() { CGIT_CONFIG="$PWD/limitrc" QUERY_STRING="$1" cgit; }
+
+# --- The refs page caps each section and links to the category pages --------
+test_expect_success 'refs page lists max-ref-count branches and tags' '
+ limitq "url=limits/refs/" >tmp &&
+ test $(grep -c "log/?h=" tmp) -eq 2 &&
+ test $(grep -c "/tag/?h=tag-" tmp) -eq 2
+'
+
+test_expect_success 'refs page links each overflow to its own category' '
+ grep "refs/heads" tmp &&
+ grep "refs/tags" tmp
+'
+
+test_expect_success 'branch page paginates independently' '
+ limitq "url=limits/refs/heads/" >tmp &&
+ test $(grep -c "log/?h=" tmp) -eq 2 &&
+ grep "\[next\]" tmp &&
+ limitq "url=limits/refs/heads/&ofs=2" >tmp &&
+ grep "\[prev\]" tmp &&
+ ! grep "/tag/?h=tag-" tmp
+'
+
+test_expect_success 'tag page paginates independently' '
+ limitq "url=limits/refs/tags/&ofs=2" >tmp &&
+ grep "/tag/?h=tag-" tmp &&
+ grep "\[prev\]" tmp &&
+ ! grep "log/?h=" tmp
+'
+
+test_done