summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2007-10-27 08:55:10 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2007-10-27 08:55:10 (UTC)
commitdd0f27eb36e737261b57d6ebcbd9fe20e559470d (patch) (unidiff)
tree4729ecc84e0b24b044131546ff39bdd55a6cd9a6
parent47bae9f58d5ecae437767b8e7835b23ad1804d0b (diff)
parentac1f493b6bbc589327e9ba3303f112fcd323c6b6 (diff)
downloadcgit-dd0f27eb36e737261b57d6ebcbd9fe20e559470d.zip
cgit-dd0f27eb36e737261b57d6ebcbd9fe20e559470d.tar.gz
cgit-dd0f27eb36e737261b57d6ebcbd9fe20e559470d.tar.bz2
Merge branch 'filter-refs'
* filter-refs: Add links to the new refs page from summary page Add support for refs view Make cgit_print_branches()/cgit_print_tags() external Add descriptions of summary-branches and summary-tags to cgitrc Add support for config param summary-branches Move logic for age comparision from cmp_tag_age into cmp_age() Add support for config param summary-tags Sort tags by age Use reflist to print tag info Use reflist to print branch info Add functions and types for ref lists
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c3
-rw-r--r--cgit.h27
-rw-r--r--cgitrc10
-rw-r--r--shared.c49
-rw-r--r--ui-refs.c30
-rw-r--r--ui-shared.c6
-rw-r--r--ui-summary.c139
8 files changed, 229 insertions, 37 deletions
diff --git a/Makefile b/Makefile
index 8e3da72..36b5ff6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,43 +1,43 @@
1CGIT_VERSION = v0.6.3 1CGIT_VERSION = v0.6.3
2CGIT_SCRIPT_NAME = cgit.cgi 2CGIT_SCRIPT_NAME = cgit.cgi
3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit 3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
4CGIT_CONFIG = /etc/cgitrc 4CGIT_CONFIG = /etc/cgitrc
5CACHE_ROOT = /var/cache/cgit 5CACHE_ROOT = /var/cache/cgit
6SHA1_HEADER = <openssl/sha.h> 6SHA1_HEADER = <openssl/sha.h>
7GIT_VER = 1.5.3.3 7GIT_VER = 1.5.3.3
8GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 8GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
9 9
10# 10#
11# Let the user override the above settings. 11# Let the user override the above settings.
12# 12#
13-include cgit.conf 13-include cgit.conf
14 14
15 15
16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
17OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 17OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
18 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \ 18 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \
19 ui-snapshot.o ui-blob.o ui-tag.o 19 ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o
20 20
21 21
22.PHONY: all git install clean distclean force-version get-git 22.PHONY: all git install clean distclean force-version get-git
23 23
24all: cgit git 24all: cgit git
25 25
26VERSION: force-version 26VERSION: force-version
27 @./gen-version.sh "$(CGIT_VERSION)" 27 @./gen-version.sh "$(CGIT_VERSION)"
28-include VERSION 28-include VERSION
29 29
30 30
31CFLAGS += -g -Wall -Igit 31CFLAGS += -g -Wall -Igit
32CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 32CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
33CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 33CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
34CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 34CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
35CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 35CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
36CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' 36CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
37 37
38 38
39cgit: cgit.c $(OBJECTS) 39cgit: cgit.c $(OBJECTS)
40 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) 40 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
41 41
42$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION 42$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION
43 43
diff --git a/cgit.c b/cgit.c
index 1b85b15..cc18ed4 100644
--- a/cgit.c
+++ b/cgit.c
@@ -82,48 +82,51 @@ static void cgit_print_repo_page(struct cacheitem *item)
82 82
83 show_search = (cgit_cmd == CMD_LOG); 83 show_search = (cgit_cmd == CMD_LOG);
84 cgit_print_docstart(title, item); 84 cgit_print_docstart(title, item);
85 if (!cgit_cmd) { 85 if (!cgit_cmd) {
86 cgit_print_pageheader("summary", show_search); 86 cgit_print_pageheader("summary", show_search);
87 cgit_print_summary(); 87 cgit_print_summary();
88 cgit_print_docend(); 88 cgit_print_docend();
89 return; 89 return;
90 } 90 }
91 91
92 cgit_print_pageheader(cgit_query_page, show_search); 92 cgit_print_pageheader(cgit_query_page, show_search);
93 93
94 switch(cgit_cmd) { 94 switch(cgit_cmd) {
95 case CMD_LOG: 95 case CMD_LOG:
96 cgit_print_log(cgit_query_sha1, cgit_query_ofs, 96 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
97 cgit_max_commit_count, cgit_query_search, 97 cgit_max_commit_count, cgit_query_search,
98 cgit_query_path, 1); 98 cgit_query_path, 1);
99 break; 99 break;
100 case CMD_TREE: 100 case CMD_TREE:
101 cgit_print_tree(cgit_query_sha1, cgit_query_path); 101 cgit_print_tree(cgit_query_sha1, cgit_query_path);
102 break; 102 break;
103 case CMD_COMMIT: 103 case CMD_COMMIT:
104 cgit_print_commit(cgit_query_sha1); 104 cgit_print_commit(cgit_query_sha1);
105 break; 105 break;
106 case CMD_REFS:
107 cgit_print_refs();
108 break;
106 case CMD_TAG: 109 case CMD_TAG:
107 cgit_print_tag(cgit_query_sha1); 110 cgit_print_tag(cgit_query_sha1);
108 break; 111 break;
109 case CMD_DIFF: 112 case CMD_DIFF:
110 cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path); 113 cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path);
111 break; 114 break;
112 default: 115 default:
113 cgit_print_error("Invalid request"); 116 cgit_print_error("Invalid request");
114 } 117 }
115 cgit_print_docend(); 118 cgit_print_docend();
116} 119}
117 120
118static void cgit_fill_cache(struct cacheitem *item, int use_cache) 121static void cgit_fill_cache(struct cacheitem *item, int use_cache)
119{ 122{
120 static char buf[PATH_MAX]; 123 static char buf[PATH_MAX];
121 int stdout2; 124 int stdout2;
122 125
123 getcwd(buf, sizeof(buf)); 126 getcwd(buf, sizeof(buf));
124 item->st.st_mtime = time(NULL); 127 item->st.st_mtime = time(NULL);
125 128
126 if (use_cache) { 129 if (use_cache) {
127 stdout2 = chk_positive(dup(STDOUT_FILENO), 130 stdout2 = chk_positive(dup(STDOUT_FILENO),
128 "Preserving STDOUT"); 131 "Preserving STDOUT");
129 chk_zero(close(STDOUT_FILENO), "Closing STDOUT"); 132 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
diff --git a/cgit.h b/cgit.h
index e96311f..f8f0316 100644
--- a/cgit.h
+++ b/cgit.h
@@ -7,48 +7,49 @@
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <xdiff/xdiff.h> 18#include <xdiff/xdiff.h>
19 19
20 20
21/* 21/*
22 * The valid cgit repo-commands 22 * The valid cgit repo-commands
23 */ 23 */
24#define CMD_LOG 1 24#define CMD_LOG 1
25#define CMD_COMMIT 2 25#define CMD_COMMIT 2
26#define CMD_DIFF 3 26#define CMD_DIFF 3
27#define CMD_TREE 4 27#define CMD_TREE 4
28#define CMD_BLOB 5 28#define CMD_BLOB 5
29#define CMD_SNAPSHOT 6 29#define CMD_SNAPSHOT 6
30#define CMD_TAG 7 30#define CMD_TAG 7
31#define CMD_REFS 8
31 32
32/* 33/*
33 * Dateformats used on misc. pages 34 * Dateformats used on misc. pages
34 */ 35 */
35#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" 36#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
36#define FMT_SHORTDATE "%Y-%m-%d" 37#define FMT_SHORTDATE "%Y-%m-%d"
37 38
38 39
39/* 40/*
40 * Limits used for relative dates 41 * Limits used for relative dates
41 */ 42 */
42#define TM_MIN 60 43#define TM_MIN 60
43#define TM_HOUR (TM_MIN * 60) 44#define TM_HOUR (TM_MIN * 60)
44#define TM_DAY (TM_HOUR * 24) 45#define TM_DAY (TM_HOUR * 24)
45#define TM_WEEK (TM_DAY * 7) 46#define TM_WEEK (TM_DAY * 7)
46#define TM_YEAR (TM_DAY * 365) 47#define TM_YEAR (TM_DAY * 365)
47#define TM_MONTH (TM_YEAR / 12.0) 48#define TM_MONTH (TM_YEAR / 12.0)
48 49
49 50
50typedef void (*configfn)(const char *name, const char *value); 51typedef void (*configfn)(const char *name, const char *value);
51typedef void (*filepair_fn)(struct diff_filepair *pair); 52typedef void (*filepair_fn)(struct diff_filepair *pair);
52typedef void (*linediff_fn)(char *line, int len); 53typedef void (*linediff_fn)(char *line, int len);
53 54
54struct cacheitem { 55struct cacheitem {
@@ -77,112 +78,133 @@ struct repolist {
77 int length; 78 int length;
78 int count; 79 int count;
79 struct repoinfo *repos; 80 struct repoinfo *repos;
80}; 81};
81 82
82struct commitinfo { 83struct commitinfo {
83 struct commit *commit; 84 struct commit *commit;
84 char *author; 85 char *author;
85 char *author_email; 86 char *author_email;
86 unsigned long author_date; 87 unsigned long author_date;
87 char *committer; 88 char *committer;
88 char *committer_email; 89 char *committer_email;
89 unsigned long committer_date; 90 unsigned long committer_date;
90 char *subject; 91 char *subject;
91 char *msg; 92 char *msg;
92}; 93};
93 94
94struct taginfo { 95struct taginfo {
95 char *tagger; 96 char *tagger;
96 char *tagger_email; 97 char *tagger_email;
97 int tagger_date; 98 int tagger_date;
98 char *msg; 99 char *msg;
99}; 100};
100 101
102struct refinfo {
103 const char *refname;
104 struct object *object;
105 union {
106 struct taginfo *tag;
107 struct commitinfo *commit;
108 };
109};
110
111struct reflist {
112 struct refinfo **refs;
113 int alloc;
114 int count;
115};
116
101extern const char *cgit_version; 117extern const char *cgit_version;
102 118
103extern struct repolist cgit_repolist; 119extern struct repolist cgit_repolist;
104extern struct repoinfo *cgit_repo; 120extern struct repoinfo *cgit_repo;
105extern int cgit_cmd; 121extern int cgit_cmd;
106 122
107extern char *cgit_root_title; 123extern char *cgit_root_title;
108extern char *cgit_css; 124extern char *cgit_css;
109extern char *cgit_logo; 125extern char *cgit_logo;
110extern char *cgit_index_header; 126extern char *cgit_index_header;
111extern char *cgit_logo_link; 127extern char *cgit_logo_link;
112extern char *cgit_module_link; 128extern char *cgit_module_link;
113extern char *cgit_agefile; 129extern char *cgit_agefile;
114extern char *cgit_virtual_root; 130extern char *cgit_virtual_root;
115extern char *cgit_script_name; 131extern char *cgit_script_name;
116extern char *cgit_cache_root; 132extern char *cgit_cache_root;
117extern char *cgit_repo_group; 133extern char *cgit_repo_group;
118 134
119extern int cgit_nocache; 135extern int cgit_nocache;
120extern int cgit_snapshots; 136extern int cgit_snapshots;
121extern int cgit_enable_index_links; 137extern int cgit_enable_index_links;
122extern int cgit_enable_log_filecount; 138extern int cgit_enable_log_filecount;
123extern int cgit_enable_log_linecount; 139extern int cgit_enable_log_linecount;
124extern int cgit_max_lock_attempts; 140extern int cgit_max_lock_attempts;
125extern int cgit_cache_root_ttl; 141extern int cgit_cache_root_ttl;
126extern int cgit_cache_repo_ttl; 142extern int cgit_cache_repo_ttl;
127extern int cgit_cache_dynamic_ttl; 143extern int cgit_cache_dynamic_ttl;
128extern int cgit_cache_static_ttl; 144extern int cgit_cache_static_ttl;
129extern int cgit_cache_max_create_time; 145extern int cgit_cache_max_create_time;
130extern int cgit_summary_log; 146extern int cgit_summary_log;
147extern int cgit_summary_tags;
148extern int cgit_summary_branches;
131 149
132extern int cgit_max_msg_len; 150extern int cgit_max_msg_len;
133extern int cgit_max_repodesc_len; 151extern int cgit_max_repodesc_len;
134extern int cgit_max_commit_count; 152extern int cgit_max_commit_count;
135 153
136extern int cgit_query_has_symref; 154extern int cgit_query_has_symref;
137extern int cgit_query_has_sha1; 155extern int cgit_query_has_sha1;
138 156
139extern char *cgit_querystring; 157extern char *cgit_querystring;
140extern char *cgit_query_repo; 158extern char *cgit_query_repo;
141extern char *cgit_query_page; 159extern char *cgit_query_page;
142extern char *cgit_query_search; 160extern char *cgit_query_search;
143extern char *cgit_query_head; 161extern char *cgit_query_head;
144extern char *cgit_query_sha1; 162extern char *cgit_query_sha1;
145extern char *cgit_query_sha2; 163extern char *cgit_query_sha2;
146extern char *cgit_query_path; 164extern char *cgit_query_path;
147extern char *cgit_query_name; 165extern char *cgit_query_name;
148extern int cgit_query_ofs; 166extern int cgit_query_ofs;
149 167
150extern int htmlfd; 168extern int htmlfd;
151 169
152extern int cgit_get_cmd_index(const char *cmd); 170extern int cgit_get_cmd_index(const char *cmd);
153extern struct repoinfo *cgit_get_repoinfo(const char *url); 171extern struct repoinfo *cgit_get_repoinfo(const char *url);
154extern void cgit_global_config_cb(const char *name, const char *value); 172extern void cgit_global_config_cb(const char *name, const char *value);
155extern void cgit_repo_config_cb(const char *name, const char *value); 173extern void cgit_repo_config_cb(const char *name, const char *value);
156extern void cgit_querystring_cb(const char *name, const char *value); 174extern void cgit_querystring_cb(const char *name, const char *value);
157 175
158extern int chk_zero(int result, char *msg); 176extern int chk_zero(int result, char *msg);
159extern int chk_positive(int result, char *msg); 177extern int chk_positive(int result, char *msg);
160extern int chk_non_negative(int result, char *msg); 178extern int chk_non_negative(int result, char *msg);
161 179
162extern int hextoint(char c); 180extern int hextoint(char c);
163extern char *trim_end(const char *str, char c); 181extern char *trim_end(const char *str, char c);
164 182
183extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
184extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
185 int flags, void *cb_data);
186
165extern void *cgit_free_commitinfo(struct commitinfo *info); 187extern void *cgit_free_commitinfo(struct commitinfo *info);
166 188
167extern int cgit_diff_files(const unsigned char *old_sha1, 189extern int cgit_diff_files(const unsigned char *old_sha1,
168 const unsigned char *new_sha1, 190 const unsigned char *new_sha1,
169 linediff_fn fn); 191 linediff_fn fn);
170 192
171extern void cgit_diff_tree(const unsigned char *old_sha1, 193extern void cgit_diff_tree(const unsigned char *old_sha1,
172 const unsigned char *new_sha1, 194 const unsigned char *new_sha1,
173 filepair_fn fn, const char *prefix); 195 filepair_fn fn, const char *prefix);
174 196
175extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 197extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
176 198
177extern char *fmt(const char *format,...); 199extern char *fmt(const char *format,...);
178 200
179extern void html(const char *txt); 201extern void html(const char *txt);
180extern void htmlf(const char *format,...); 202extern void htmlf(const char *format,...);
181extern void html_txt(char *txt); 203extern void html_txt(char *txt);
182extern void html_ntxt(int len, char *txt); 204extern void html_ntxt(int len, char *txt);
183extern void html_attr(char *txt); 205extern void html_attr(char *txt);
184extern void html_hidden(char *name, char *value); 206extern void html_hidden(char *name, char *value);
185extern void html_link_open(char *url, char *title, char *class); 207extern void html_link_open(char *url, char *title, char *class);
186extern void html_link_close(void); 208extern void html_link_close(void);
187extern void html_filemode(unsigned short mode); 209extern void html_filemode(unsigned short mode);
188extern int html_include(const char *filename); 210extern int html_include(const char *filename);
@@ -193,57 +215,62 @@ extern struct commitinfo *cgit_parse_commit(struct commit *commit);
193extern struct taginfo *cgit_parse_tag(struct tag *tag); 215extern struct taginfo *cgit_parse_tag(struct tag *tag);
194extern void cgit_parse_url(const char *url); 216extern void cgit_parse_url(const char *url);
195 217
196extern char *cache_safe_filename(const char *unsafe); 218extern char *cache_safe_filename(const char *unsafe);
197extern int cache_lock(struct cacheitem *item); 219extern int cache_lock(struct cacheitem *item);
198extern int cache_unlock(struct cacheitem *item); 220extern int cache_unlock(struct cacheitem *item);
199extern int cache_cancel_lock(struct cacheitem *item); 221extern int cache_cancel_lock(struct cacheitem *item);
200extern int cache_exist(struct cacheitem *item); 222extern int cache_exist(struct cacheitem *item);
201extern int cache_expired(struct cacheitem *item); 223extern int cache_expired(struct cacheitem *item);
202 224
203extern char *cgit_repourl(const char *reponame); 225extern char *cgit_repourl(const char *reponame);
204extern char *cgit_fileurl(const char *reponame, const char *pagename, 226extern char *cgit_fileurl(const char *reponame, const char *pagename,
205 const char *filename, const char *query); 227 const char *filename, const char *query);
206extern char *cgit_pageurl(const char *reponame, const char *pagename, 228extern char *cgit_pageurl(const char *reponame, const char *pagename,
207 const char *query); 229 const char *query);
208 230
209extern const char *cgit_repobasename(const char *reponame); 231extern const char *cgit_repobasename(const char *reponame);
210 232
211extern void cgit_tree_link(char *name, char *title, char *class, char *head, 233extern void cgit_tree_link(char *name, char *title, char *class, char *head,
212 char *rev, char *path); 234 char *rev, char *path);
213extern void cgit_log_link(char *name, char *title, char *class, char *head, 235extern void cgit_log_link(char *name, char *title, char *class, char *head,
214 char *rev, char *path, int ofs); 236 char *rev, char *path, int ofs);
215extern void cgit_commit_link(char *name, char *title, char *class, char *head, 237extern void cgit_commit_link(char *name, char *title, char *class, char *head,
216 char *rev); 238 char *rev);
239extern void cgit_refs_link(char *name, char *title, char *class, char *head,
240 char *rev, char *path);
217extern void cgit_snapshot_link(char *name, char *title, char *class, 241extern void cgit_snapshot_link(char *name, char *title, char *class,
218 char *head, char *rev, char *archivename); 242 char *head, char *rev, char *archivename);
219extern void cgit_diff_link(char *name, char *title, char *class, char *head, 243extern void cgit_diff_link(char *name, char *title, char *class, char *head,
220 char *new_rev, char *old_rev, char *path); 244 char *new_rev, char *old_rev, char *path);
221 245
222extern void cgit_object_link(struct object *obj); 246extern void cgit_object_link(struct object *obj);
223 247
224extern void cgit_print_error(char *msg); 248extern void cgit_print_error(char *msg);
225extern void cgit_print_date(time_t secs, char *format); 249extern void cgit_print_date(time_t secs, char *format);
226extern void cgit_print_age(time_t t, time_t max_relative, char *format); 250extern void cgit_print_age(time_t t, time_t max_relative, char *format);
227extern void cgit_print_docstart(char *title, struct cacheitem *item); 251extern void cgit_print_docstart(char *title, struct cacheitem *item);
228extern void cgit_print_docend(); 252extern void cgit_print_docend();
229extern void cgit_print_pageheader(char *title, int show_search); 253extern void cgit_print_pageheader(char *title, int show_search);
230extern void cgit_print_snapshot_start(const char *mimetype, 254extern void cgit_print_snapshot_start(const char *mimetype,
231 const char *filename, 255 const char *filename,
232 struct cacheitem *item); 256 struct cacheitem *item);
257extern void cgit_print_branches(int maxcount);
258extern void cgit_print_tags(int maxcount);
233 259
234extern void cgit_print_repolist(struct cacheitem *item); 260extern void cgit_print_repolist(struct cacheitem *item);
235extern void cgit_print_summary(); 261extern void cgit_print_summary();
236extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager); 262extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager);
237extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 263extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
238extern void cgit_print_tree(const char *rev, char *path); 264extern void cgit_print_tree(const char *rev, char *path);
239extern void cgit_print_commit(char *hex); 265extern void cgit_print_commit(char *hex);
266extern void cgit_print_refs();
240extern void cgit_print_tag(char *revname); 267extern void cgit_print_tag(char *revname);
241extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix); 268extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix);
242extern void cgit_print_snapshot(struct cacheitem *item, const char *head, 269extern void cgit_print_snapshot(struct cacheitem *item, const char *head,
243 const char *hex, const char *prefix, 270 const char *hex, const char *prefix,
244 const char *filename, int snapshot); 271 const char *filename, int snapshot);
245extern void cgit_print_snapshot_links(const char *repo, const char *head, 272extern void cgit_print_snapshot_links(const char *repo, const char *head,
246 const char *hex, int snapshots); 273 const char *hex, int snapshots);
247extern int cgit_parse_snapshots_mask(const char *str); 274extern int cgit_parse_snapshots_mask(const char *str);
248 275
249#endif /* CGIT_H */ 276#endif /* CGIT_H */
diff --git a/cgitrc b/cgitrc
index 796a62c..2b09e01 100644
--- a/cgitrc
+++ b/cgitrc
@@ -9,48 +9,58 @@
9 9
10 10
11## Set allowed snapshot types by default. Can be overridden per repo 11## Set allowed snapshot types by default. Can be overridden per repo
12# can be any combination of zip/tar.gz/tar.bz2/tar 12# can be any combination of zip/tar.gz/tar.bz2/tar
13#snapshots=0 13#snapshots=0
14 14
15 15
16## Enable/disable extra links to summary/log/tree per repo on index page 16## Enable/disable extra links to summary/log/tree per repo on index page
17#enable-index-links=0 17#enable-index-links=0
18 18
19 19
20## Enable/disable display of 'number of files changed' in log view 20## Enable/disable display of 'number of files changed' in log view
21#enable-log-filecount=0 21#enable-log-filecount=0
22 22
23 23
24## Enable/disable display of 'number of lines changed' in log view 24## Enable/disable display of 'number of lines changed' in log view
25#enable-log-linecount=0 25#enable-log-linecount=0
26 26
27 27
28## Enable/disable display of HEAD shortlog in summary view. Set it to maximum 28## Enable/disable display of HEAD shortlog in summary view. Set it to maximum
29## number of commits that should be displayed 29## number of commits that should be displayed
30#summary-log=0 30#summary-log=0
31 31
32 32
33## Restrict the number of branches printed in summary view. Set to 0 to
34## print all branches.
35#summary-branches=0
36
37
38## Restrict the number of tags printed in summary view. Set to 0 to
39## print all tags.
40#summary-tags=0
41
42
33## The "Idle" column on the repository index page can read a timestamp 43## The "Idle" column on the repository index page can read a timestamp
34## from the specified agefile (if this file cannot be found, the mtime 44## from the specified agefile (if this file cannot be found, the mtime
35## of HEAD is used). 45## of HEAD is used).
36## The cgit repo on hjemli.net uses the the following command in it's 46## The cgit repo on hjemli.net uses the the following command in it's
37## post-receive hook to update the age-file: 47## post-receive hook to update the age-file:
38## git-for-each-ref --format="%(committerdate)" --sort=-committerdate \ 48## git-for-each-ref --format="%(committerdate)" --sort=-committerdate \
39## --count=1 > $GIT_DIR/info/web/last-modifie 49## --count=1 > $GIT_DIR/info/web/last-modifie
40## 50##
41#agefile=info/web/last-modified 51#agefile=info/web/last-modified
42 52
43 53
44## Git detects renames, but with a limit on the number of files to 54## Git detects renames, but with a limit on the number of files to
45## consider. This option can be used to specify another limit (or -1 to 55## consider. This option can be used to specify another limit (or -1 to
46## use the default limit). 56## use the default limit).
47## 57##
48#renamelimit=-1 58#renamelimit=-1
49 59
50 60
51## Specify a root for virtual urls. This makes cgit generate urls like 61## Specify a root for virtual urls. This makes cgit generate urls like
52## 62##
53## http://localhost/git/repo/log/?h=branch 63## http://localhost/git/repo/log/?h=branch
54## 64##
55## instead of 65## instead of
56## 66##
diff --git a/shared.c b/shared.c
index 3d4feea..7eb2b0e 100644
--- a/shared.c
+++ b/shared.c
@@ -17,75 +17,77 @@ const char *cgit_version = CGIT_VERSION;
17char *cgit_root_title = "Git repository browser"; 17char *cgit_root_title = "Git repository browser";
18char *cgit_css = "/cgit.css"; 18char *cgit_css = "/cgit.css";
19char *cgit_logo = "/git-logo.png"; 19char *cgit_logo = "/git-logo.png";
20char *cgit_index_header = NULL; 20char *cgit_index_header = NULL;
21char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; 21char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
22char *cgit_module_link = "./?repo=%s&page=commit&id=%s"; 22char *cgit_module_link = "./?repo=%s&page=commit&id=%s";
23char *cgit_agefile = "info/web/last-modified"; 23char *cgit_agefile = "info/web/last-modified";
24char *cgit_virtual_root = NULL; 24char *cgit_virtual_root = NULL;
25char *cgit_script_name = CGIT_SCRIPT_NAME; 25char *cgit_script_name = CGIT_SCRIPT_NAME;
26char *cgit_cache_root = CGIT_CACHE_ROOT; 26char *cgit_cache_root = CGIT_CACHE_ROOT;
27char *cgit_repo_group = NULL; 27char *cgit_repo_group = NULL;
28 28
29int cgit_nocache = 0; 29int cgit_nocache = 0;
30int cgit_snapshots = 0; 30int cgit_snapshots = 0;
31int cgit_enable_index_links = 0; 31int cgit_enable_index_links = 0;
32int cgit_enable_log_filecount = 0; 32int cgit_enable_log_filecount = 0;
33int cgit_enable_log_linecount = 0; 33int cgit_enable_log_linecount = 0;
34int cgit_max_lock_attempts = 5; 34int cgit_max_lock_attempts = 5;
35int cgit_cache_root_ttl = 5; 35int cgit_cache_root_ttl = 5;
36int cgit_cache_repo_ttl = 5; 36int cgit_cache_repo_ttl = 5;
37int cgit_cache_dynamic_ttl = 5; 37int cgit_cache_dynamic_ttl = 5;
38int cgit_cache_static_ttl = -1; 38int cgit_cache_static_ttl = -1;
39int cgit_cache_max_create_time = 5; 39int cgit_cache_max_create_time = 5;
40int cgit_summary_log = 0; 40int cgit_summary_log = 0;
41int cgit_summary_tags = 0;
42int cgit_summary_branches = 0;
41int cgit_renamelimit = -1; 43int cgit_renamelimit = -1;
42 44
43int cgit_max_msg_len = 60; 45int cgit_max_msg_len = 60;
44int cgit_max_repodesc_len = 60; 46int cgit_max_repodesc_len = 60;
45int cgit_max_commit_count = 50; 47int cgit_max_commit_count = 50;
46 48
47int cgit_query_has_symref = 0; 49int cgit_query_has_symref = 0;
48int cgit_query_has_sha1 = 0; 50int cgit_query_has_sha1 = 0;
49 51
50char *cgit_querystring = NULL; 52char *cgit_querystring = NULL;
51char *cgit_query_repo = NULL; 53char *cgit_query_repo = NULL;
52char *cgit_query_page = NULL; 54char *cgit_query_page = NULL;
53char *cgit_query_head = NULL; 55char *cgit_query_head = NULL;
54char *cgit_query_search = NULL; 56char *cgit_query_search = NULL;
55char *cgit_query_sha1 = NULL; 57char *cgit_query_sha1 = NULL;
56char *cgit_query_sha2 = NULL; 58char *cgit_query_sha2 = NULL;
57char *cgit_query_path = NULL; 59char *cgit_query_path = NULL;
58char *cgit_query_name = NULL; 60char *cgit_query_name = NULL;
59int cgit_query_ofs = 0; 61int cgit_query_ofs = 0;
60 62
61int htmlfd = 0; 63int htmlfd = 0;
62 64
63 65
64int cgit_get_cmd_index(const char *cmd) 66int cgit_get_cmd_index(const char *cmd)
65{ 67{
66 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 68 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
67 "snapshot", "tag", NULL}; 69 "snapshot", "tag", "refs", NULL};
68 int i; 70 int i;
69 71
70 for(i = 0; cmds[i]; i++) 72 for(i = 0; cmds[i]; i++)
71 if (!strcmp(cmd, cmds[i])) 73 if (!strcmp(cmd, cmds[i]))
72 return i + 1; 74 return i + 1;
73 return 0; 75 return 0;
74} 76}
75 77
76int chk_zero(int result, char *msg) 78int chk_zero(int result, char *msg)
77{ 79{
78 if (result != 0) 80 if (result != 0)
79 die("%s: %s", msg, strerror(errno)); 81 die("%s: %s", msg, strerror(errno));
80 return result; 82 return result;
81} 83}
82 84
83int chk_positive(int result, char *msg) 85int chk_positive(int result, char *msg)
84{ 86{
85 if (result <= 0) 87 if (result <= 0)
86 die("%s: %s", msg, strerror(errno)); 88 die("%s: %s", msg, strerror(errno));
87 return result; 89 return result;
88} 90}
89 91
90int chk_non_negative(int result, char *msg) 92int chk_non_negative(int result, char *msg)
91{ 93{
@@ -160,48 +162,52 @@ void cgit_global_config_cb(const char *name, const char *value)
160 else if (!strcmp(name, "enable-index-links")) 162 else if (!strcmp(name, "enable-index-links"))
161 cgit_enable_index_links = atoi(value); 163 cgit_enable_index_links = atoi(value);
162 else if (!strcmp(name, "enable-log-filecount")) 164 else if (!strcmp(name, "enable-log-filecount"))
163 cgit_enable_log_filecount = atoi(value); 165 cgit_enable_log_filecount = atoi(value);
164 else if (!strcmp(name, "enable-log-linecount")) 166 else if (!strcmp(name, "enable-log-linecount"))
165 cgit_enable_log_linecount = atoi(value); 167 cgit_enable_log_linecount = atoi(value);
166 else if (!strcmp(name, "cache-root")) 168 else if (!strcmp(name, "cache-root"))
167 cgit_cache_root = xstrdup(value); 169 cgit_cache_root = xstrdup(value);
168 else if (!strcmp(name, "cache-root-ttl")) 170 else if (!strcmp(name, "cache-root-ttl"))
169 cgit_cache_root_ttl = atoi(value); 171 cgit_cache_root_ttl = atoi(value);
170 else if (!strcmp(name, "cache-repo-ttl")) 172 else if (!strcmp(name, "cache-repo-ttl"))
171 cgit_cache_repo_ttl = atoi(value); 173 cgit_cache_repo_ttl = atoi(value);
172 else if (!strcmp(name, "cache-static-ttl")) 174 else if (!strcmp(name, "cache-static-ttl"))
173 cgit_cache_static_ttl = atoi(value); 175 cgit_cache_static_ttl = atoi(value);
174 else if (!strcmp(name, "cache-dynamic-ttl")) 176 else if (!strcmp(name, "cache-dynamic-ttl"))
175 cgit_cache_dynamic_ttl = atoi(value); 177 cgit_cache_dynamic_ttl = atoi(value);
176 else if (!strcmp(name, "max-message-length")) 178 else if (!strcmp(name, "max-message-length"))
177 cgit_max_msg_len = atoi(value); 179 cgit_max_msg_len = atoi(value);
178 else if (!strcmp(name, "max-repodesc-length")) 180 else if (!strcmp(name, "max-repodesc-length"))
179 cgit_max_repodesc_len = atoi(value); 181 cgit_max_repodesc_len = atoi(value);
180 else if (!strcmp(name, "max-commit-count")) 182 else if (!strcmp(name, "max-commit-count"))
181 cgit_max_commit_count = atoi(value); 183 cgit_max_commit_count = atoi(value);
182 else if (!strcmp(name, "summary-log")) 184 else if (!strcmp(name, "summary-log"))
183 cgit_summary_log = atoi(value); 185 cgit_summary_log = atoi(value);
186 else if (!strcmp(name, "summary-branches"))
187 cgit_summary_branches = atoi(value);
188 else if (!strcmp(name, "summary-tags"))
189 cgit_summary_tags = atoi(value);
184 else if (!strcmp(name, "agefile")) 190 else if (!strcmp(name, "agefile"))
185 cgit_agefile = xstrdup(value); 191 cgit_agefile = xstrdup(value);
186 else if (!strcmp(name, "renamelimit")) 192 else if (!strcmp(name, "renamelimit"))
187 cgit_renamelimit = atoi(value); 193 cgit_renamelimit = atoi(value);
188 else if (!strcmp(name, "repo.group")) 194 else if (!strcmp(name, "repo.group"))
189 cgit_repo_group = xstrdup(value); 195 cgit_repo_group = xstrdup(value);
190 else if (!strcmp(name, "repo.url")) 196 else if (!strcmp(name, "repo.url"))
191 cgit_repo = add_repo(value); 197 cgit_repo = add_repo(value);
192 else if (!strcmp(name, "repo.name")) 198 else if (!strcmp(name, "repo.name"))
193 cgit_repo->name = xstrdup(value); 199 cgit_repo->name = xstrdup(value);
194 else if (cgit_repo && !strcmp(name, "repo.path")) 200 else if (cgit_repo && !strcmp(name, "repo.path"))
195 cgit_repo->path = trim_end(value, '/'); 201 cgit_repo->path = trim_end(value, '/');
196 else if (cgit_repo && !strcmp(name, "repo.desc")) 202 else if (cgit_repo && !strcmp(name, "repo.desc"))
197 cgit_repo->desc = xstrdup(value); 203 cgit_repo->desc = xstrdup(value);
198 else if (cgit_repo && !strcmp(name, "repo.owner")) 204 else if (cgit_repo && !strcmp(name, "repo.owner"))
199 cgit_repo->owner = xstrdup(value); 205 cgit_repo->owner = xstrdup(value);
200 else if (cgit_repo && !strcmp(name, "repo.defbranch")) 206 else if (cgit_repo && !strcmp(name, "repo.defbranch"))
201 cgit_repo->defbranch = xstrdup(value); 207 cgit_repo->defbranch = xstrdup(value);
202 else if (cgit_repo && !strcmp(name, "repo.snapshots")) 208 else if (cgit_repo && !strcmp(name, "repo.snapshots"))
203 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 209 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
204 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) 210 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
205 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); 211 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
206 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) 212 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
207 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value); 213 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value);
@@ -270,48 +276,89 @@ int hextoint(char c)
270} 276}
271 277
272char *trim_end(const char *str, char c) 278char *trim_end(const char *str, char c)
273{ 279{
274 int len; 280 int len;
275 char *s, *t; 281 char *s, *t;
276 282
277 if (str == NULL) 283 if (str == NULL)
278 return NULL; 284 return NULL;
279 t = (char *)str; 285 t = (char *)str;
280 len = strlen(t); 286 len = strlen(t);
281 while(len > 0 && t[len - 1] == c) 287 while(len > 0 && t[len - 1] == c)
282 len--; 288 len--;
283 289
284 if (len == 0) 290 if (len == 0)
285 return NULL; 291 return NULL;
286 292
287 c = t[len]; 293 c = t[len];
288 t[len] = '\0'; 294 t[len] = '\0';
289 s = xstrdup(t); 295 s = xstrdup(t);
290 t[len] = c; 296 t[len] = c;
291 return s; 297 return s;
292} 298}
293 299
300void cgit_add_ref(struct reflist *list, struct refinfo *ref)
301{
302 size_t size;
303
304 if (list->count >= list->alloc) {
305 list->alloc += (list->alloc ? list->alloc : 4);
306 size = list->alloc * sizeof(struct refinfo *);
307 list->refs = xrealloc(list->refs, size);
308 }
309 list->refs[list->count++] = ref;
310}
311
312struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
313{
314 struct refinfo *ref;
315
316 ref = xmalloc(sizeof (struct refinfo));
317 ref->refname = xstrdup(refname);
318 ref->object = parse_object(sha1);
319 switch (ref->object->type) {
320 case OBJ_TAG:
321 ref->tag = cgit_parse_tag((struct tag *)ref->object);
322 break;
323 case OBJ_COMMIT:
324 ref->commit = cgit_parse_commit((struct commit *)ref->object);
325 break;
326 }
327 return ref;
328}
329
330int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
331 void *cb_data)
332{
333 struct reflist *list = (struct reflist *)cb_data;
334 struct refinfo *info = cgit_mk_refinfo(refname, sha1);
335
336 if (info)
337 cgit_add_ref(list, info);
338 return 0;
339}
340
294void cgit_diff_tree_cb(struct diff_queue_struct *q, 341void cgit_diff_tree_cb(struct diff_queue_struct *q,
295 struct diff_options *options, void *data) 342 struct diff_options *options, void *data)
296{ 343{
297 int i; 344 int i;
298 345
299 for (i = 0; i < q->nr; i++) { 346 for (i = 0; i < q->nr; i++) {
300 if (q->queue[i]->status == 'U') 347 if (q->queue[i]->status == 'U')
301 continue; 348 continue;
302 ((filepair_fn)data)(q->queue[i]); 349 ((filepair_fn)data)(q->queue[i]);
303 } 350 }
304} 351}
305 352
306static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 353static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
307{ 354{
308 enum object_type type; 355 enum object_type type;
309 356
310 if (is_null_sha1(sha1)) { 357 if (is_null_sha1(sha1)) {
311 file->ptr = (char *)""; 358 file->ptr = (char *)"";
312 file->size = 0; 359 file->size = 0;
313 } else { 360 } else {
314 file->ptr = read_sha1_file(sha1, &type, 361 file->ptr = read_sha1_file(sha1, &type,
315 (unsigned long *)&file->size); 362 (unsigned long *)&file->size);
316 } 363 }
317 return 1; 364 return 1;
diff --git a/ui-refs.c b/ui-refs.c
new file mode 100644
index 0000000..295f5ba
--- a/dev/null
+++ b/ui-refs.c
@@ -0,0 +1,30 @@
1/* ui-refs.c: browse symbolic refs
2 *
3 * Copyright (C) 2006 Lars Hjemli
4 *
5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text)
7 */
8
9#include "cgit.h"
10
11
12
13
14void cgit_print_refs()
15{
16
17 html("<table class='list nowrap'>");
18
19 if (cgit_query_path && !strncmp(cgit_query_path, "heads", 5))
20 cgit_print_branches(0);
21 else if (cgit_query_path && !strncmp(cgit_query_path, "tags", 4))
22 cgit_print_tags(0);
23 else {
24 cgit_print_branches(0);
25 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
26 cgit_print_tags(0);
27 }
28
29 html("</table>");
30}
diff --git a/ui-shared.c b/ui-shared.c
index 5c5bcf3..e4bb98f 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -206,48 +206,54 @@ void cgit_log_link(char *name, char *title, char *class, char *head,
206 delim = "&"; 206 delim = "&";
207 } 207 }
208 if (ofs > 0) { 208 if (ofs > 0) {
209 html(delim); 209 html(delim);
210 html("ofs="); 210 html("ofs=");
211 htmlf("%d", ofs); 211 htmlf("%d", ofs);
212 } 212 }
213 html("'>"); 213 html("'>");
214 html_txt(name); 214 html_txt(name);
215 html("</a>"); 215 html("</a>");
216} 216}
217 217
218void cgit_commit_link(char *name, char *title, char *class, char *head, 218void cgit_commit_link(char *name, char *title, char *class, char *head,
219 char *rev) 219 char *rev)
220{ 220{
221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { 221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
222 name[cgit_max_msg_len] = '\0'; 222 name[cgit_max_msg_len] = '\0';
223 name[cgit_max_msg_len - 1] = '.'; 223 name[cgit_max_msg_len - 1] = '.';
224 name[cgit_max_msg_len - 2] = '.'; 224 name[cgit_max_msg_len - 2] = '.';
225 name[cgit_max_msg_len - 3] = '.'; 225 name[cgit_max_msg_len - 3] = '.';
226 } 226 }
227 reporevlink("commit", name, title, class, head, rev, NULL); 227 reporevlink("commit", name, title, class, head, rev, NULL);
228} 228}
229 229
230void cgit_refs_link(char *name, char *title, char *class, char *head,
231 char *rev, char *path)
232{
233 reporevlink("refs", name, title, class, head, rev, path);
234}
235
230void cgit_snapshot_link(char *name, char *title, char *class, char *head, 236void cgit_snapshot_link(char *name, char *title, char *class, char *head,
231 char *rev, char *archivename) 237 char *rev, char *archivename)
232{ 238{
233 reporevlink("snapshot", name, title, class, head, rev, archivename); 239 reporevlink("snapshot", name, title, class, head, rev, archivename);
234} 240}
235 241
236void cgit_diff_link(char *name, char *title, char *class, char *head, 242void cgit_diff_link(char *name, char *title, char *class, char *head,
237 char *new_rev, char *old_rev, char *path) 243 char *new_rev, char *old_rev, char *path)
238{ 244{
239 char *delim; 245 char *delim;
240 246
241 delim = repolink(title, class, "diff", head, path); 247 delim = repolink(title, class, "diff", head, path);
242 if (new_rev && strcmp(new_rev, cgit_query_head)) { 248 if (new_rev && strcmp(new_rev, cgit_query_head)) {
243 html(delim); 249 html(delim);
244 html("id="); 250 html("id=");
245 html_attr(new_rev); 251 html_attr(new_rev);
246 delim = "&amp;"; 252 delim = "&amp;";
247 } 253 }
248 if (old_rev) { 254 if (old_rev) {
249 html(delim); 255 html(delim);
250 html("id2="); 256 html("id2=");
251 html_attr(old_rev); 257 html_attr(old_rev);
252 } 258 }
253 html("'>"); 259 html("'>");
diff --git a/ui-summary.c b/ui-summary.c
index de8a180..016fea2 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -1,189 +1,258 @@
1/* ui-summary.c: functions for generating repo summary page 1/* ui-summary.c: functions for generating repo summary page
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11static int header; 11static int header;
12 12
13static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1, 13static int cmp_age(int age1, int age2)
14 int flags, void *cb_data) 14{
15 if (age1 != 0 && age2 != 0)
16 return age2 - age1;
17
18 if (age1 == 0 && age2 == 0)
19 return 0;
20
21 if (age1 == 0)
22 return +1;
23
24 return -1;
25}
26
27static int cmp_ref_name(const void *a, const void *b)
28{
29 struct refinfo *r1 = *(struct refinfo **)a;
30 struct refinfo *r2 = *(struct refinfo **)b;
31
32 return strcmp(r1->refname, r2->refname);
33}
34
35static int cmp_branch_age(const void *a, const void *b)
36{
37 struct refinfo *r1 = *(struct refinfo **)a;
38 struct refinfo *r2 = *(struct refinfo **)b;
39
40 return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
41}
42
43static int cmp_tag_age(const void *a, const void *b)
44{
45 struct refinfo *r1 = *(struct refinfo **)a;
46 struct refinfo *r2 = *(struct refinfo **)b;
47
48 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date);
49}
50
51static void cgit_print_branch(struct refinfo *ref)
15{ 52{
16 struct commit *commit; 53 struct commit *commit;
17 struct commitinfo *info; 54 struct commitinfo *info;
18 char buf[256]; 55 char *name = (char *)ref->refname;
19 char *ref;
20 56
21 ref = xstrdup(refname); 57 commit = lookup_commit(ref->object->sha1);
22 strncpy(buf, refname, sizeof(buf));
23 commit = lookup_commit(sha1);
24 // object is not really parsed at this point, because of some fallout 58 // object is not really parsed at this point, because of some fallout
25 // from previous calls to git functions in cgit_print_log() 59 // from previous calls to git functions in cgit_print_log()
26 commit->object.parsed = 0; 60 commit->object.parsed = 0;
27 if (commit && !parse_commit(commit)){ 61 if (commit && !parse_commit(commit)){
28 info = cgit_parse_commit(commit); 62 info = cgit_parse_commit(commit);
29 html("<tr><td>"); 63 html("<tr><td>");
30 cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0); 64 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0);
31 html("</td><td>"); 65 html("</td><td>");
32 cgit_print_age(commit->date, -1, NULL); 66 cgit_print_age(commit->date, -1, NULL);
33 html("</td><td>"); 67 html("</td><td>");
34 html_txt(info->author); 68 html_txt(info->author);
35 html("</td><td>"); 69 html("</td><td>");
36 cgit_commit_link(info->subject, NULL, NULL, ref, NULL); 70 cgit_commit_link(info->subject, NULL, NULL, name, NULL);
37 html("</td></tr>\n"); 71 html("</td></tr>\n");
38 cgit_free_commitinfo(info); 72 cgit_free_commitinfo(info);
39 } else { 73 } else {
40 html("<tr><td>"); 74 html("<tr><td>");
41 html_txt(buf); 75 html_txt(name);
42 html("</td><td colspan='3'>"); 76 html("</td><td colspan='3'>");
43 htmlf("*** bad ref %s ***", sha1_to_hex(sha1)); 77 htmlf("*** bad ref %s ***", sha1_to_hex(ref->object->sha1));
44 html("</td></tr>\n"); 78 html("</td></tr>\n");
45 } 79 }
46 free(ref);
47 return 0;
48} 80}
49 81
50static void print_tag_header() 82static void print_tag_header()
51{ 83{
52 html("<tr class='nohover'><th class='left'>Tag</th>" 84 html("<tr class='nohover'><th class='left'>Tag</th>"
53 "<th class='left'>Age</th>" 85 "<th class='left'>Age</th>"
54 "<th class='left'>Author</th>" 86 "<th class='left'>Author</th>"
55 "<th class='left'>Reference</th></tr>\n"); 87 "<th class='left'>Reference</th></tr>\n");
56 header = 1; 88 header = 1;
57} 89}
58 90
59static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1, 91static int print_tag(struct refinfo *ref)
60 int flags, void *cb_data)
61{ 92{
62 struct tag *tag; 93 struct tag *tag;
63 struct taginfo *info; 94 struct taginfo *info;
64 struct object *obj; 95 char *url, *name = (char *)ref->refname;
65 char buf[256], *url;
66 96
67 strncpy(buf, refname, sizeof(buf)); 97 if (ref->object->type == OBJ_TAG) {
68 obj = parse_object(sha1); 98 tag = lookup_tag(ref->object->sha1);
69 if (!obj)
70 return 1;
71 if (obj->type == OBJ_TAG) {
72 tag = lookup_tag(sha1);
73 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 99 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
74 return 2; 100 return 2;
75 if (!header)
76 print_tag_header();
77 html("<tr><td>"); 101 html("<tr><td>");
78 url = cgit_pageurl(cgit_query_repo, "tag", 102 url = cgit_pageurl(cgit_query_repo, "tag",
79 fmt("id=%s", refname)); 103 fmt("id=%s", name));
80 html_link_open(url, NULL, NULL); 104 html_link_open(url, NULL, NULL);
81 html_txt(buf); 105 html_txt(name);
82 html_link_close(); 106 html_link_close();
83 html("</td><td>"); 107 html("</td><td>");
84 if (info->tagger_date > 0) 108 if (info->tagger_date > 0)
85 cgit_print_age(info->tagger_date, -1, NULL); 109 cgit_print_age(info->tagger_date, -1, NULL);
86 html("</td><td>"); 110 html("</td><td>");
87 if (info->tagger) 111 if (info->tagger)
88 html(info->tagger); 112 html(info->tagger);
89 html("</td><td>"); 113 html("</td><td>");
90 cgit_object_link(tag->tagged); 114 cgit_object_link(tag->tagged);
91 html("</td></tr>\n"); 115 html("</td></tr>\n");
92 } else { 116 } else {
93 if (!header) 117 if (!header)
94 print_tag_header(); 118 print_tag_header();
95 html("<tr><td>"); 119 html("<tr><td>");
96 html_txt(buf); 120 html_txt(name);
97 html("</td><td colspan='2'/><td>"); 121 html("</td><td colspan='2'/><td>");
98 cgit_object_link(obj); 122 cgit_object_link(ref->object);
99 html("</td></tr>\n"); 123 html("</td></tr>\n");
100 } 124 }
101 return 0; 125 return 0;
102} 126}
103 127
104static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1, 128static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1,
105 int flags, void *cb_data) 129 int flags, void *cb_data)
106{ 130{
107 struct tag *tag; 131 struct tag *tag;
108 struct taginfo *info; 132 struct taginfo *info;
109 struct object *obj; 133 struct object *obj;
110 char buf[256], *url; 134 char buf[256], *url;
111 unsigned char fileid[20]; 135 unsigned char fileid[20];
112 136
113 if (prefixcmp(refname, "refs/archives")) 137 if (prefixcmp(refname, "refs/archives"))
114 return 0; 138 return 0;
115 strncpy(buf, refname+14, sizeof(buf)); 139 strncpy(buf, refname+14, sizeof(buf));
116 obj = parse_object(sha1); 140 obj = parse_object(sha1);
117 if (!obj) 141 if (!obj)
118 return 1; 142 return 1;
119 if (obj->type == OBJ_TAG) { 143 if (obj->type == OBJ_TAG) {
120 tag = lookup_tag(sha1); 144 tag = lookup_tag(sha1);
121 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 145 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
122 return 0; 146 return 0;
123 hashcpy(fileid, tag->tagged->sha1); 147 hashcpy(fileid, tag->tagged->sha1);
124 } else if (obj->type != OBJ_BLOB) { 148 } else if (obj->type != OBJ_BLOB) {
125 return 0; 149 return 0;
126 } else { 150 } else {
127 hashcpy(fileid, sha1); 151 hashcpy(fileid, sha1);
128 } 152 }
129 if (!header) { 153 if (!header) {
130 html("<table id='downloads'>"); 154 html("<table id='downloads'>");
131 html("<tr><th>Downloads</th></tr>"); 155 html("<tr><th>Downloads</th></tr>");
132 header = 1; 156 header = 1;
133 } 157 }
134 html("<tr><td>"); 158 html("<tr><td>");
135 url = cgit_pageurl(cgit_query_repo, "blob", 159 url = cgit_pageurl(cgit_query_repo, "blob",
136 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 160 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
137 buf)); 161 buf));
138 html_link_open(url, NULL, NULL); 162 html_link_open(url, NULL, NULL);
139 html_txt(buf); 163 html_txt(buf);
140 html_link_close(); 164 html_link_close();
141 html("</td></tr>"); 165 html("</td></tr>");
142 return 0; 166 return 0;
143} 167}
144 168
145static void cgit_print_branches() 169static void print_refs_link(char *path)
146{ 170{
171 html("<tr class='nohover'><td colspan='4'>");
172 cgit_refs_link("[...]", NULL, NULL, cgit_query_head, NULL, path);
173 html("</td></tr>");
174}
175
176void cgit_print_branches(int maxcount)
177{
178 struct reflist list;
179 int i;
180
147 html("<tr class='nohover'><th class='left'>Branch</th>" 181 html("<tr class='nohover'><th class='left'>Branch</th>"
148 "<th class='left'>Idle</th>" 182 "<th class='left'>Idle</th>"
149 "<th class='left'>Author</th>" 183 "<th class='left'>Author</th>"
150 "<th class='left'>Head commit</th></tr>\n"); 184 "<th class='left'>Head commit</th></tr>\n");
151 for_each_branch_ref(cgit_print_branch_cb, NULL); 185
186 list.refs = NULL;
187 list.alloc = list.count = 0;
188 for_each_branch_ref(cgit_refs_cb, &list);
189
190 if (maxcount == 0 || maxcount > list.count)
191 maxcount = list.count;
192
193 if (maxcount < list.count) {
194 qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
195 qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
196 }
197
198 for(i=0; i<maxcount; i++)
199 cgit_print_branch(list.refs[i]);
200
201 if (maxcount < list.count)
202 print_refs_link("heads");
152} 203}
153 204
154static void cgit_print_tags() 205void cgit_print_tags(int maxcount)
155{ 206{
207 struct reflist list;
208 int i;
209
156 header = 0; 210 header = 0;
157 for_each_tag_ref(cgit_print_tag_cb, NULL); 211 list.refs = NULL;
212 list.alloc = list.count = 0;
213 for_each_tag_ref(cgit_refs_cb, &list);
214 if (list.count == 0)
215 return;
216 qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
217 if (!maxcount)
218 maxcount = list.count;
219 else if (maxcount > list.count)
220 maxcount = list.count;
221 print_tag_header();
222 for(i=0; i<maxcount; i++)
223 print_tag(list.refs[i]);
224
225 if (maxcount < list.count)
226 print_refs_link("tags");
158} 227}
159 228
160static void cgit_print_archives() 229static void cgit_print_archives()
161{ 230{
162 header = 0; 231 header = 0;
163 for_each_ref(cgit_print_archive_cb, NULL); 232 for_each_ref(cgit_print_archive_cb, NULL);
164 if (header) 233 if (header)
165 html("</table>"); 234 html("</table>");
166} 235}
167 236
168void cgit_print_summary() 237void cgit_print_summary()
169{ 238{
170 html("<div id='summary'>"); 239 html("<div id='summary'>");
171 cgit_print_archives(); 240 cgit_print_archives();
172 html("<h2>"); 241 html("<h2>");
173 html_txt(cgit_repo->name); 242 html_txt(cgit_repo->name);
174 html(" - "); 243 html(" - ");
175 html_txt(cgit_repo->desc); 244 html_txt(cgit_repo->desc);
176 html("</h2>"); 245 html("</h2>");
177 if (cgit_repo->readme) 246 if (cgit_repo->readme)
178 html_include(cgit_repo->readme); 247 html_include(cgit_repo->readme);
179 html("</div>"); 248 html("</div>");
180 if (cgit_summary_log > 0) 249 if (cgit_summary_log > 0)
181 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, 0); 250 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, 0);
182 html("<table class='list nowrap'>"); 251 html("<table class='list nowrap'>");
183 if (cgit_summary_log > 0) 252 if (cgit_summary_log > 0)
184 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 253 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
185 cgit_print_branches(); 254 cgit_print_branches(cgit_summary_branches);
186 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 255 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
187 cgit_print_tags(); 256 cgit_print_tags(cgit_summary_tags);
188 html("</table>"); 257 html("</table>");
189} 258}