/* ui-refs.c: browse symbolic refs * * Copyright (C) 2006-2014 cgit Development Team * * Licensed under GNU General Public License v2 * (see LICENSE.txt for full license text) */ #define USE_THE_REPOSITORY_VARIABLE #include "cgit.h" #include "ui-refs.h" #include "html.h" #include "ui-shared.h" static inline int cmp_age(int age1, int age2) { /* age1 and age2 are assumed to be non-negative */ return age2 - age1; } static int cmp_ref_name(const void *a, const void *b) { struct refinfo *r1 = *(struct refinfo **)a; struct refinfo *r2 = *(struct refinfo **)b; return strcmp(r1->refname, r2->refname); } static int cmp_branch_age(const void *a, const void *b) { struct refinfo *r1 = *(struct refinfo **)a; struct refinfo *r2 = *(struct refinfo **)b; return cmp_age(r1->commit->committer_date, r2->commit->committer_date); } static int get_ref_age(struct refinfo *ref) { if (!ref->object) return 0; switch (ref->object->type) { case OBJ_TAG: return ref->tag ? ref->tag->tagger_date : 0; case OBJ_COMMIT: return ref->commit ? ref->commit->committer_date : 0; } return 0; } static int cmp_tag_age(const void *a, const void *b) { struct refinfo *r1 = *(struct refinfo **)a; struct refinfo *r2 = *(struct refinfo **)b; return cmp_age(get_ref_age(r1), get_ref_age(r2)); } static int print_branch(struct refinfo *ref) { struct commitinfo *info = ref->commit; char *name = (char *)ref->refname; if (!info) return 1; html(""); cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL, ctx.qry.showmsg, 0); html(""); if (ref->object->type == OBJ_COMMIT) { cgit_commit_link(info->subject, NULL, NULL, name, NULL, NULL); html(""); cgit_open_filter(ctx.repo->email_filter, info->author_email, "refs"); html_txt(info->author); cgit_close_filter(ctx.repo->email_filter); html(""); cgit_print_age(info->committer_date, info->committer_tz, -1); } else { html(""); cgit_object_link(ref->object); } html("\n"); return 0; } static void print_tag_header(void) { html("Tag" "Download" "Author" "Age\n"); } static int print_tag(struct refinfo *ref) { struct tag *tag = NULL; struct taginfo *info = NULL; char *name = (char *)ref->refname; struct object *obj = ref->object; if (obj->type == OBJ_TAG) { tag = (struct tag *)obj; obj = tag->tagged; info = ref->tag; if (!info) return 1; } html(""); cgit_tag_link(name, NULL, NULL, name); html(""); if (ctx.repo->snapshots && (obj->type == OBJ_COMMIT)) cgit_print_snapshot_links(ctx.repo, name, "  "); else cgit_object_link(obj); html(""); if (info) { if (info->tagger) { cgit_open_filter(ctx.repo->email_filter, info->tagger_email, "refs"); html_txt(info->tagger); cgit_close_filter(ctx.repo->email_filter); } } else if (ref->object->type == OBJ_COMMIT) { cgit_open_filter(ctx.repo->email_filter, ref->commit->author_email, "refs"); html_txt(ref->commit->author); cgit_close_filter(ctx.repo->email_filter); } html(""); if (info) { if (info->tagger_date > 0) cgit_print_age(info->tagger_date, info->tagger_tz, -1); } else if (ref->object->type == OBJ_COMMIT) { cgit_print_age(ref->commit->commit->date, 0, -1); } html("\n"); return 0; } static void print_refs_link(const char *path) { html(""); cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path); html(""); } /* 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) { char *url; html(""); if (ofs > 0) { url = cgit_pageurl(ctx.qry.repo, fmt("refs/%s", path), fmt("ofs=%d", ofs > pagesize ? ofs - pagesize : 0)); html("[prev] "); 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(" [next]"); free(url); } html(""); } 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("Branch" "Commit message" "Author" "Age\n"); } 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; qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age); if (ctx.repo->branch_sort == 0) qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name); for (i = 0; i < maxcount; i++) print_branch(list.refs[i]); if (maxcount < list.count) print_refs_link("heads"); cgit_free_reflist_inner(&list); } void cgit_print_tags(int maxcount) { struct reflist list; int i; 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 (!maxcount) maxcount = list.count; else if (maxcount > list.count) maxcount = list.count; print_tag_header(); for (i = 0; i < maxcount; i++) print_tag(list.refs[i]); if (maxcount < list.count) print_refs_link("tags"); 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(""); if (ctx.qry.path && starts_with(ctx.qry.path, "heads")) print_branches_page(ctx.cfg.max_ref_count); else if (ctx.qry.path && starts_with(ctx.qry.path, "tags")) print_tags_page(ctx.cfg.max_ref_count); else { /* The combined page caps each section, with the [...] rows * leading to the dedicated pages above. */ cgit_print_branches(ctx.cfg.max_ref_count); html(""); cgit_print_tags(ctx.cfg.max_ref_count); } html("
 
"); cgit_print_layout_end(); }