aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules8
-rw-r--r--Makefile102
-rw-r--r--cgit.c36
-rw-r--r--cgit.css75
-rw-r--r--cgit.h47
-rw-r--r--cgitrc9
-rwxr-xr-xgen-version.sh20
m---------git0
-rw-r--r--parsing.c2
-rw-r--r--shared.c45
-rwxr-xr-xsubmodules.sh181
-rw-r--r--ui-commit.c78
-rw-r--r--ui-diff.c66
-rw-r--r--ui-log.c26
-rw-r--r--ui-repolist.c66
-rw-r--r--ui-shared.c234
-rw-r--r--ui-snapshot.c156
-rw-r--r--ui-summary.c49
-rw-r--r--ui-tag.c74
-rw-r--r--ui-tree.c213
-rw-r--r--ui-view.c55
22 files changed, 929 insertions, 614 deletions
diff --git a/.gitignore b/.gitignore
index c4c9ac3..5664962 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
1# Files I don't care to see in git-status/commit 1# Files I don't care to see in git-status/commit
2cgit 2cgit
3VERSION
3*.o 4*.o
4*~ 5*~
diff --git a/.gitmodules b/.gitmodules
index 51dd1ef..1daea94 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,5 +1,3 @@
1# This file maps a submodule path to an url from where the submodule 1[submodule "git"]
2# can be obtained. The script "submodules.sh" finds the url in this file 2 url = git://git.kernel.org/pub/scm/git/git.git
3# when invoked with -i to clone the submodules. 3 path = git
4
5git git://git.kernel.org/pub/scm/git/git.git
diff --git a/Makefile b/Makefile
index 57f80f8..fcbe3e4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,87 +1,71 @@
1CGIT_VERSION = 0.5 1CGIT_VERSION = v0.5
2
3prefix = /var/www/htdocs/cgit
4
5SHA1_HEADER = <openssl/sha.h>
6CACHE_ROOT = /var/cache/cgit
7CGIT_CONFIG = /etc/cgitrc
8CGIT_SCRIPT_NAME = cgit.cgi 2CGIT_SCRIPT_NAME = cgit.cgi
3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
4CGIT_CONFIG = /etc/cgitrc
5CACHE_ROOT = /var/cache/cgit
6SHA1_HEADER = <openssl/sha.h>
7GIT_VER = 1.5.2
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
15EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
16OBJECTS = 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 \
17 ui-summary.o ui-log.o ui-view.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 \
18 ui-snapshot.o ui-blob.o 19 ui-snapshot.o ui-blob.o ui-tag.o
20
21
22.PHONY: all git install clean distclean force-version get-git
19 23
20CFLAGS += -Wall 24all: cgit git
21 25
22ifdef DEBUG 26VERSION: force-version
23 CFLAGS += -g 27 @./gen-version.sh "$(CGIT_VERSION)"
24endif 28-include VERSION
25 29
26CFLAGS += -Igit 30
31CFLAGS += -g -Wall -Igit
27CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 32CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
28CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 33CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
29CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 34CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
30CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 35CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
31 36
32 37
33# 38cgit: cgit.c $(OBJECTS)
34# If make is run on a nongit platform, get the git sources as a tarball.
35#
36GITVER = $(shell git version 2>/dev/null || echo nogit)
37ifeq ($(GITVER),nogit)
38GITURL = http://www.kernel.org/pub/software/scm/git/git-1.5.2.tar.bz2
39INITGIT = test -e git/git.c || ((curl "$(GITURL)" | tar -xj) && mv git-1.5.2 git)
40else
41INITGIT = ./submodules.sh -i
42endif
43
44
45#
46# basic build rules
47#
48all: cgit
49
50cgit: cgit.c cgit.h $(OBJECTS)
51 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) 39 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
52 40
53$(OBJECTS): cgit.h git/libgit.a 41$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION
54 42
55git/libgit.a: 43git/xdiff/lib.a: | git
56 $(INITGIT)
57 $(MAKE) -C git
58 44
59# 45git/libgit.a: | git
60# phony targets
61#
62install: all clean-cache
63 mkdir -p $(prefix)
64 install cgit $(prefix)/$(CGIT_SCRIPT_NAME)
65 install cgit.css $(prefix)/cgit.css
66 46
67clean-cgit: 47git:
68 rm -f cgit *.o 48 cd git && $(MAKE) xdiff/lib.a
49 cd git && $(MAKE) libgit.a
69 50
70distclean-cgit: clean-cgit 51install: all
71 git clean -d -x 52 mkdir -p $(CGIT_SCRIPT_PATH)
72 53 install cgit $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
73clean-sub: 54 install cgit.css $(CGIT_SCRIPT_PATH)/cgit.css
74 $(MAKE) -C git clean
75
76distclean-sub: clean-sub
77 $(shell cd git && git clean -d -x)
78
79clean-cache:
80 rm -rf $(CACHE_ROOT)/* 55 rm -rf $(CACHE_ROOT)/*
81 56
82clean: clean-cgit clean-sub 57uninstall:
58 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
59 rm -f $(CGIT_SCRIPT_PATH)/cgit.css
60 rm -rf $(CACHE_ROOT)
61
62clean:
63 rm -f cgit VERSION *.o
64 cd git && $(MAKE) clean
83 65
84distclean: distclean-cgit distclean-sub 66distclean: clean
67 git clean -d -x
68 cd git && git clean -d -x
85 69
86.PHONY: all install clean clean-cgit clean-sub clean-cache \ 70get-git:
87 distclean distclean-cgit distclean-sub 71 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index 34e590e..c86d290 100644
--- a/cgit.c
+++ b/cgit.c
@@ -8,9 +8,6 @@
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11const char cgit_version[] = CGIT_VERSION;
12
13
14static int cgit_prepare_cache(struct cacheitem *item) 11static int cgit_prepare_cache(struct cacheitem *item)
15{ 12{
16 if (!cgit_repo && cgit_query_repo) { 13 if (!cgit_repo && cgit_query_repo) {
@@ -29,13 +26,15 @@ static int cgit_prepare_cache(struct cacheitem *item)
29 } 26 }
30 27
31 if (!cgit_cmd) { 28 if (!cgit_cmd) {
32 item->name = xstrdup(fmt("%s/%s/index.html", cgit_cache_root, 29 item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root,
33 cache_safe_filename(cgit_repo->url))); 30 cache_safe_filename(cgit_repo->url),
31 cache_safe_filename(cgit_querystring)));
34 item->ttl = cgit_cache_repo_ttl; 32 item->ttl = cgit_cache_repo_ttl;
35 } else { 33 } else {
36 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root, 34 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root,
37 cache_safe_filename(cgit_repo->url), cgit_query_page, 35 cache_safe_filename(cgit_repo->url),
38 cache_safe_filename(cgit_querystring))); 36 cgit_query_page,
37 cache_safe_filename(cgit_querystring)));
39 if (cgit_query_has_symref) 38 if (cgit_query_has_symref)
40 item->ttl = cgit_cache_dynamic_ttl; 39 item->ttl = cgit_cache_dynamic_ttl;
41 else if (cgit_query_has_sha1) 40 else if (cgit_query_has_sha1)
@@ -69,8 +68,10 @@ static void cgit_print_repo_page(struct cacheitem *item)
69 setenv("GIT_DIR", cgit_repo->path, 1); 68 setenv("GIT_DIR", cgit_repo->path, 1);
70 69
71 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { 70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
72 cgit_print_snapshot(item, cgit_query_sha1, "zip", 71 cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1,
73 cgit_repo->url, cgit_query_name); 72 cgit_repobasename(cgit_repo->url),
73 cgit_query_path,
74 cgit_repo->snapshots );
74 return; 75 return;
75 } 76 }
76 77
@@ -92,22 +93,21 @@ static void cgit_print_repo_page(struct cacheitem *item)
92 93
93 switch(cgit_cmd) { 94 switch(cgit_cmd) {
94 case CMD_LOG: 95 case CMD_LOG:
95 cgit_print_log(cgit_query_head, cgit_query_ofs, 96 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
96 cgit_max_commit_count, cgit_query_search, 97 cgit_max_commit_count, cgit_query_search,
97 cgit_query_path, 1); 98 cgit_query_path, 1);
98 break; 99 break;
99 case CMD_TREE: 100 case CMD_TREE:
100 cgit_print_tree(cgit_query_head, cgit_query_sha1, cgit_query_path); 101 cgit_print_tree(cgit_query_sha1, cgit_query_path);
101 break; 102 break;
102 case CMD_COMMIT: 103 case CMD_COMMIT:
103 cgit_print_commit(cgit_query_head); 104 cgit_print_commit(cgit_query_sha1);
104 break; 105 break;
105 case CMD_VIEW: 106 case CMD_TAG:
106 cgit_print_view(cgit_query_sha1, cgit_query_path); 107 cgit_print_tag(cgit_query_sha1);
107 break; 108 break;
108 case CMD_DIFF: 109 case CMD_DIFF:
109 cgit_print_diff(cgit_query_head, cgit_query_sha1, cgit_query_sha2, 110 cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
110 cgit_query_path);
111 break; 111 break;
112 default: 112 default:
113 cgit_print_error("Invalid request"); 113 cgit_print_error("Invalid request");
@@ -227,6 +227,7 @@ static void cgit_parse_args(int argc, const char **argv)
227int main(int argc, const char **argv) 227int main(int argc, const char **argv)
228{ 228{
229 struct cacheitem item; 229 struct cacheitem item;
230 const char *cgit_config_env = getenv("CGIT_CONFIG");
230 231
231 htmlfd = STDOUT_FILENO; 232 htmlfd = STDOUT_FILENO;
232 item.st.st_mtime = time(NULL); 233 item.st.st_mtime = time(NULL);
@@ -234,7 +235,8 @@ int main(int argc, const char **argv)
234 cgit_repolist.count = 0; 235 cgit_repolist.count = 0;
235 cgit_repolist.repos = NULL; 236 cgit_repolist.repos = NULL;
236 237
237 cgit_read_config(CGIT_CONFIG, cgit_global_config_cb); 238 cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
239 cgit_global_config_cb);
238 cgit_repo = NULL; 240 cgit_repo = NULL;
239 if (getenv("SCRIPT_NAME")) 241 if (getenv("SCRIPT_NAME"))
240 cgit_script_name = xstrdup(getenv("SCRIPT_NAME")); 242 cgit_script_name = xstrdup(getenv("SCRIPT_NAME"));
diff --git a/cgit.css b/cgit.css
index 8977533..54bbfcc 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,6 +1,7 @@
1body { 1body {
2 font-family: arial; 2 font-family: arial, sans-serif;
3 font-size: 11pt; 3 font-size: 11pt;
4 color: black;
4 background: white; 5 background: white;
5} 6}
6 7
@@ -94,6 +95,14 @@ td#header {
94 vertical-align: text-bottom; 95 vertical-align: text-bottom;
95} 96}
96 97
98td#header a {
99 color: #666;
100}
101
102td#header a:hoved {
103 text-decoration: underline;
104}
105
97td#logo { 106td#logo {
98 text-align: right; 107 text-align: right;
99 vertical-align: middle; 108 vertical-align: middle;
@@ -114,15 +123,19 @@ td#crumb {
114 123
115td#crumb a { 124td#crumb a {
116 color: #ccc; 125 color: #ccc;
126 background-color: #666;
127 padding: 0em 0.5em 0em 0.5em;
117} 128}
118 129
119td#crumb a:hover { 130td#crumb a:hover {
120 color: #eee; 131 color: #666;
132 background-color: #ccc;
133 text-decoration: none;
121} 134}
122 135
123td#search { 136td#search {
124 text-align: right; 137 text-align: right;
125 vertical-align: center; 138 vertical-align: middle;
126 padding-right: 0.5em; 139 padding-right: 0.5em;
127} 140}
128 141
@@ -171,35 +184,47 @@ div.error {
171 margin: 1em 2em; 184 margin: 1em 2em;
172} 185}
173 186
174td.ls-blob, td.ls-dir, td.ls-mod { 187a.ls-blob, a.ls-dir, a.ls-mod {
175 font-family: monospace; 188 font-family: monospace;
176} 189}
177 190
178div.ls-dir a { 191td.ls-size {
179 font-weight: bold; 192 text-align: right;
180} 193}
181 194
182th.filesize, td.filesize { 195td.ls-size {
183 text-align: right; 196 font-family: monospace;
184} 197}
185 198
186td.filesize { 199td.ls-mode {
187 font-family: monospace; 200 font-family: monospace;
188} 201}
189 202
190td.links { 203table.blob {
191 font-size: 80%; 204 margin-top: 0.5em;
192 padding-left: 2em; 205 border-top: solid 1px black;
193} 206}
194 207
195td.filemode { 208table.blob td.no {
196 font-family: monospace; 209 border-right: solid 1px black;
210 color: black;
211 background-color: #eee;
212 text-align: right;
213}
214
215table.blob td.no a {
216 color: black;
197} 217}
198 218
199td.blob { 219table.blob td.no a:hover {
220 color: black;
221 text-decoration: none;
222}
223
224table.blob td.txt {
200 white-space: pre; 225 white-space: pre;
201 font-family: monospace; 226 font-family: monospace;
202 background-color: white; 227 padding-left: 0.5em;
203} 228}
204 229
205table.nowrap td { 230table.nowrap td {
@@ -215,6 +240,7 @@ table.commit-info th {
215 text-align: left; 240 text-align: left;
216 font-weight: normal; 241 font-weight: normal;
217 padding: 0.1em 1em 0.1em 0.1em; 242 padding: 0.1em 1em 0.1em 0.1em;
243 vertical-align: top;
218} 244}
219 245
220table.commit-info td { 246table.commit-info td {
@@ -287,7 +313,7 @@ table.diffstat td.upd a {
287 313
288table.diffstat td.graph { 314table.diffstat td.graph {
289 width: 75%; 315 width: 75%;
290 vertical-align: center; 316 vertical-align: middle;
291} 317}
292 318
293table.diffstat td.graph table { 319table.diffstat td.graph table {
@@ -308,10 +334,6 @@ table.diffstat td.graph td.rem {
308 background-color: #c55; 334 background-color: #c55;
309} 335}
310 336
311table.diffstat td.graph td.none {
312 background-color: none;
313}
314
315div.diffstat-summary { 337div.diffstat-summary {
316 color: #888; 338 color: #888;
317 padding-top: 0.5em; 339 padding-top: 0.5em;
@@ -340,7 +362,7 @@ table.diff td div.del {
340} 362}
341 363
342.sha1 { 364.sha1 {
343 font-family: courier; 365 font-family: monospace;
344 font-size: 90%; 366 font-size: 90%;
345} 367}
346 368
@@ -359,16 +381,17 @@ table.list td.repogroup {
359 381
360a.button { 382a.button {
361 font-size: 80%; 383 font-size: 80%;
362 color: #333; 384 color: #aaa;
363 background-color: #ccc; 385 background-color: #eee;
364 border: solid 1px #999; 386 border: solid 1px #aaa;
365 padding: 0em 0.5em; 387 padding: 0em 0.5em;
366 margin: 0.1em 0.25em; 388 margin: 0.1em 0.25em;
367} 389}
368 390
369a.button:hover { 391a.button:hover {
370 text-decoration: none; 392 text-decoration: none;
371 background-color: #eee; 393 color: #333;
394 background-color: #ccc;
372} 395}
373 396
374a.primary { 397a.primary {
diff --git a/cgit.h b/cgit.h
index 2f3fca1..e3d9cb8 100644
--- a/cgit.h
+++ b/cgit.h
@@ -25,10 +25,9 @@
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_VIEW 5 28#define CMD_BLOB 5
29#define CMD_BLOB 6 29#define CMD_SNAPSHOT 6
30#define CMD_SNAPSHOT 7 30#define CMD_TAG 7
31
32 31
33/* 32/*
34 * Dateformats used on misc. pages 33 * Dateformats used on misc. pages
@@ -99,7 +98,7 @@ struct taginfo {
99 char *msg; 98 char *msg;
100}; 99};
101 100
102extern const char cgit_version[]; 101extern const char *cgit_version;
103 102
104extern struct repolist cgit_repolist; 103extern struct repolist cgit_repolist;
105extern struct repoinfo *cgit_repo; 104extern struct repoinfo *cgit_repo;
@@ -119,6 +118,7 @@ extern char *cgit_repo_group;
119 118
120extern int cgit_nocache; 119extern int cgit_nocache;
121extern int cgit_snapshots; 120extern int cgit_snapshots;
121extern int cgit_enable_index_links;
122extern int cgit_enable_log_filecount; 122extern int cgit_enable_log_filecount;
123extern int cgit_enable_log_linecount; 123extern int cgit_enable_log_linecount;
124extern int cgit_max_lock_attempts; 124extern int cgit_max_lock_attempts;
@@ -157,8 +157,10 @@ extern void cgit_querystring_cb(const char *name, const char *value);
157 157
158extern int chk_zero(int result, char *msg); 158extern int chk_zero(int result, char *msg);
159extern int chk_positive(int result, char *msg); 159extern int chk_positive(int result, char *msg);
160extern int chk_non_negative(int result, char *msg);
160 161
161extern int hextoint(char c); 162extern int hextoint(char c);
163extern char *trim_end(const char *str, char c);
162 164
163extern void *cgit_free_commitinfo(struct commitinfo *info); 165extern void *cgit_free_commitinfo(struct commitinfo *info);
164 166
@@ -199,9 +201,26 @@ extern int cache_exist(struct cacheitem *item);
199extern int cache_expired(struct cacheitem *item); 201extern int cache_expired(struct cacheitem *item);
200 202
201extern char *cgit_repourl(const char *reponame); 203extern char *cgit_repourl(const char *reponame);
204extern char *cgit_fileurl(const char *reponame, const char *pagename,
205 const char *filename, const char *query);
202extern char *cgit_pageurl(const char *reponame, const char *pagename, 206extern char *cgit_pageurl(const char *reponame, const char *pagename,
203 const char *query); 207 const char *query);
204 208
209extern const char *cgit_repobasename(const char *reponame);
210
211extern void cgit_tree_link(char *name, char *title, char *class, char *head,
212 char *rev, char *path);
213extern void cgit_log_link(char *name, char *title, char *class, char *head,
214 char *rev, char *path, int ofs);
215extern void cgit_commit_link(char *name, char *title, char *class, char *head,
216 char *rev);
217extern void cgit_snapshot_link(char *name, char *title, char *class,
218 char *head, char *rev, char *archivename);
219extern void cgit_diff_link(char *name, char *title, char *class, char *head,
220 char *new_rev, char *old_rev, char *path);
221
222extern void cgit_object_link(struct object *obj);
223
205extern void cgit_print_error(char *msg); 224extern void cgit_print_error(char *msg);
206extern void cgit_print_date(time_t secs, char *format); 225extern void cgit_print_date(time_t secs, char *format);
207extern void cgit_print_age(time_t t, time_t max_relative, char *format); 226extern void cgit_print_age(time_t t, time_t max_relative, char *format);
@@ -215,14 +234,16 @@ extern void cgit_print_snapshot_start(const char *mimetype,
215extern void cgit_print_repolist(struct cacheitem *item); 234extern void cgit_print_repolist(struct cacheitem *item);
216extern void cgit_print_summary(); 235extern void cgit_print_summary();
217extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager); 236extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager);
218extern void cgit_print_view(const char *hex, char *path);
219extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 237extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
220extern void cgit_print_tree(const char *rev, const char *hex, char *path); 238extern void cgit_print_tree(const char *rev, char *path);
221extern void cgit_print_commit(const char *hex); 239extern void cgit_print_commit(char *hex);
222extern void cgit_print_diff(const char *head, const char *old_hex, const char *new_hex, 240extern void cgit_print_tag(char *revname);
223 char *path); 241extern void cgit_print_diff(const char *new_hex, const char *old_hex);
224extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, 242extern void cgit_print_snapshot(struct cacheitem *item, const char *head,
225 const char *format, const char *prefix, 243 const char *hex, const char *prefix,
226 const char *filename); 244 const char *filename, int snapshot);
245extern void cgit_print_snapshot_links(const char *repo, const char *head,
246 const char *hex, int snapshots);
247extern int cgit_parse_snapshots_mask(const char *str);
227 248
228#endif /* CGIT_H */ 249#endif /* CGIT_H */
diff --git a/cgitrc b/cgitrc
index 0f602e4..1040997 100644
--- a/cgitrc
+++ b/cgitrc
@@ -8,10 +8,15 @@
8#nocache=0 8#nocache=0
9 9
10 10
11## Enable/disable snapshots by default. This 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#snapshots=0 13#snapshots=0
13 14
14 15
16## Enable/disable extra links to summary/log/tree per repo on index page
17#enable-index-links=0
18
19
15## Enable/disable display of 'number of files changed' in log view 20## Enable/disable display of 'number of files changed' in log view
16#enable-log-filecount=0 21#enable-log-filecount=0
17 22
@@ -109,7 +114,7 @@
109#repo.desc=the caching cgi for git 114#repo.desc=the caching cgi for git
110#repo.path=/pub/git/cgit 115#repo.path=/pub/git/cgit
111#repo.owner=Lars Hjemli 116#repo.owner=Lars Hjemli
112#repo.snapshots=1 # override a sitewide snapshot-setting 117#repo.snapshots=tar.bz2 # override a sitewide snapshot-setting
113#repo.enable-log-filecount=0 # override the default filecount setting 118#repo.enable-log-filecount=0 # override the default filecount setting
114#repo.enable-log-linecount=0 # override the default linecount setting 119#repo.enable-log-linecount=0 # override the default linecount setting
115#repo.module-link=/git/%s/commit/?id=%s # override the standard module-link 120#repo.module-link=/git/%s/commit/?id=%s # override the standard module-link
diff --git a/gen-version.sh b/gen-version.sh
new file mode 100755
index 0000000..739c83e
--- /dev/null
+++ b/gen-version.sh
@@ -0,0 +1,20 @@
1#!/bin/sh
2
3# Get version-info specified in Makefile
4V=$1
5
6# Use `git describe` to get current version if we're inside a git repo
7if test -d .git
8then
9 V=$(git describe --abbrev=4 HEAD 2>/dev/null | sed -e 's/-/./g')
10fi
11
12new="CGIT_VERSION = $V"
13old=$(cat VERSION 2>/dev/null)
14
15# Exit if VERSION is uptodate
16test "$old" = "$new" && exit 0
17
18# Update VERSION with new version-info
19echo "$new" > VERSION
20cat VERSION
diff --git a/git b/git
Subproject aba170cdb4874b72dd619e6f7bbc13c33295f83 Subproject 86bab9615c3516d4ac7756ae3c1285d331b78f0
diff --git a/parsing.c b/parsing.c
index 74a2484..2c05c09 100644
--- a/parsing.c
+++ b/parsing.c
@@ -168,7 +168,7 @@ void cgit_parse_url(const char *url)
168 if (p) { 168 if (p) {
169 p[0] = '\0'; 169 p[0] = '\0';
170 if (p[1]) 170 if (p[1])
171 cgit_query_path = xstrdup(p + 1); 171 cgit_query_path = trim_end(p + 1, '/');
172 } 172 }
173 cgit_cmd = cgit_get_cmd_index(cmd + 1); 173 cgit_cmd = cgit_get_cmd_index(cmd + 1);
174 cgit_query_page = xstrdup(cmd + 1); 174 cgit_query_page = xstrdup(cmd + 1);
diff --git a/shared.c b/shared.c
index b6d2fa1..077934f 100644
--- a/shared.c
+++ b/shared.c
@@ -12,6 +12,8 @@ struct repolist cgit_repolist;
12struct repoinfo *cgit_repo; 12struct repoinfo *cgit_repo;
13int cgit_cmd; 13int cgit_cmd;
14 14
15const char *cgit_version = CGIT_VERSION;
16
15char *cgit_root_title = "Git repository browser"; 17char *cgit_root_title = "Git repository browser";
16char *cgit_css = "/cgit.css"; 18char *cgit_css = "/cgit.css";
17char *cgit_logo = "/git-logo.png"; 19char *cgit_logo = "/git-logo.png";
@@ -26,6 +28,7 @@ char *cgit_repo_group = NULL;
26 28
27int cgit_nocache = 0; 29int cgit_nocache = 0;
28int cgit_snapshots = 0; 30int cgit_snapshots = 0;
31int cgit_enable_index_links = 0;
29int cgit_enable_log_filecount = 0; 32int cgit_enable_log_filecount = 0;
30int cgit_enable_log_linecount = 0; 33int cgit_enable_log_linecount = 0;
31int cgit_max_lock_attempts = 5; 34int cgit_max_lock_attempts = 5;
@@ -59,7 +62,8 @@ int htmlfd = 0;
59 62
60int cgit_get_cmd_index(const char *cmd) 63int cgit_get_cmd_index(const char *cmd)
61{ 64{
62 static char *cmds[] = {"log", "commit", "diff", "tree", "view", "blob", "snapshot", NULL}; 65 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
66 "snapshot", "tag", NULL};
63 int i; 67 int i;
64 68
65 for(i = 0; cmds[i]; i++) 69 for(i = 0; cmds[i]; i++)
@@ -82,6 +86,13 @@ int chk_positive(int result, char *msg)
82 return result; 86 return result;
83} 87}
84 88
89int chk_non_negative(int result, char *msg)
90{
91 if (result < 0)
92 die("%s: %s",msg, strerror(errno));
93 return result;
94}
95
85struct repoinfo *add_repo(const char *url) 96struct repoinfo *add_repo(const char *url)
86{ 97{
87 struct repoinfo *ret; 98 struct repoinfo *ret;
@@ -144,7 +155,9 @@ void cgit_global_config_cb(const char *name, const char *value)
144 else if (!strcmp(name, "nocache")) 155 else if (!strcmp(name, "nocache"))
145 cgit_nocache = atoi(value); 156 cgit_nocache = atoi(value);
146 else if (!strcmp(name, "snapshots")) 157 else if (!strcmp(name, "snapshots"))
147 cgit_snapshots = atoi(value); 158 cgit_snapshots = cgit_parse_snapshots_mask(value);
159 else if (!strcmp(name, "enable-index-links"))
160 cgit_enable_index_links = atoi(value);
148 else if (!strcmp(name, "enable-log-filecount")) 161 else if (!strcmp(name, "enable-log-filecount"))
149 cgit_enable_log_filecount = atoi(value); 162 cgit_enable_log_filecount = atoi(value);
150 else if (!strcmp(name, "enable-log-linecount")) 163 else if (!strcmp(name, "enable-log-linecount"))
@@ -184,7 +197,7 @@ void cgit_global_config_cb(const char *name, const char *value)
184 else if (cgit_repo && !strcmp(name, "repo.defbranch")) 197 else if (cgit_repo && !strcmp(name, "repo.defbranch"))
185 cgit_repo->defbranch = xstrdup(value); 198 cgit_repo->defbranch = xstrdup(value);
186 else if (cgit_repo && !strcmp(name, "repo.snapshots")) 199 else if (cgit_repo && !strcmp(name, "repo.snapshots"))
187 cgit_repo->snapshots = cgit_snapshots * atoi(value); 200 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
188 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) 201 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
189 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); 202 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
190 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) 203 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
@@ -224,7 +237,7 @@ void cgit_querystring_cb(const char *name, const char *value)
224 } else if (!strcmp(name, "ofs")) { 237 } else if (!strcmp(name, "ofs")) {
225 cgit_query_ofs = atoi(value); 238 cgit_query_ofs = atoi(value);
226 } else if (!strcmp(name, "path")) { 239 } else if (!strcmp(name, "path")) {
227 cgit_query_path = xstrdup(value); 240 cgit_query_path = trim_end(value, '/');
228 } else if (!strcmp(name, "name")) { 241 } else if (!strcmp(name, "name")) {
229 cgit_query_name = xstrdup(value); 242 cgit_query_name = xstrdup(value);
230 } 243 }
@@ -253,6 +266,28 @@ int hextoint(char c)
253 return -1; 266 return -1;
254} 267}
255 268
269char *trim_end(const char *str, char c)
270{
271 int len;
272 char *s, *t;
273
274 if (str == NULL)
275 return NULL;
276 t = (char *)str;
277 len = strlen(t);
278 while(len > 0 && t[len - 1] == c)
279 len--;
280
281 if (len == 0)
282 return NULL;
283
284 c = t[len];
285 t[len] = '\0';
286 s = xstrdup(t);
287 t[len] = c;
288 return s;
289}
290
256void cgit_diff_tree_cb(struct diff_queue_struct *q, 291void cgit_diff_tree_cb(struct diff_queue_struct *q,
257 struct diff_options *options, void *data) 292 struct diff_options *options, void *data)
258{ 293{
@@ -359,7 +394,7 @@ void cgit_diff_tree(const unsigned char *old_sha1,
359 opt.format_callback_data = fn; 394 opt.format_callback_data = fn;
360 diff_setup_done(&opt); 395 diff_setup_done(&opt);
361 396
362 if (old_sha1) 397 if (old_sha1 && !is_null_sha1(old_sha1))
363 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 398 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
364 else 399 else
365 ret = diff_root_tree_sha1(new_sha1, "", &opt); 400 ret = diff_root_tree_sha1(new_sha1, "", &opt);
diff --git a/submodules.sh b/submodules.sh
deleted file mode 100755
index 1d7b13f..0000000
--- a/submodules.sh
+++ /dev/null
@@ -1,181 +0,0 @@
1#!/bin/sh
2#
3# submodules.sh: init, update or list git submodules
4#
5# Copyright (C) 2006 Lars Hjemli
6#
7# Licensed under GNU General Public License v2
8# (see COPYING for full license text)
9#
10
11
12usage="submodules.sh [-i | -u] [-q] [--cached] [path...]"
13init=
14update=
15quiet=
16cached=
17
18
19say()
20{
21 if test -z "$quiet"
22 then
23 echo -e "$@"
24 fi
25}
26
27
28die()
29{
30 echo >&2 -e "$@"
31 exit 1
32}
33
34
35
36#
37# Silently checkout specified submodule revision, return exit status of git-checkout
38#
39# $1 = local path
40# $2 = requested sha1
41#
42module_checkout()
43{
44 $(cd "$1" && git checkout "$2" 1>/dev/null 2>/dev/null)
45}
46
47
48#
49# Find all (requested) submodules, run clone + checkout on missing paths
50#
51# $@ = requested paths (default to all)
52#
53modules_init()
54{
55 git ls-files --stage -- $@ | grep -e '^160000 ' |
56 while read mode sha1 stage path
57 do
58 test -d "$path/.git" && continue
59
60 if test -d "$path"
61 then
62 rmdir "$path" 2>/dev/null ||
63 die "Directory '$path' exist, but not as a submodule"
64 fi
65
66 test -e "$path" && die "A file already exist at path '$path'"
67
68 url=$(sed -nre "s/^$path[ \t]+//p" .gitmodules)
69 test -z "$url" && die "No url found for $path in .gitmodules"
70
71 git clone "$url" "$path" || die "Clone of submodule '$path' failed"
72 module_checkout "$path" "$sha1" || die "Checkout of submodule '$path' failed"
73 say "Submodule '$path' initialized"
74 done
75}
76
77#
78# Checkout correct revision of each initialized submodule
79#
80# $@ = requested paths (default to all)
81#
82modules_update()
83{
84 git ls-files --stage -- $@ | grep -e '^160000 ' |
85 while read mode sha1 stage path
86 do
87 if ! test -d "$path/.git"
88 then
89 say "Submodule '$path' not initialized"
90 continue;
91 fi
92 subsha1=$(cd "$path" && git rev-parse --verify HEAD) ||
93 die "Unable to find current revision of submodule '$path'"
94 if test "$subsha1" != "$sha1"
95 then
96 module_checkout "$path" "$sha1" ||
97 die "Unable to checkout revision $sha1 of submodule '$path'"
98 say "Submodule '$path' reset to revision $sha1"
99 fi
100 done
101}
102
103#
104# List all registered submodules, prefixed with:
105# - submodule not initialized
106# + different version checked out
107#
108# If --cached was specified the revision in the index will be printed
109# instead of the currently checked out revision.
110#
111# $@ = requested paths (default to all)
112#
113modules_list()
114{
115 git ls-files --stage -- $@ | grep -e '^160000 ' |
116 while read mode sha1 stage path
117 do
118 if ! test -d "$path/.git"
119 then
120 say "-$sha1 $path"
121 continue;
122 fi
123 revname=$(cd "$path" && git describe $sha1)
124 if git diff-files --quiet -- "$path"
125 then
126 say " $sha1 $path\t($revname)"
127 else
128 if test -z "$cached"
129 then
130 sha1=$(cd "$path" && git rev-parse HEAD)
131 revname=$(cd "$path" && git describe HEAD)
132 fi
133 say "+$sha1 $path\t($revname)"
134 fi
135 done
136}
137
138
139while case "$#" in 0) break ;; esac
140do
141 case "$1" in
142 -i)
143 init=1
144 ;;
145 -u)
146 update=1
147 ;;
148 -q)
149 quiet=1
150 ;;
151 --cached)
152 cached=1
153 ;;
154 --)
155 break
156 ;;
157 -*)
158 echo "Usage: $usage"
159 exit 1
160 ;;
161 --*)
162 echo "Usage: $usage"
163 exit 1
164 ;;
165 *)
166 break
167 ;;
168 esac
169 shift
170done
171
172
173if test "$init" = "1"
174then
175 modules_init $@
176elif test "$update" = "1"
177then
178 modules_update $@
179else
180 modules_list $@
181fi
diff --git a/ui-commit.c b/ui-commit.c
index 1d12bbb..90e09ed 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -11,6 +11,7 @@
11static int files, slots; 11static int files, slots;
12static int total_adds, total_rems, max_changes; 12static int total_adds, total_rems, max_changes;
13static int lines_added, lines_removed; 13static int lines_added, lines_removed;
14static char *curr_rev;
14 15
15static struct fileinfo { 16static struct fileinfo {
16 char status; 17 char status;
@@ -27,7 +28,6 @@ static struct fileinfo {
27 28
28void print_fileinfo(struct fileinfo *info) 29void print_fileinfo(struct fileinfo *info)
29{ 30{
30 char *query, *query2;
31 char *class; 31 char *class;
32 32
33 switch (info->status) { 33 switch (info->status) {
@@ -75,24 +75,12 @@ void print_fileinfo(struct fileinfo *info)
75 html("]</span>"); 75 html("]</span>");
76 } 76 }
77 htmlf("</td><td class='%s'>", class); 77 htmlf("</td><td class='%s'>", class);
78 query = fmt("id=%s&amp;id2=%s&amp;path=%s", sha1_to_hex(info->old_sha1), 78 cgit_tree_link(info->new_path, NULL, NULL, cgit_query_head, curr_rev,
79 sha1_to_hex(info->new_sha1), info->new_path); 79 info->new_path);
80 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), 80 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
81 NULL, NULL); 81 htmlf(" (%s from %s)",
82 if (info->status == DIFF_STATUS_COPIED || 82 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed",
83 info->status == DIFF_STATUS_RENAMED) { 83 info->old_path);
84 html_txt(info->new_path);
85 htmlf("</a> (%s from ", info->status == DIFF_STATUS_COPIED ?
86 "copied" : "renamed");
87 query2 = fmt("id=%s", sha1_to_hex(info->old_sha1));
88 html_link_open(cgit_pageurl(cgit_query_repo, "view", query2),
89 NULL, NULL);
90 html_txt(info->old_path);
91 html("</a>)");
92 } else {
93 html_txt(info->new_path);
94 html("</a>");
95 }
96 html("</td><td class='right'>"); 84 html("</td><td class='right'>");
97 htmlf("%d", info->added + info->removed); 85 htmlf("%d", info->added + info->removed);
98 html("</td><td class='graph'>"); 86 html("</td><td class='graph'>");
@@ -145,16 +133,19 @@ void inspect_filepair(struct diff_filepair *pair)
145} 133}
146 134
147 135
148void cgit_print_commit(const char *hex) 136void cgit_print_commit(char *hex)
149{ 137{
150 struct commit *commit, *parent; 138 struct commit *commit, *parent;
151 struct commitinfo *info; 139 struct commitinfo *info;
152 struct commit_list *p; 140 struct commit_list *p;
153 unsigned char sha1[20]; 141 unsigned char sha1[20];
154 char *query; 142 char *tmp;
155 char *filename;
156 int i; 143 int i;
157 144
145 if (!hex)
146 hex = cgit_query_head;
147 curr_rev = hex;
148
158 if (get_sha1(hex, sha1)) { 149 if (get_sha1(hex, sha1)) {
159 cgit_print_error(fmt("Bad object id: %s", hex)); 150 cgit_print_error(fmt("Bad object id: %s", hex));
160 return; 151 return;
@@ -181,11 +172,11 @@ void cgit_print_commit(const char *hex)
181 html("</td><td class='right'>"); 172 html("</td><td class='right'>");
182 cgit_print_date(info->committer_date, FMT_LONGDATE); 173 cgit_print_date(info->committer_date, FMT_LONGDATE);
183 html("</td></tr>\n"); 174 html("</td></tr>\n");
184 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='"); 175 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
185 query = fmt("h=%s&amp;id=%s", sha1_to_hex(commit->object.sha1), 176 tmp = xstrdup(hex);
186 sha1_to_hex(commit->tree->object.sha1)); 177 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
187 html_attr(cgit_pageurl(cgit_query_repo, "tree", query)); 178 cgit_query_head, tmp, NULL);
188 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1)); 179 html("</td></tr>\n");
189 for (p = commit->parents; p ; p = p->next) { 180 for (p = commit->parents; p ; p = p->next) {
190 parent = lookup_commit_reference(p->item->object.sha1); 181 parent = lookup_commit_reference(p->item->object.sha1);
191 if (!parent) { 182 if (!parent) {
@@ -195,23 +186,19 @@ void cgit_print_commit(const char *hex)
195 continue; 186 continue;
196 } 187 }
197 html("<tr><th>parent</th>" 188 html("<tr><th>parent</th>"
198 "<td colspan='2' class='sha1'>" 189 "<td colspan='2' class='sha1'>");
199 "<a href='"); 190 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
200 query = fmt("h=%s", sha1_to_hex(p->item->object.sha1)); 191 cgit_query_head, sha1_to_hex(p->item->object.sha1));
201 html_attr(cgit_pageurl(cgit_query_repo, "commit", query)); 192 html(" (");
202 htmlf("'>%s</a> (<a href='", 193 cgit_diff_link("diff", NULL, NULL, cgit_query_head, hex,
203 sha1_to_hex(p->item->object.sha1)); 194 sha1_to_hex(p->item->object.sha1), NULL);
204 query = fmt("id=%s&amp;id2=%s", sha1_to_hex(parent->tree->object.sha1), 195 html(")</td></tr>");
205 sha1_to_hex(commit->tree->object.sha1));
206 html_attr(cgit_pageurl(cgit_query_repo, "diff", query));
207 html("'>diff</a>)</td></tr>");
208 } 196 }
209 if (cgit_repo->snapshots) { 197 if (cgit_repo->snapshots) {
210 htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='"); 198 html("<tr><th>download</th><td colspan='2' class='sha1'>");
211 filename = fmt("%s-%s.zip", cgit_query_repo, hex); 199 cgit_print_snapshot_links(cgit_query_repo, cgit_query_head,
212 html_attr(cgit_pageurl(cgit_query_repo, "snapshot", 200 hex, cgit_repo->snapshots);
213 fmt("id=%s&amp;name=%s", hex, filename))); 201 html("</td></tr>");
214 htmlf("'>%s</a></td></tr>", filename);
215 } 202 }
216 html("</table>\n"); 203 html("</table>\n");
217 html("<div class='commit-subject'>"); 204 html("<div class='commit-subject'>");
@@ -231,10 +218,9 @@ void cgit_print_commit(const char *hex)
231 html("<div class='diffstat-summary'>"); 218 html("<div class='diffstat-summary'>");
232 htmlf("%d files changed, %d insertions, %d deletions (", 219 htmlf("%d files changed, %d insertions, %d deletions (",
233 files, total_adds, total_rems); 220 files, total_adds, total_rems);
234 query = fmt("h=%s", hex); 221 cgit_diff_link("show diff", NULL, NULL, cgit_query_head, hex,
235 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), NULL, NULL); 222 NULL, NULL);
236 html("show diff</a>)"); 223 html(")</div>");
237 html("</div>");
238 } 224 }
239 cgit_free_commitinfo(info); 225 cgit_free_commitinfo(info);
240} 226}
diff --git a/ui-diff.c b/ui-diff.c
index 4695e3a..0be845f 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -89,54 +89,52 @@ static void filepair_cb(struct diff_filepair *pair)
89 cgit_print_error("Error running diff"); 89 cgit_print_error("Error running diff");
90} 90}
91 91
92void cgit_print_diff(const char *head, const char *old_hex, const char *new_hex, char *path) 92void cgit_print_diff(const char *new_rev, const char *old_rev)
93{ 93{
94 unsigned char sha1[20], sha2[20]; 94 unsigned char sha1[20], sha2[20];
95 enum object_type type; 95 enum object_type type;
96 unsigned long size; 96 unsigned long size;
97 struct commit *commit; 97 struct commit *commit, *commit2;
98 98
99 if (head && !old_hex && !new_hex) { 99 if (!new_rev)
100 get_sha1(head, sha1); 100 new_rev = cgit_query_head;
101 commit = lookup_commit_reference(sha1); 101 get_sha1(new_rev, sha1);
102 if (commit && !parse_commit(commit)) { 102 type = sha1_object_info(sha1, &size);
103 html("<table class='diff'>"); 103 if (type == OBJ_BAD) {
104 html("<tr><td>"); 104 cgit_print_error(fmt("Bad object name: %s", new_rev));
105 cgit_diff_commit(commit, filepair_cb); 105 return;
106 html("</td></tr>"); 106 }
107 html("</table>"); 107 if (type != OBJ_COMMIT) {
108 } 108 cgit_print_error(fmt("Unhandled object type: %s",
109 typename(type)));
109 return; 110 return;
110 } 111 }
111 112
112 get_sha1(old_hex, sha1); 113 commit = lookup_commit_reference(sha1);
113 get_sha1(new_hex, sha2); 114 if (!commit || parse_commit(commit))
115 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(sha1)));
114 116
115 type = sha1_object_info(sha1, &size); 117 if (old_rev)
116 if (type == OBJ_BAD) { 118 get_sha1(old_rev, sha2);
119 else if (commit->parents && commit->parents->item)
120 hashcpy(sha2, commit->parents->item->object.sha1);
121 else
122 hashclr(sha2);
123
124 if (!is_null_sha1(sha2)) {
117 type = sha1_object_info(sha2, &size); 125 type = sha1_object_info(sha2, &size);
118 if (type == OBJ_BAD) { 126 if (type == OBJ_BAD) {
119 cgit_print_error(fmt("Bad object names: %s, %s", old_hex, new_hex)); 127 cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(sha2)));
120 return; 128 return;
121 } 129 }
130 commit2 = lookup_commit_reference(sha2);
131 if (!commit2 || parse_commit(commit2))
132 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(sha2)));
122 } 133 }
123 134
124 html("<table class='diff'>"); 135 html("<table class='diff'>");
125 switch(type) { 136 html("<tr><td>");
126 case OBJ_BLOB: 137 cgit_diff_tree(sha2, sha1, filepair_cb);
127 html("<tr><td>"); 138 html("</td></tr>");
128 header(sha1, path, 0644, sha2, path, 0644);
129 if (cgit_diff_files(sha1, sha2, print_line))
130 cgit_print_error("Error running diff");
131 html("</td></tr>");
132 break;
133 case OBJ_TREE:
134 cgit_diff_tree(sha1, sha2, filepair_cb);
135 break;
136 default:
137 cgit_print_error(fmt("Unhandled object type: %s",
138 typename(type)));
139 break;
140 }
141 html("</table>"); 139 html("</table>");
142} 140}
diff --git a/ui-log.c b/ui-log.c
index bb17e1d..d38e40a 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -31,11 +31,8 @@ void print_commit(struct commit *commit)
31 html("<tr><td>"); 31 html("<tr><td>");
32 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 32 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
33 html("</td><td>"); 33 html("</td><td>");
34 char *qry = fmt("h=%s", sha1_to_hex(commit->object.sha1)); 34 cgit_commit_link(info->subject, NULL, NULL, cgit_query_head,
35 char *url = cgit_pageurl(cgit_query_repo, "commit", qry); 35 sha1_to_hex(commit->object.sha1));
36 html_link_open(url, NULL, NULL);
37 html_ntxt(cgit_max_msg_len, info->subject);
38 html_link_close();
39 if (cgit_repo->enable_log_filecount) { 36 if (cgit_repo->enable_log_filecount) {
40 files = 0; 37 files = 0;
41 lines = 0; 38 lines = 0;
@@ -62,6 +59,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, i
62 int argc = 2; 59 int argc = 2;
63 int i; 60 int i;
64 61
62 if (!tip)
63 argv[1] = cgit_query_head;
64
65 if (grep) 65 if (grep)
66 argv[argc++] = fmt("--grep=%s", grep); 66 argv[argc++] = fmt("--grep=%s", grep);
67 if (path) { 67 if (path) {
@@ -113,17 +113,15 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, i
113 if (pager) { 113 if (pager) {
114 html("<div class='pager'>"); 114 html("<div class='pager'>");
115 if (ofs > 0) { 115 if (ofs > 0) {
116 html("&nbsp;<a href='"); 116 cgit_log_link("[prev]", NULL, NULL, cgit_query_head,
117 html(cgit_pageurl(cgit_query_repo, cgit_query_page, 117 cgit_query_sha1, cgit_query_path,
118 fmt("h=%s&amp;ofs=%d", tip, ofs-cnt))); 118 ofs - cnt);
119 html("'>[prev]</a>&nbsp;"); 119 html("&nbsp;");
120 } 120 }
121
122 if ((commit = get_revision(&rev)) != NULL) { 121 if ((commit = get_revision(&rev)) != NULL) {
123 html("&nbsp;<a href='"); 122 cgit_log_link("[next]", NULL, NULL, cgit_query_head,
124 html(cgit_pageurl(cgit_query_repo, "log", 123 cgit_query_sha1, cgit_query_path,
125 fmt("h=%s&amp;ofs=%d", tip, ofs+cnt))); 124 ofs + cnt);
126 html("'>[next]</a>&nbsp;");
127 } 125 }
128 html("</div>"); 126 html("</div>");
129 } 127 }
diff --git a/ui-repolist.c b/ui-repolist.c
index e5c6c20..4c86543 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -44,16 +44,19 @@ static void print_modtime(struct repoinfo *repo)
44 44
45void cgit_print_repolist(struct cacheitem *item) 45void cgit_print_repolist(struct cacheitem *item)
46{ 46{
47 struct repoinfo *repo; 47 int i, columns = 4;
48 int i;
49 char *last_group = NULL; 48 char *last_group = NULL;
50 49
50 if (cgit_enable_index_links)
51 columns++;
52
51 cgit_print_docstart(cgit_root_title, item); 53 cgit_print_docstart(cgit_root_title, item);
52 cgit_print_pageheader(cgit_root_title, 0); 54 cgit_print_pageheader(cgit_root_title, 0);
53 55
54 html("<table class='list nowrap'>"); 56 html("<table class='list nowrap'>");
55 if (cgit_index_header) { 57 if (cgit_index_header) {
56 html("<tr class='nohover'><td colspan='5' class='include-block'>"); 58 htmlf("<tr class='nohover'><td colspan='%d' class='include-block'>",
59 columns);
57 html_include(cgit_index_header); 60 html_include(cgit_index_header);
58 html("</td></tr>"); 61 html("</td></tr>");
59 } 62 }
@@ -61,42 +64,45 @@ void cgit_print_repolist(struct cacheitem *item)
61 "<th class='left'>Name</th>" 64 "<th class='left'>Name</th>"
62 "<th class='left'>Description</th>" 65 "<th class='left'>Description</th>"
63 "<th class='left'>Owner</th>" 66 "<th class='left'>Owner</th>"
64 "<th class='left'>Idle</th>" 67 "<th class='left'>Idle</th>");
65 "<th>Links</th></tr>\n"); 68 if (cgit_enable_index_links)
69 html("<th>Links</th>");
70 html("</tr>\n");
66 71
67 for (i=0; i<cgit_repolist.count; i++) { 72 for (i=0; i<cgit_repolist.count; i++) {
68 repo = &cgit_repolist.repos[i]; 73 cgit_repo = &cgit_repolist.repos[i];
69 if ((last_group == NULL && repo->group != NULL) || 74 if ((last_group == NULL && cgit_repo->group != NULL) ||
70 (last_group != NULL && repo->group == NULL) || 75 (last_group != NULL && cgit_repo->group == NULL) ||
71 (last_group != NULL && repo->group!= NULL && 76 (last_group != NULL && cgit_repo->group != NULL &&
72 strcmp(repo->group, last_group))) { 77 strcmp(cgit_repo->group, last_group))) {
73 html("<tr class='nohover'><td colspan='4' class='repogroup'>"); 78 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
74 html_txt(repo->group); 79 columns);
80 html_txt(cgit_repo->group);
75 html("</td></tr>"); 81 html("</td></tr>");
76 last_group = repo->group; 82 last_group = cgit_repo->group;
77 } 83 }
78 htmlf("<tr><td class='%s'>", 84 htmlf("<tr><td class='%s'>",
79 repo->group ? "sublevel-repo" : "toplevel-repo"); 85 cgit_repo->group ? "sublevel-repo" : "toplevel-repo");
80 html_link_open(cgit_repourl(repo->url), repo->desc, NULL); 86 html_link_open(cgit_repourl(cgit_repo->url), NULL, NULL);
81 html_txt(repo->name); 87 html_txt(cgit_repo->name);
82 html_link_close(); 88 html_link_close();
83 html("</td><td>"); 89 html("</td><td>");
84 html_ntxt(cgit_max_repodesc_len, repo->desc); 90 html_ntxt(cgit_max_repodesc_len, cgit_repo->desc);
85 html("</td><td>");
86 html_txt(repo->owner);
87 html("</td><td>"); 91 html("</td><td>");
88 print_modtime(repo); 92 html_txt(cgit_repo->owner);
89 html("</td><td>"); 93 html("</td><td>");
90 html_link_open(cgit_repourl(repo->url), 94 print_modtime(cgit_repo);
91 "Summary", "button"); 95 html("</td>");
92 html("S</a>"); 96 if (cgit_enable_index_links) {
93 html_link_open(cgit_pageurl(repo->name, "log", NULL), 97 html("<td>");
94 "Log", "button"); 98 html_link_open(cgit_repourl(cgit_repo->url),
95 html("L</a>"); 99 NULL, "button");
96 html_link_open(cgit_pageurl(repo->name, "tree", NULL), 100 html("summary</a>");
97 "Files", "button"); 101 cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 0);
98 html("F</a>"); 102 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
99 html("</td></tr>\n"); 103 html("</td>");
104 }
105 html("</tr>\n");
100 } 106 }
101 html("</table>"); 107 html("</table>");
102 cgit_print_docend(); 108 cgit_print_docend();
diff --git a/ui-shared.c b/ui-shared.c
index aba93e8..5c5bcf3 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -57,13 +57,13 @@ char *cgit_repourl(const char *reponame)
57 } 57 }
58} 58}
59 59
60char *cgit_pageurl(const char *reponame, const char *pagename, 60char *cgit_fileurl(const char *reponame, const char *pagename,
61 const char *query) 61 const char *filename, const char *query)
62{ 62{
63 if (cgit_virtual_root) { 63 if (cgit_virtual_root) {
64 if (query) 64 if (query)
65 return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame, 65 return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame,
66 pagename, query); 66 pagename, filename?filename:"", query);
67 else 67 else
68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame, 68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame,
69 pagename); 69 pagename);
@@ -75,6 +75,37 @@ char *cgit_pageurl(const char *reponame, const char *pagename,
75 } 75 }
76} 76}
77 77
78char *cgit_pageurl(const char *reponame, const char *pagename,
79 const char *query)
80{
81 return cgit_fileurl(reponame,pagename,0,query);
82}
83
84const char *cgit_repobasename(const char *reponame)
85{
86 /* I assume we don't need to store more than one repo basename */
87 static char rvbuf[1024];
88 int p;
89 const char *rv;
90 strncpy(rvbuf,reponame,sizeof(rvbuf));
91 if(rvbuf[sizeof(rvbuf)-1])
92 die("cgit_repobasename: truncated repository name '%s'", reponame);
93 p = strlen(rvbuf)-1;
94 /* strip trailing slashes */
95 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
96 /* strip trailing .git */
97 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
98 p -= 3; rvbuf[p--] = 0;
99 }
100 /* strip more trailing slashes if any */
101 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
102 /* find last slash in the remaining string */
103 rv = strrchr(rvbuf,'/');
104 if(rv)
105 return ++rv;
106 return rvbuf;
107}
108
78char *cgit_currurl() 109char *cgit_currurl()
79{ 110{
80 if (!cgit_virtual_root) 111 if (!cgit_virtual_root)
@@ -87,6 +118,166 @@ char *cgit_currurl()
87 return fmt("%s/", cgit_virtual_root); 118 return fmt("%s/", cgit_virtual_root);
88} 119}
89 120
121static char *repolink(char *title, char *class, char *page, char *head,
122 char *path)
123{
124 char *delim = "?";
125
126 html("<a");
127 if (title) {
128 html(" title='");
129 html_attr(title);
130 html("'");
131 }
132 if (class) {
133 html(" class='");
134 html_attr(class);
135 html("'");
136 }
137 html(" href='");
138 if (cgit_virtual_root) {
139 html_attr(cgit_virtual_root);
140 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/')
141 html("/");
142 html_attr(cgit_repo->url);
143 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
144 html("/");
145 if (page) {
146 html(page);
147 html("/");
148 if (path)
149 html_attr(path);
150 }
151 } else {
152 html(cgit_script_name);
153 html("?url=");
154 html_attr(cgit_repo->url);
155 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
156 html("/");
157 if (page) {
158 html(page);
159 html("/");
160 if (path)
161 html_attr(path);
162 }
163 delim = "&amp;";
164 }
165 if (head && strcmp(head, cgit_repo->defbranch)) {
166 html(delim);
167 html("h=");
168 html_attr(head);
169 delim = "&amp;";
170 }
171 return fmt("%s", delim);
172}
173
174static void reporevlink(char *page, char *name, char *title, char *class,
175 char *head, char *rev, char *path)
176{
177 char *delim;
178
179 delim = repolink(title, class, page, head, path);
180 if (rev && strcmp(rev, cgit_query_head)) {
181 html(delim);
182 html("id=");
183 html_attr(rev);
184 }
185 html("'>");
186 html_txt(name);
187 html("</a>");
188}
189
190void cgit_tree_link(char *name, char *title, char *class, char *head,
191 char *rev, char *path)
192{
193 reporevlink("tree", name, title, class, head, rev, path);
194}
195
196void cgit_log_link(char *name, char *title, char *class, char *head,
197 char *rev, char *path, int ofs)
198{
199 char *delim;
200
201 delim = repolink(title, class, "log", head, path);
202 if (rev && strcmp(rev, cgit_query_head)) {
203 html(delim);
204 html("id=");
205 html_attr(rev);
206 delim = "&";
207 }
208 if (ofs > 0) {
209 html(delim);
210 html("ofs=");
211 htmlf("%d", ofs);
212 }
213 html("'>");
214 html_txt(name);
215 html("</a>");
216}
217
218void cgit_commit_link(char *name, char *title, char *class, char *head,
219 char *rev)
220{
221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
222 name[cgit_max_msg_len] = '\0';
223 name[cgit_max_msg_len - 1] = '.';
224 name[cgit_max_msg_len - 2] = '.';
225 name[cgit_max_msg_len - 3] = '.';
226 }
227 reporevlink("commit", name, title, class, head, rev, NULL);
228}
229
230void cgit_snapshot_link(char *name, char *title, char *class, char *head,
231 char *rev, char *archivename)
232{
233 reporevlink("snapshot", name, title, class, head, rev, archivename);
234}
235
236void cgit_diff_link(char *name, char *title, char *class, char *head,
237 char *new_rev, char *old_rev, char *path)
238{
239 char *delim;
240
241 delim = repolink(title, class, "diff", head, path);
242 if (new_rev && strcmp(new_rev, cgit_query_head)) {
243 html(delim);
244 html("id=");
245 html_attr(new_rev);