summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c5
-rw-r--r--cgit.css11
-rw-r--r--cgit.h31
-rw-r--r--cgitrc17
-rwxr-xr-xgen-version.sh2
-rw-r--r--shared.c64
-rw-r--r--ui-commit.c4
-rw-r--r--ui-diff.c45
-rw-r--r--ui-refs.c30
-rw-r--r--ui-shared.c6
-rw-r--r--ui-summary.c172
12 files changed, 299 insertions, 90 deletions
diff --git a/Makefile b/Makefile
index 8e3da72..36b5ff6 100644
--- a/Makefile
+++ b/Makefile
@@ -13,13 +13,13 @@ GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
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
diff --git a/cgit.c b/cgit.c
index c86d290..cc18ed4 100644
--- a/cgit.c
+++ b/cgit.c
@@ -100,17 +100,20 @@ static void cgit_print_repo_page(struct cacheitem *item)
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); 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}
diff --git a/cgit.css b/cgit.css
index 65ff5c3..b8c3d81 100644
--- a/cgit.css
+++ b/cgit.css
@@ -269,16 +269,12 @@ table.diffstat {
269 border-collapse: collapse; 269 border-collapse: collapse;
270 width: 100%; 270 width: 100%;
271 border: solid 1px #aaa; 271 border: solid 1px #aaa;
272 background-color: #eee; 272 background-color: #eee;
273} 273}
274 274
275table.diffstat tr:hover {
276 background-color: #ccc;
277}
278
279table.diffstat th { 275table.diffstat th {
280 font-weight: normal; 276 font-weight: normal;
281 text-align: left; 277 text-align: left;
282 text-decoration: underline; 278 text-decoration: underline;
283 padding: 0.1em 1em 0.1em 0.1em; 279 padding: 0.1em 1em 0.1em 0.1em;
284 font-size: 100%; 280 font-size: 100%;
@@ -336,20 +332,25 @@ table.diffstat td.graph td.rem {
336 332
337div.diffstat-summary { 333div.diffstat-summary {
338 color: #888; 334 color: #888;
339 padding-top: 0.5em; 335 padding-top: 0.5em;
340} 336}
341 337
338table.diff {
339 width: 100%;
340}
341
342table.diff td { 342table.diff td {
343 font-family: monospace; 343 font-family: monospace;
344 white-space: pre; 344 white-space: pre;
345} 345}
346 346
347table.diff td div.head { 347table.diff td div.head {
348 font-weight: bold; 348 font-weight: bold;
349 padding-top: 1em; 349 margin-top: 1em;
350 background-color: #eee;
350} 351}
351 352
352table.diff td div.hunk { 353table.diff td div.hunk {
353 color: #009; 354 color: #009;
354} 355}
355 356
diff --git a/cgit.h b/cgit.h
index e3d9cb8..f8f0316 100644
--- a/cgit.h
+++ b/cgit.h
@@ -25,12 +25,13 @@
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"
@@ -95,12 +96,27 @@ struct 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
@@ -125,12 +141,14 @@ extern 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;
@@ -159,21 +177,25 @@ extern 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); 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);
@@ -211,12 +233,14 @@ extern const char *cgit_repobasename(const char *reponame);
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);
@@ -227,21 +251,24 @@ extern 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); 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);
diff --git a/cgitrc b/cgitrc
index 34ea116..2b09e01 100644
--- a/cgitrc
+++ b/cgitrc
@@ -27,23 +27,40 @@
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
54## Git detects renames, but with a limit on the number of files to
55## consider. This option can be used to specify another limit (or -1 to
56## use the default limit).
57##
58#renamelimit=-1
59
60
44## 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
45## 62##
46## http://localhost/git/repo/log/?h=branch 63## http://localhost/git/repo/log/?h=branch
47## 64##
48## instead of 65## instead of
49## 66##
diff --git a/gen-version.sh b/gen-version.sh
index 739c83e..3a08015 100755
--- a/gen-version.sh
+++ b/gen-version.sh
@@ -3,13 +3,13 @@
3# Get version-info specified in Makefile 3# Get version-info specified in Makefile
4V=$1 4V=$1
5 5
6# Use `git describe` to get current version if we're inside a git repo 6# Use `git describe` to get current version if we're inside a git repo
7if test -d .git 7if test -d .git
8then 8then
9 V=$(git describe --abbrev=4 HEAD 2>/dev/null | sed -e 's/-/./g') 9 V=$(git describe --abbrev=4 HEAD 2>/dev/null)
10fi 10fi
11 11
12new="CGIT_VERSION = $V" 12new="CGIT_VERSION = $V"
13old=$(cat VERSION 2>/dev/null) 13old=$(cat VERSION 2>/dev/null)
14 14
15# Exit if VERSION is uptodate 15# Exit if VERSION is uptodate
diff --git a/shared.c b/shared.c
index 0fe513f..7eb2b0e 100644
--- a/shared.c
+++ b/shared.c
@@ -35,12 +35,15 @@ int 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;
43int cgit_renamelimit = -1;
41 44
42int cgit_max_msg_len = 60; 45int cgit_max_msg_len = 60;
43int cgit_max_repodesc_len = 60; 46int cgit_max_repodesc_len = 60;
44int cgit_max_commit_count = 50; 47int cgit_max_commit_count = 50;
45 48
46int cgit_query_has_symref = 0; 49int cgit_query_has_symref = 0;
@@ -60,13 +63,13 @@ int cgit_query_ofs = 0;
60int htmlfd = 0; 63int htmlfd = 0;
61 64
62 65
63int cgit_get_cmd_index(const char *cmd) 66int cgit_get_cmd_index(const char *cmd)
64{ 67{
65 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 68 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
66 "snapshot", "tag", NULL}; 69 "snapshot", "tag", "refs", NULL};
67 int i; 70 int i;
68 71
69 for(i = 0; cmds[i]; i++) 72 for(i = 0; cmds[i]; i++)
70 if (!strcmp(cmd, cmds[i])) 73 if (!strcmp(cmd, cmds[i]))
71 return i + 1; 74 return i + 1;
72 return 0; 75 return 0;
@@ -177,14 +180,20 @@ void cgit_global_config_cb(const char *name, const char *value)
177 else if (!strcmp(name, "max-repodesc-length")) 180 else if (!strcmp(name, "max-repodesc-length"))
178 cgit_max_repodesc_len = atoi(value); 181 cgit_max_repodesc_len = atoi(value);
179 else if (!strcmp(name, "max-commit-count")) 182 else if (!strcmp(name, "max-commit-count"))
180 cgit_max_commit_count = atoi(value); 183 cgit_max_commit_count = atoi(value);
181 else if (!strcmp(name, "summary-log")) 184 else if (!strcmp(name, "summary-log"))
182 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);
183 else if (!strcmp(name, "agefile")) 190 else if (!strcmp(name, "agefile"))
184 cgit_agefile = xstrdup(value); 191 cgit_agefile = xstrdup(value);
192 else if (!strcmp(name, "renamelimit"))
193 cgit_renamelimit = atoi(value);
185 else if (!strcmp(name, "repo.group")) 194 else if (!strcmp(name, "repo.group"))
186 cgit_repo_group = xstrdup(value); 195 cgit_repo_group = xstrdup(value);
187 else if (!strcmp(name, "repo.url")) 196 else if (!strcmp(name, "repo.url"))
188 cgit_repo = add_repo(value); 197 cgit_repo = add_repo(value);
189 else if (!strcmp(name, "repo.name")) 198 else if (!strcmp(name, "repo.name"))
190 cgit_repo->name = xstrdup(value); 199 cgit_repo->name = xstrdup(value);
@@ -285,12 +294,53 @@ char *trim_end(const char *str, char c)
285 t[len] = '\0'; 294 t[len] = '\0';
286 s = xstrdup(t); 295 s = xstrdup(t);
287 t[len] = c; 296 t[len] = c;
288 return s; 297 return s;
289} 298}
290 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
291void cgit_diff_tree_cb(struct diff_queue_struct *q, 341void cgit_diff_tree_cb(struct diff_queue_struct *q,
292 struct diff_options *options, void *data) 342 struct diff_options *options, void *data)
293{ 343{
294 int i; 344 int i;
295 345
296 for (i = 0; i < q->nr; i++) { 346 for (i = 0; i < q->nr; i++) {
@@ -380,23 +430,31 @@ int cgit_diff_files(const unsigned char *old_sha1,
380 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 430 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
381 return 0; 431 return 0;
382} 432}
383 433
384void cgit_diff_tree(const unsigned char *old_sha1, 434void cgit_diff_tree(const unsigned char *old_sha1,
385 const unsigned char *new_sha1, 435 const unsigned char *new_sha1,
386 filepair_fn fn) 436 filepair_fn fn, const char *prefix)
387{ 437{
388 struct diff_options opt; 438 struct diff_options opt;
389 int ret; 439 int ret;
440 int prefixlen;
390 441
391 diff_setup(&opt); 442 diff_setup(&opt);
392 opt.output_format = DIFF_FORMAT_CALLBACK; 443 opt.output_format = DIFF_FORMAT_CALLBACK;
393 opt.detect_rename = 1; 444 opt.detect_rename = 1;
445 opt.rename_limit = cgit_renamelimit;
394 opt.recursive = 1; 446 opt.recursive = 1;
395 opt.format_callback = cgit_diff_tree_cb; 447 opt.format_callback = cgit_diff_tree_cb;
396 opt.format_callback_data = fn; 448 opt.format_callback_data = fn;
449 if (prefix) {
450 opt.nr_paths = 1;
451 opt.paths = &prefix;
452 prefixlen = strlen(prefix);
453 opt.pathlens = &prefixlen;
454 }
397 diff_setup_done(&opt); 455 diff_setup_done(&opt);
398 456
399 if (old_sha1 && !is_null_sha1(old_sha1)) 457 if (old_sha1 && !is_null_sha1(old_sha1))
400 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 458 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
401 else 459 else
402 ret = diff_root_tree_sha1(new_sha1, "", &opt); 460 ret = diff_root_tree_sha1(new_sha1, "", &opt);
@@ -407,8 +465,8 @@ void cgit_diff_tree(const unsigned char *old_sha1,
407void cgit_diff_commit(struct commit *commit, filepair_fn fn) 465void cgit_diff_commit(struct commit *commit, filepair_fn fn)
408{ 466{
409 unsigned char *old_sha1 = NULL; 467 unsigned char *old_sha1 = NULL;
410 468
411 if (commit->parents) 469 if (commit->parents)
412 old_sha1 = commit->parents->item->object.sha1; 470 old_sha1 = commit->parents->item->object.sha1;
413 cgit_diff_tree(old_sha1, commit->object.sha1, fn); 471 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
414} 472}
diff --git a/ui-commit.c b/ui-commit.c
index 90e09ed..4ac8955 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -72,14 +72,14 @@ void print_fileinfo(struct fileinfo *info)
72 !is_null_sha1(info->new_sha1)) { 72 !is_null_sha1(info->new_sha1)) {
73 html("<span class='modechange'>["); 73 html("<span class='modechange'>[");
74 html_filemode(info->old_mode); 74 html_filemode(info->old_mode);
75 html("]</span>"); 75 html("]</span>");
76 } 76 }
77 htmlf("</td><td class='%s'>", class); 77 htmlf("</td><td class='%s'>", class);
78 cgit_tree_link(info->new_path, NULL, NULL, cgit_query_head, curr_rev, 78 cgit_diff_link(info->new_path, NULL, NULL, cgit_query_head, curr_rev,
79 info->new_path); 79 NULL, info->new_path);
80 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) 80 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
81 htmlf(" (%s from %s)", 81 htmlf(" (%s from %s)",
82 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed", 82 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed",
83 info->old_path); 83 info->old_path);
84 html("</td><td class='right'>"); 84 html("</td><td class='right'>");
85 htmlf("%d", info->added + info->removed); 85 htmlf("%d", info->added + info->removed);
diff --git a/ui-diff.c b/ui-diff.c
index 0be845f..ac9a3fa 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -6,12 +6,15 @@
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
11 11
12unsigned char old_rev_sha1[20];
13unsigned char new_rev_sha1[20];
14
12/* 15/*
13 * print a single line returned from xdiff 16 * print a single line returned from xdiff
14 */ 17 */
15static void print_line(char *line, int len) 18static void print_line(char *line, int len)
16{ 19{
17 char *class = "ctx"; 20 char *class = "ctx";
@@ -64,15 +67,23 @@ static void header(unsigned char *sha1, char *path1, int mode1,
64 if (mode1 != 0 && mode2 != 0) { 67 if (mode1 != 0 && mode2 != 0) {
65 htmlf(" %.6o", mode1); 68 htmlf(" %.6o", mode1);
66 if (mode2 != mode1) 69 if (mode2 != mode1)
67 htmlf("..%.6o", mode2); 70 htmlf("..%.6o", mode2);
68 } 71 }
69 html("<br/>--- a/"); 72 html("<br/>--- a/");
70 html_txt(path1); 73 if (mode1 != 0)
74 cgit_tree_link(path1, NULL, NULL, cgit_query_head,
75 sha1_to_hex(old_rev_sha1), path1);
76 else
77 html_txt(path1);
71 html("<br/>+++ b/"); 78 html("<br/>+++ b/");
72 html_txt(path2); 79 if (mode2 != 0)
80 cgit_tree_link(path2, NULL, NULL, cgit_query_head,
81 sha1_to_hex(new_rev_sha1), path2);
82 else
83 html_txt(path2);
73 } 84 }
74 html("</div>"); 85 html("</div>");
75} 86}
76 87
77static void filepair_cb(struct diff_filepair *pair) 88static void filepair_cb(struct diff_filepair *pair)
78{ 89{
@@ -86,55 +97,53 @@ static void filepair_cb(struct diff_filepair *pair)
86 return; 97 return;
87 } 98 }
88 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line)) 99 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line))
89 cgit_print_error("Error running diff"); 100 cgit_print_error("Error running diff");
90} 101}
91 102
92void cgit_print_diff(const char *new_rev, const char *old_rev) 103void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix)
93{ 104{
94 unsigned char sha1[20], sha2[20];
95 enum object_type type; 105 enum object_type type;
96 unsigned long size; 106 unsigned long size;
97 struct commit *commit, *commit2; 107 struct commit *commit, *commit2;
98 108
99 if (!new_rev) 109 if (!new_rev)
100 new_rev = cgit_query_head; 110 new_rev = cgit_query_head;
101 get_sha1(new_rev, sha1); 111 get_sha1(new_rev, new_rev_sha1);
102 type = sha1_object_info(sha1, &size); 112 type = sha1_object_info(new_rev_sha1, &size);
103 if (type == OBJ_BAD) { 113 if (type == OBJ_BAD) {
104 cgit_print_error(fmt("Bad object name: %s", new_rev)); 114 cgit_print_error(fmt("Bad object name: %s", new_rev));
105 return; 115 return;
106 } 116 }
107 if (type != OBJ_COMMIT) { 117 if (type != OBJ_COMMIT) {
108 cgit_print_error(fmt("Unhandled object type: %s", 118 cgit_print_error(fmt("Unhandled object type: %s",
109 typename(type))); 119 typename(type)));
110 return; 120 return;
111 } 121 }
112 122
113 commit = lookup_commit_reference(sha1); 123 commit = lookup_commit_reference(new_rev_sha1);
114 if (!commit || parse_commit(commit)) 124 if (!commit || parse_commit(commit))
115 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(sha1))); 125 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1)));
116 126
117 if (old_rev) 127 if (old_rev)
118 get_sha1(old_rev, sha2); 128 get_sha1(old_rev, old_rev_sha1);
119 else if (commit->parents && commit->parents->item) 129 else if (commit->parents && commit->parents->item)
120 hashcpy(sha2, commit->parents->item->object.sha1); 130 hashcpy(old_rev_sha1, commit->parents->item->object.sha1);
121 else 131 else
122 hashclr(sha2); 132 hashclr(old_rev_sha1);
123 133
124 if (!is_null_sha1(sha2)) { 134 if (!is_null_sha1(old_rev_sha1)) {
125 type = sha1_object_info(sha2, &size); 135 type = sha1_object_info(old_rev_sha1, &size);
126 if (type == OBJ_BAD) { 136 if (type == OBJ_BAD) {
127 cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(sha2))); 137 cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(old_rev_sha1)));
128 return; 138 return;
129 } 139 }
130 commit2 = lookup_commit_reference(sha2); 140 commit2 = lookup_commit_reference(old_rev_sha1);
131 if (!commit2 || parse_commit(commit2)) 141 if (!commit2 || parse_commit(commit2))
132 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(sha2))); 142 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(old_rev_sha1)));
133 } 143 }
134
135 html("<table class='diff'>"); 144 html("<table class='diff'>");
136 html("<tr><td>"); 145 html("<tr><td>");
137 cgit_diff_tree(sha2, sha1, filepair_cb); 146 cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix);
138 html("</td></tr>"); 147 html("</td></tr>");
139 html("</table>"); 148 html("</table>");
140} 149}
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
@@ -224,12 +224,18 @@ void cgit_commit_link(char *name, char *title, char *class, char *head,
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
diff --git a/ui-summary.c b/ui-summary.c
index de8a180..178e959 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -7,81 +7,94 @@
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)
15{ 14{
16 struct commit *commit; 15 if (age1 != 0 && age2 != 0)
17 struct commitinfo *info; 16 return age2 - age1;
18 char buf[256]; 17
19 char *ref; 18 if (age1 == 0 && age2 == 0)
20 19 return 0;
21 ref = xstrdup(refname); 20
22 strncpy(buf, refname, sizeof(buf)); 21 if (age1 == 0)
23 commit = lookup_commit(sha1); 22 return +1;
24 // object is not really parsed at this point, because of some fallout 23
25 // from previous calls to git functions in cgit_print_log() 24 return -1;
26 commit->object.parsed = 0; 25}
27 if (commit && !parse_commit(commit)){ 26
28 info = cgit_parse_commit(commit); 27static int cmp_ref_name(const void *a, const void *b)
29 html("<tr><td>"); 28{
30 cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0); 29 struct refinfo *r1 = *(struct refinfo **)a;
31 html("</td><td>"); 30 struct refinfo *r2 = *(struct refinfo **)b;
32 cgit_print_age(commit->date, -1, NULL); 31
33 html("</td><td>"); 32 return strcmp(r1->refname, r2->refname);
34 html_txt(info->author); 33}
35 html("</td><td>"); 34
36 cgit_commit_link(info->subject, NULL, NULL, ref, NULL); 35static int cmp_branch_age(const void *a, const void *b)
37 html("</td></tr>\n"); 36{
38 cgit_free_commitinfo(info); 37 struct refinfo *r1 = *(struct refinfo **)a;
39 } else { 38 struct refinfo *r2 = *(struct refinfo **)b;
40 html("<tr><td>"); 39
41 html_txt(buf); 40 return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
42 html("</td><td colspan='3'>"); 41}
43 htmlf("*** bad ref %s ***", sha1_to_hex(sha1)); 42
44 html("</td></tr>\n"); 43static int cmp_tag_age(const void *a, const void *b)
45 } 44{
46 free(ref); 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 int print_branch(struct refinfo *ref)
52{
53 struct commitinfo *info = ref->commit;
54 char *name = (char *)ref->refname;
55
56 if (!info)
57 return 1;
58 html("<tr><td>");
59 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0);
60 html("</td><td>");
61 cgit_print_age(info->commit->date, -1, NULL);
62 html("</td><td>");
63 html_txt(info->author);
64 html("</td><td>");
65 cgit_commit_link(info->subject, NULL, NULL, name, NULL);
66 html("</td></tr>\n");
47 return 0; 67 return 0;
48} 68}
49 69
50static void print_tag_header() 70static void print_tag_header()
51{ 71{
52 html("<tr class='nohover'><th class='left'>Tag</th>" 72 html("<tr class='nohover'><th class='left'>Tag</th>"
53 "<th class='left'>Age</th>" 73 "<th class='left'>Age</th>"
54 "<th class='left'>Author</th>" 74 "<th class='left'>Author</th>"
55 "<th class='left'>Reference</th></tr>\n"); 75 "<th class='left'>Reference</th></tr>\n");
56 header = 1; 76 header = 1;
57} 77}
58 78
59static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1, 79static int print_tag(struct refinfo *ref)
60 int flags, void *cb_data)
61{ 80{
62 struct tag *tag; 81 struct tag *tag;
63 struct taginfo *info; 82 struct taginfo *info;
64 struct object *obj; 83 char *url, *name = (char *)ref->refname;
65 char buf[256], *url;
66 84
67 strncpy(buf, refname, sizeof(buf)); 85 if (ref->object->type == OBJ_TAG) {
68 obj = parse_object(sha1); 86 tag = (struct tag *)ref->object;
69 if (!obj) 87 info = ref->tag;
70 return 1; 88 if (!tag || !info)
71 if (obj->type == OBJ_TAG) { 89 return 1;
72 tag = lookup_tag(sha1);
73 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
74 return 2;
75 if (!header)
76 print_tag_header();
77 html("<tr><td>"); 90 html("<tr><td>");
78 url = cgit_pageurl(cgit_query_repo, "tag", 91 url = cgit_pageurl(cgit_query_repo, "tag",
79 fmt("id=%s", refname)); 92 fmt("id=%s", name));
80 html_link_open(url, NULL, NULL); 93 html_link_open(url, NULL, NULL);
81 html_txt(buf); 94 html_txt(name);
82 html_link_close(); 95 html_link_close();
83 html("</td><td>"); 96 html("</td><td>");
84 if (info->tagger_date > 0) 97 if (info->tagger_date > 0)
85 cgit_print_age(info->tagger_date, -1, NULL); 98 cgit_print_age(info->tagger_date, -1, NULL);
86 html("</td><td>"); 99 html("</td><td>");
87 if (info->tagger) 100 if (info->tagger)
@@ -90,15 +103,15 @@ static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
90 cgit_object_link(tag->tagged); 103 cgit_object_link(tag->tagged);
91 html("</td></tr>\n"); 104 html("</td></tr>\n");
92 } else { 105 } else {
93 if (!header) 106 if (!header)
94 print_tag_header(); 107 print_tag_header();
95 html("<tr><td>"); 108 html("<tr><td>");
96 html_txt(buf); 109 html_txt(name);
97 html("</td><td colspan='2'/><td>"); 110 html("</td><td colspan='2'/><td>");
98 cgit_object_link(obj); 111 cgit_object_link(ref->object);
99 html("</td></tr>\n"); 112 html("</td></tr>\n");
100 } 113 }
101 return 0; 114 return 0;
102} 115}
103 116
104static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1, 117static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1,
@@ -139,25 +152,70 @@ static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1,
139 html_txt(buf); 152 html_txt(buf);
140 html_link_close(); 153 html_link_close();
141 html("</td></tr>"); 154 html("</td></tr>");
142 return 0; 155 return 0;
143} 156}
144 157
145static void cgit_print_branches() 158static void print_refs_link(char *path)
146{ 159{
160 html("<tr class='nohover'><td colspan='4'>");
161 cgit_refs_link("[...]", NULL, NULL, cgit_query_head, NULL, path);
162 html("</td></tr>");
163}
164
165void cgit_print_branches(int maxcount)
166{
167 struct reflist list;
168 int i;
169
147 html("<tr class='nohover'><th class='left'>Branch</th>" 170 html("<tr class='nohover'><th class='left'>Branch</th>"
148 "<th class='left'>Idle</th>" 171 "<th class='left'>Idle</th>"
149 "<th class='left'>Author</th>" 172 "<th class='left'>Author</th>"
150 "<th class='left'>Head commit</th></tr>\n"); 173 "<th class='left'>Head commit</th></tr>\n");
151 for_each_branch_ref(cgit_print_branch_cb, NULL); 174
175 list.refs = NULL;
176 list.alloc = list.count = 0;
177 for_each_branch_ref(cgit_refs_cb, &list);
178
179 if (maxcount == 0 || maxcount > list.count)
180 maxcount = list.count;
181
182 if (maxcount < list.count) {
183 qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
184 qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
185 }
186
187 for(i=0; i<maxcount; i++)
188 print_branch(list.refs[i]);
189
190 if (maxcount < list.count)
191 print_refs_link("heads");
152} 192}
153 193
154static void cgit_print_tags() 194void cgit_print_tags(int maxcount)
155{ 195{
196 struct reflist list;
197 int i;
198
156 header = 0; 199 header = 0;
157 for_each_tag_ref(cgit_print_tag_cb, NULL); 200 list.refs = NULL;
201 list.alloc = list.count = 0;
202 for_each_tag_ref(cgit_refs_cb, &list);
203 if (list.count == 0)
204 return;
205 qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
206 if (!maxcount)
207 maxcount = list.count;
208 else if (maxcount > list.count)
209 maxcount = list.count;
210 print_tag_header();
211 for(i=0; i<maxcount; i++)
212 print_tag(list.refs[i]);
213
214 if (maxcount < list.count)
215 print_refs_link("tags");
158} 216}
159 217
160static void cgit_print_archives() 218static void cgit_print_archives()
161{ 219{
162 header = 0; 220 header = 0;
163 for_each_ref(cgit_print_archive_cb, NULL); 221 for_each_ref(cgit_print_archive_cb, NULL);
@@ -179,11 +237,11 @@ void cgit_print_summary()
179 html("</div>"); 237 html("</div>");
180 if (cgit_summary_log > 0) 238 if (cgit_summary_log > 0)
181 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, 0); 239 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, 0);
182 html("<table class='list nowrap'>"); 240 html("<table class='list nowrap'>");
183 if (cgit_summary_log > 0) 241 if (cgit_summary_log > 0)
184 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 242 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
185 cgit_print_branches(); 243 cgit_print_branches(cgit_summary_branches);
186 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 244 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
187 cgit_print_tags(); 245 cgit_print_tags(cgit_summary_tags);
188 html("</table>"); 246 html("</table>");
189} 247}