summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c2
-rw-r--r--cgit.css50
-rw-r--r--cgit.h5
-rw-r--r--parsing.c1
-rw-r--r--ui-commit.c80
-rw-r--r--ui-log.c11
-rw-r--r--ui-tree.c6
-rw-r--r--ui-view.c2
9 files changed, 140 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index 2a4d62a..58a583b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,30 @@
CGIT_VERSION = 0.1-pre
INSTALL_BIN = /var/www/htdocs/cgit.cgi
INSTALL_CSS = /var/www/htdocs/cgit.css
CACHE_ROOT = /var/cache/cgit
EXTLIBS = ../git/libgit.a ../git/xdiff/lib.a -lz -lcrypto
OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
- ui-summary.o ui-log.o ui-view.c ui-tree.c
+ ui-summary.o ui-log.o ui-view.c ui-tree.c ui-commit.c
CFLAGS += -Wall
all: cgit
install: all clean-cache
install cgit $(INSTALL_BIN)
install cgit.css $(INSTALL_CSS)
cgit: cgit.c cgit.h git.h $(OBJECTS)
$(CC) $(CFLAGS) -DCGIT_VERSION='"$(CGIT_VERSION)"' cgit.c -o cgit \
$(OBJECTS) $(EXTLIBS)
$(OBJECTS): cgit.h git.h
.PHONY: clean
clean:
rm -f cgit *.o
clean-cache:
rm -rf $(CACHE_ROOT)/*
diff --git a/cgit.c b/cgit.c
index d7e586d..37cdb83 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,82 +1,84 @@
/* cgit.c: cgi for the git scm
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
const char cgit_version[] = CGIT_VERSION;
static void cgit_print_repo_page(struct cacheitem *item)
{
if (chdir(fmt("%s/%s", cgit_root, cgit_query_repo)) ||
cgit_read_config("info/cgit", cgit_repo_config_cb)) {
char *title = fmt("%s - %s", cgit_root_title, "Bad request");
cgit_print_docstart(title, item);
cgit_print_pageheader(title);
cgit_print_error(fmt("Unable to scan repository: %s",
strerror(errno)));
cgit_print_docend();
return;
}
setenv("GIT_DIR", fmt("%s/%s", cgit_root, cgit_query_repo), 1);
char *title = fmt("%s - %s", cgit_repo_name, cgit_repo_desc);
cgit_print_docstart(title, item);
cgit_print_pageheader(title);
if (!cgit_query_page) {
cgit_print_summary();
} else if (!strcmp(cgit_query_page, "log")) {
cgit_print_log(cgit_query_head, cgit_query_ofs, 100);
} else if (!strcmp(cgit_query_page, "tree")) {
cgit_print_tree(cgit_query_sha1);
+ } else if (!strcmp(cgit_query_page, "commit")) {
+ cgit_print_commit(cgit_query_sha1);
} else if (!strcmp(cgit_query_page, "view")) {
cgit_print_view(cgit_query_sha1);
}
cgit_print_docend();
}
static void cgit_fill_cache(struct cacheitem *item)
{
htmlfd = item->fd;
item->st.st_mtime = time(NULL);
if (cgit_query_repo)
cgit_print_repo_page(item);
else
cgit_print_repolist(item);
}
static void cgit_check_cache(struct cacheitem *item)
{
int i = 0;
cache_prepare(item);
top:
if (++i > cgit_max_lock_attempts) {
die("cgit_refresh_cache: unable to lock %s: %s",
item->name, strerror(errno));
}
if (!cache_exist(item)) {
if (!cache_lock(item)) {
sleep(1);
goto top;
}
if (!cache_exist(item)) {
cgit_fill_cache(item);
cache_unlock(item);
} else {
cache_cancel_lock(item);
}
} else if (cache_expired(item) && cache_lock(item)) {
if (cache_expired(item)) {
cgit_fill_cache(item);
cache_unlock(item);
} else {
cache_cancel_lock(item);
}
}
}
static void cgit_print_cache(struct cacheitem *item)
diff --git a/cgit.css b/cgit.css
index 97b4e27..3579598 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,78 +1,120 @@
body {
font-family: arial;
font-size: normal;
background: white;
padding: 0em;
margin: 0.5em;
}
h2 {
font-size: normal;
font-weight: bold;
margin-bottom: 0.1em;
}
table.list {
border: solid 1px black;
border-collapse: collapse;
border: solid 1px #aaa;
}
table.list th {
- text-align: left;
font-weight: bold;
background: #ddd;
border-bottom: solid 1px #aaa;
- padding: 0.1em 0.5em 0.1em;
+ padding: 0.1em 0.5em 0.1em 0.5em;
vertical-align: baseline;
}
table.list td {
border: none;
- padding: 0.1em 1em 0.1em 0.5em;
+ padding: 0.1em 0.5em 0.1em 0.5em;
background: white;
}
img {
border: none;
}
div#header {
background-color: #ddd;
padding: 0.25em 0.25em 0.25em 0.5em;
font-size: 150%;
font-weight: bold;
border: solid 1px #aaa;
vertical-align: middle;
}
div#header img#logo {
float: right;
}
div#content {
margin: 0.5em 0.5em;
}
+div#blob {
+ border: solid 1px black;
+}
+
div.error {
color: red;
font-weight: bold;
margin: 1em 2em;
}
div.ls-dir a {
font-weight: bold;
}
th.filesize, td.filesize {
text-align: right;
}
th.filemode, td.filemode {
text-align: center;
}
td.blob {
white-space: pre;
font-family: courier;
font-size: 100%;
-} \ No newline at end of file
+}
+
+table.log td {
+ white-space: nowrap;
+}
+
+table.commit-info {
+ border-collapse: collapse;
+ margin-top: 1em;
+
+}
+table.commit-info th {
+ text-align: left;
+ font-weight: normal;
+ padding: 0.1em 1em 0.1em 0.1em;
+}
+table.commit-info td {
+ font-weight: normal;
+ padding: 0.1em 1em 0.1em 0.1em;
+}
+div.commit-subject {
+ font-weight: bold;
+ font-size: 110%;
+ margin: 1em 0em 1em;
+}
+div.commit-msg {
+ white-space: pre;
+ font-family: courier;
+ font-size: 100%;
+}
+.sha1 {
+ font-family: courier;
+ font-size: 90%;
+}
+.left {
+ text-align: left;
+}
+.right {
+ text-align: right;
+}
diff --git a/cgit.h b/cgit.h
index 268db53..a905e47 100644
--- a/cgit.h
+++ b/cgit.h
@@ -47,52 +47,53 @@ extern char *cgit_repo_owner;
extern int cgit_query_has_symref;
extern int cgit_query_has_sha1;
extern char *cgit_querystring;
extern char *cgit_query_repo;
extern char *cgit_query_page;
extern char *cgit_query_head;
extern char *cgit_query_sha1;
extern int cgit_query_ofs;
extern int htmlfd;
extern void cgit_global_config_cb(const char *name, const char *value);
extern void cgit_repo_config_cb(const char *name, const char *value);
extern void cgit_querystring_cb(const char *name, const char *value);
extern char *fmt(const char *format,...);
extern void html(const char *txt);
extern void htmlf(const char *format,...);
extern void html_txt(char *txt);
extern void html_attr(char *txt);
extern void html_link_open(char *url, char *title, char *class);
extern void html_link_close(void);
extern int cgit_read_config(const char *filename, configfn fn);
extern int cgit_parse_query(char *txt, configfn fn);
extern struct commitinfo *cgit_parse_commit(struct commit *commit);
extern void cache_prepare(struct cacheitem *item);
extern int cache_lock(struct cacheitem *item);
extern int cache_unlock(struct cacheitem *item);
extern int cache_cancel_lock(struct cacheitem *item);
extern int cache_exist(struct cacheitem *item);
extern int cache_expired(struct cacheitem *item);
extern char *cgit_repourl(const char *reponame);
extern char *cgit_pageurl(const char *reponame, const char *pagename,
const char *query);
extern void cgit_print_error(char *msg);
extern void cgit_print_docstart(char *title, struct cacheitem *item);
extern void cgit_print_docend();
extern void cgit_print_pageheader(char *title);
extern void cgit_print_repolist(struct cacheitem *item);
extern void cgit_print_summary();
extern void cgit_print_log(const char *tip, int ofs, int cnt);
-extern void cgit_print_view(char *hex);
-extern void cgit_print_tree(const char *sha1);
+extern void cgit_print_view(const char *hex);
+extern void cgit_print_tree(const char *hex);
+extern void cgit_print_commit(const char *hex);
#endif /* CGIT_H */
diff --git a/parsing.c b/parsing.c
index 6cab0e9..be471b5 100644
--- a/parsing.c
+++ b/parsing.c
@@ -105,55 +105,56 @@ int cgit_parse_query(char *txt, configfn fn)
return 0;
}
char *substr(const char *head, const char *tail)
{
char *buf;
buf = xmalloc(tail - head + 1);
strncpy(buf, head, tail - head);
buf[tail - head] = '\0';
return buf;
}
struct commitinfo *cgit_parse_commit(struct commit *commit)
{
struct commitinfo *ret;
char *p = commit->buffer, *t = commit->buffer;
ret = xmalloc(sizeof(*ret));
ret->commit = commit;
if (strncmp(p, "tree ", 5))
die("Bad commit: %s", sha1_to_hex(commit->object.sha1));
else
p += 46; // "tree " + hex[40] + "\n"
while (!strncmp(p, "parent ", 7))
p += 48; // "parent " + hex[40] + "\n"
if (!strncmp(p, "author ", 7)) {
p += 7;
t = strchr(p, '<') - 1;
ret->author = substr(p, t);
p = strchr(p, '\n') + 1;
}
if (!strncmp(p, "committer ", 9)) {
p += 9;
t = strchr(p, '<') - 1;
ret->committer = substr(p, t);
p = strchr(p, '\n') + 1;
}
while (*p == '\n')
p = strchr(p, '\n') + 1;
t = strchr(p, '\n');
ret->subject = substr(p, t);
+ p = t + 1;
while (*p == '\n')
p = strchr(p, '\n') + 1;
ret->msg = p;
return ret;
}
diff --git a/ui-commit.c b/ui-commit.c
new file mode 100644
index 0000000..1c0e7e5
--- a/dev/null
+++ b/ui-commit.c
@@ -0,0 +1,80 @@
+#include "cgit.h"
+
+void cgit_print_date(unsigned long secs)
+{
+ char buf[32];
+ struct tm *time;
+
+ time = gmtime(&secs);
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", time);
+ html_txt(buf);
+
+}
+
+void cgit_print_commit(const char *hex)
+{
+ struct commit *commit;
+ struct commitinfo *info;
+ struct commit_list *p;
+ unsigned long size;
+ char type[20];
+ char *buf;
+
+ unsigned char sha1[20];
+
+ if (get_sha1(hex, sha1)) {
+ cgit_print_error(fmt("Bad object id: %s", hex));
+ return;
+ }
+
+ buf = read_sha1_file(sha1, type, &size);
+ if (!buf) {
+ cgit_print_error(fmt("Bad object reference: %s", hex));
+ return;
+ }
+
+ commit = lookup_commit(sha1);
+ if (!commit) {
+ cgit_print_error(fmt("Bad commit reference: %s", hex));
+ return;
+ }
+
+ commit->buffer = buf;
+ if (parse_commit_buffer(commit, buf, size)) {
+ cgit_print_error(fmt("Malformed commit buffer: %s", hex));
+ return;
+ }
+
+ info = cgit_parse_commit(commit);
+
+ html("<table class='commit-info'>\n");
+ html("<tr><th>author</th><td colspan='2'>");
+ html_txt(info->author);
+ html("</td></tr>\n");
+ html("<tr><th>committer</th><td>");
+ html_txt(info->committer);
+ html("</td><td class='right'>");
+ cgit_print_date(commit->date);
+ html("</td></tr>\n");
+ html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='");
+ html_attr(cgit_pageurl(cgit_query_repo, "tree", fmt("id=%s", sha1_to_hex(commit->tree->object.sha1))));
+ htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1));
+
+ for (p = commit->parents; p ; p = p->next) {
+ html("<tr><th>parent</th><td colspan='2' class='sha1'><a href='");
+ html_attr(cgit_pageurl(cgit_query_repo, "commit", fmt("id=%s", sha1_to_hex(p->item->object.sha1))));
+ htmlf("'>%s</a></td></tr>\n",
+ sha1_to_hex(p->item->object.sha1));
+ }
+ html("</table>\n");
+ html("<div class='commit-subject'>");
+ html_txt(info->subject);
+ html("</div>");
+ html("<div class='commit-msg'>");
+ html_txt(info->msg);
+ html("</div>");
+ free(info->author);
+ free(info->committer);
+ free(info->subject);
+ free(info);
+}
diff --git a/ui-log.c b/ui-log.c
index 31331ef..c52af79 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -1,97 +1,92 @@
/* ui-log.c: functions for log output
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
void print_commit(struct commit *commit)
{
char buf[32];
struct commitinfo *info;
struct tm *time;
info = cgit_parse_commit(commit);
time = gmtime(&commit->date);
html("<tr><td>");
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", time);
html_txt(buf);
html("</td><td>");
char *qry = fmt("id=%s", sha1_to_hex(commit->object.sha1));
- char *url = cgit_pageurl(cgit_query_repo, "view", qry);
+ char *url = cgit_pageurl(cgit_query_repo, "commit", qry);
html_link_open(url, NULL, NULL);
html_txt(info->subject);
html_link_close();
html("</td><td>");
html_txt(info->author);
- html("</td><td><a href='");
- html_attr(cgit_pageurl(cgit_query_repo, "tree",
- fmt("id=%s",
- sha1_to_hex(commit->tree->object.sha1))));
- html("'>tree</a>");
html("</td></tr>\n");
free(info->author);
free(info->committer);
free(info->subject);
free(info);
}
void cgit_print_log(const char *tip, int ofs, int cnt)
{
struct rev_info rev;
struct commit *commit;
const char *argv[2] = {NULL, tip};
int i;
init_revisions(&rev, NULL);
rev.abbrev = DEFAULT_ABBREV;
rev.commit_format = CMIT_FMT_DEFAULT;
rev.verbose_header = 1;
rev.show_root_diff = 0;
setup_revisions(2, argv, &rev, NULL);
prepare_revision_walk(&rev);
html("<h2>Log</h2>");
- html("<table class='list'>");
- html("<tr><th>Date</th><th>Message</th><th>Author</th><th>Link</th></tr>\n");
+ html("<table class='list log'>");
+ html("<tr><th class='left'>Date</th><th class='left'>Message</th><th class='left'>Author</th></tr>\n");
if (ofs<0)
ofs = 0;
for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) {
free(commit->buffer);
commit->buffer = NULL;
free_commit_list(commit->parents);
commit->parents = NULL;
}
for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
print_commit(commit);
free(commit->buffer);
commit->buffer = NULL;
free_commit_list(commit->parents);
commit->parents = NULL;
}
html("</table>\n");
html("<div class='pager'>");
if (ofs > 0) {
html("&nbsp;<a href='");
html(cgit_pageurl(cgit_query_repo, cgit_query_page,
fmt("h=%s&ofs=%d", tip, ofs-cnt)));
html("'>[prev]</a>&nbsp;");
}
if ((commit = get_revision(&rev)) != NULL) {
html("&nbsp;<a href='");
html(cgit_pageurl(cgit_query_repo, "log",
fmt("h=%s&ofs=%d", tip, ofs+cnt)));
html("'>[next]</a>&nbsp;");
}
html("</div>");
}
diff --git a/ui-tree.c b/ui-tree.c
index 84930cb..c4d75ab 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -17,54 +17,54 @@ static int print_entry(const unsigned char *sha1, const char *base,
char type[20];
unsigned long size;
if (sha1_object_info(sha1, type, &size)) {
cgit_print_error(fmt("Bad object name: %s",
sha1_to_hex(sha1)));
return 0;
}
name = xstrdup(pathname);
html("<tr><td>");
if (S_ISDIR(mode)) {
html("<div class='ls-dir'><a href='");
html_attr(cgit_pageurl(cgit_query_repo, "tree",
fmt("id=%s", sha1_to_hex(sha1))));
} else {
html("<div class='ls-blob'><a href='");
html_attr(cgit_pageurl(cgit_query_repo, "view",
fmt("id=%s", sha1_to_hex(sha1))));
}
html("'>");
html_txt(name);
if (S_ISDIR(mode))
html("/");
html("</a></div></td>");
htmlf("<td class='filesize'>%li</td>", size);
htmlf("<td class='filemode'>%06o</td>", mode);
html("</tr>\n");
free(name);
return 0;
}
void cgit_print_tree(const char *hex)
{
struct tree *tree;
unsigned char sha1[20];
if (get_sha1_hex(hex, sha1)) {
cgit_print_error(fmt("Invalid object id: %s", hex));
return;
}
tree = parse_tree_indirect(sha1);
if (!tree) {
cgit_print_error(fmt("Not a tree object: %s", hex));
return;
}
html("<h2>Tree content</h2>\n");
html("<table class='list'>\n");
- html("<tr><th>Name</th>");
- html("<th class='filesize'>Size</th>");
- html("<th class='filemode'>Mode</th></tr>\n");
+ html("<tr><th class='left'>Name</th>");
+ html("<th class='right'>Size</th>");
+ html("<th class='right'>Mode</th></tr>\n");
read_tree_recursive(tree, "", 0, 1, NULL, print_entry);
html("</table>\n");
}
diff --git a/ui-view.c b/ui-view.c
index 9d13be1..b75ce9a 100644
--- a/ui-view.c
+++ b/ui-view.c
@@ -1,42 +1,42 @@
/* ui-view.c: functions to output _any_ object, given it's sha1
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
-void cgit_print_view(char *hex)
+void cgit_print_view(const char *hex)
{
unsigned char sha1[20];
char type[20];
unsigned char *buf;
unsigned long size;
if (get_sha1_hex(hex, sha1)){
cgit_print_error(fmt("Bad hex value: %s", hex));
return;
}
if (sha1_object_info(sha1, type, &size)){
cgit_print_error("Bad object name");
return;
}
buf = read_sha1_file(sha1, type, &size);
if (!buf) {
cgit_print_error("Error reading object");
return;
}
buf[size] = '\0';
html("<h2>Object content</h2>\n");
html("<table class='list'>\n");
htmlf("<tr><th>%s %s, %li bytes</th></tr>\n", type, hex, size);
html("<tr><td class='blob'>\n");
html_txt(buf);
html("\n</td></tr>\n");
html("</table>\n");
}