Merge branch 'jh/graph'

* jh/graph:
  ui-log: Move 'Age' column when commit graph is present
  ui-log: Line-wrap long commit subjects when showmsg is enabled
  ui-log: Colorize commit graph
  ui-log: Implement support for commit graphs
  ui-log: Change display of full commit messages (and notes)

Conflicts:
	cgit.css
This commit is contained in:
Lars Hjemli 2011-02-19 14:01:59 +01:00
commit 31e1f9af1d
9 changed files with 220 additions and 50 deletions

6
cgit.c
View File

@ -57,6 +57,8 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
repo->defbranch = xstrdup(value); repo->defbranch = xstrdup(value);
else if (!strcmp(name, "snapshots")) else if (!strcmp(name, "snapshots"))
repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
else if (!strcmp(name, "enable-commit-graph"))
repo->enable_commit_graph = ctx.cfg.enable_commit_graph * atoi(value);
else if (!strcmp(name, "enable-log-filecount")) else if (!strcmp(name, "enable-log-filecount"))
repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
else if (!strcmp(name, "enable-log-linecount")) else if (!strcmp(name, "enable-log-linecount"))
@ -141,6 +143,8 @@ void config_cb(const char *name, const char *value)
ctx.cfg.enable_gitweb_owner = atoi(value); ctx.cfg.enable_gitweb_owner = atoi(value);
else if (!strcmp(name, "enable-index-links")) else if (!strcmp(name, "enable-index-links"))
ctx.cfg.enable_index_links = atoi(value); ctx.cfg.enable_index_links = atoi(value);
else if (!strcmp(name, "enable-commit-graph"))
ctx.cfg.enable_commit_graph = atoi(value);
else if (!strcmp(name, "enable-log-filecount")) else if (!strcmp(name, "enable-log-filecount"))
ctx.cfg.enable_log_filecount = atoi(value); ctx.cfg.enable_log_filecount = atoi(value);
else if (!strcmp(name, "enable-log-linecount")) else if (!strcmp(name, "enable-log-linecount"))
@ -540,6 +544,8 @@ void print_repo(FILE *f, struct cgit_repo *repo)
fprintf(f, "repo.section=%s\n", repo->section); fprintf(f, "repo.section=%s\n", repo->section);
if (repo->clone_url) if (repo->clone_url)
fprintf(f, "repo.clone-url=%s\n", repo->clone_url); fprintf(f, "repo.clone-url=%s\n", repo->clone_url);
fprintf(f, "repo.enable-commit-graph=%d\n",
repo->enable_commit_graph);
fprintf(f, "repo.enable-log-filecount=%d\n", fprintf(f, "repo.enable-log-filecount=%d\n",
repo->enable_log_filecount); repo->enable_log_filecount);
fprintf(f, "repo.enable-log-linecount=%d\n", fprintf(f, "repo.enable-log-linecount=%d\n",

View File

@ -153,6 +153,35 @@ table.list td {
padding: 0.1em 0.5em 0.1em 0.5em; padding: 0.1em 0.5em 0.1em 0.5em;
} }
table.list td.commitgraph {
font-family: monospace;
white-space: pre;
}
table.list td.commitgraph .column1 {
color: #a00;
}
table.list td.commitgraph .column2 {
color: #0a0;
}
table.list td.commitgraph .column3 {
color: #aa0;
}
table.list td.commitgraph .column4 {
color: #00a;
}
table.list td.commitgraph .column5 {
color: #a0a;
}
table.list td.commitgraph .column6 {
color: #0aa;
}
table.list td.logsubject { table.list td.logsubject {
font-family: monospace; font-family: monospace;
font-weight: bold; font-weight: bold;
@ -161,18 +190,7 @@ table.list td.logsubject {
table.list td.logmsg { table.list td.logmsg {
font-family: monospace; font-family: monospace;
white-space: pre; white-space: pre;
padding: 1em 0.5em 2em 0.5em; padding: 0 0.5em;
}
table.list td.lognotes-label {
text-align:right;
vertical-align:top;
}
table.list td.lognotes {
font-family: monospace;
white-space: pre;
padding: 0em 0.5em 2em 0.5em;
} }
table.list td a { table.list td a {

3
cgit.h
View File

@ -20,6 +20,7 @@
#include <xdiff/xdiff.h> #include <xdiff/xdiff.h>
#include <utf8.h> #include <utf8.h>
#include <notes.h> #include <notes.h>
#include <graph.h>
/* /*
@ -71,6 +72,7 @@ struct cgit_repo {
char *section; char *section;
char *clone_url; char *clone_url;
int snapshots; int snapshots;
int enable_commit_graph;
int enable_log_filecount; int enable_log_filecount;
int enable_log_linecount; int enable_log_linecount;
int enable_remote_branches; int enable_remote_branches;
@ -188,6 +190,7 @@ struct cgit_config {
int enable_filter_overrides; int enable_filter_overrides;
int enable_gitweb_owner; int enable_gitweb_owner;
int enable_index_links; int enable_index_links;
int enable_commit_graph;
int enable_log_filecount; int enable_log_filecount;
int enable_log_linecount; int enable_log_linecount;
int enable_remote_branches; int enable_remote_branches;

View File

@ -91,6 +91,11 @@ embedded::
suitable for embedding in other html pages. Default value: none. See suitable for embedding in other html pages. Default value: none. See
also: "noheader". also: "noheader".
enable-commit-graph::
Flag which, when set to "1", will make cgit print an ASCII-art commit
history graph to the left of the commit messages in the repository
log page. Default value: "0".
enable-filter-overrides:: enable-filter-overrides::
Flag which, when set to "1", allows all filter settings to be Flag which, when set to "1", allows all filter settings to be
overridden in repository-specific cgitrc files. Default value: none. overridden in repository-specific cgitrc files. Default value: none.
@ -354,6 +359,10 @@ repo.defbranch::
repo.desc:: repo.desc::
The value to show as repository description. Default value: none. The value to show as repository description. Default value: none.
repo.enable-commit-graph::
A flag which can be used to disable the global setting
`enable-commit-graph'. Default value: none.
repo.enable-log-filecount:: repo.enable-log-filecount::
A flag which can be used to disable the global setting A flag which can be used to disable the global setting
`enable-log-filecount'. Default value: none. `enable-log-filecount'. Default value: none.
@ -441,6 +450,10 @@ css=/css/cgit.css
enable-index-links=1 enable-index-links=1
# Enable ASCII art commit history graph on the log pages
enable-commit-graph=1
# Show number of affected files per commit on the log pages # Show number of affected files per commit on the log pages
enable-log-filecount=1 enable-log-filecount=1

3
cmd.c
View File

@ -67,7 +67,8 @@ static void info_fn(struct cgit_context *ctx)
static void log_fn(struct cgit_context *ctx) static void log_fn(struct cgit_context *ctx)
{ {
cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count, cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1); ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1,
ctx->repo->enable_commit_graph);
} }
static void ls_cache_fn(struct cgit_context *ctx) static void ls_cache_fn(struct cgit_context *ctx)

View File

@ -56,6 +56,7 @@ struct cgit_repo *cgit_add_repo(const char *url)
ret->section = ctx.cfg.section; ret->section = ctx.cfg.section;
ret->defbranch = "master"; ret->defbranch = "master";
ret->snapshots = ctx.cfg.snapshots; ret->snapshots = ctx.cfg.snapshots;
ret->enable_commit_graph = ctx.cfg.enable_commit_graph;
ret->enable_log_filecount = ctx.cfg.enable_log_filecount; ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
ret->enable_log_linecount = ctx.cfg.enable_log_linecount; ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
ret->enable_remote_branches = ctx.cfg.enable_remote_branches; ret->enable_remote_branches = ctx.cfg.enable_remote_branches;

185
ui-log.c
View File

@ -13,6 +13,21 @@
int files, add_lines, rem_lines; int files, add_lines, rem_lines;
/*
* The list of available column colors in the commit graph.
*/
static const char *column_colors_html[] = {
"<span class='column1'>",
"<span class='column2'>",
"<span class='column3'>",
"<span class='column4'>",
"<span class='column5'>",
"<span class='column6'>",
"</span>",
};
#define COLUMN_COLORS_HTML_MAX (ARRAY_SIZE(column_colors_html) - 1)
void count_lines(char *line, int size) void count_lines(char *line, int size)
{ {
if (size <= 0) if (size <= 0)
@ -77,27 +92,93 @@ void show_commit_decorations(struct commit *commit)
} }
} }
void print_commit(struct commit *commit) void print_commit(struct commit *commit, struct rev_info *revs)
{ {
struct commitinfo *info; struct commitinfo *info;
char *tmp; char *tmp;
int cols = 2; int cols = revs->graph ? 3 : 2;
struct strbuf graphbuf = STRBUF_INIT;
struct strbuf msgbuf = STRBUF_INIT;
if (ctx.repo->enable_log_filecount) {
cols++;
if (ctx.repo->enable_log_linecount)
cols++;
}
if (revs->graph) {
/* Advance graph until current commit */
while (!graph_next_line(revs->graph, &graphbuf)) {
/* Print graph segment in otherwise empty table row */
html("<tr class='nohover'><td class='commitgraph'>");
html(graphbuf.buf);
htmlf("</td><td colspan='%d' /></tr>\n", cols);
strbuf_setlen(&graphbuf, 0);
}
/* Current commit's graph segment is now ready in graphbuf */
}
info = cgit_parse_commit(commit); info = cgit_parse_commit(commit);
htmlf("<tr%s><td>", htmlf("<tr%s>", ctx.qry.showmsg ? " class='logheader'" : "");
ctx.qry.showmsg ? " class='logheader'" : "");
if (revs->graph) {
/* Print graph segment for current commit */
html("<td class='commitgraph'>");
html(graphbuf.buf);
html("</td>");
strbuf_setlen(&graphbuf, 0);
}
else {
html("<td>");
tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1)); tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp); tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp);
html_link_open(tmp, NULL, NULL); html_link_open(tmp, NULL, NULL);
cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
html_link_close(); html_link_close();
htmlf("</td><td%s>", html("</td>");
ctx.qry.showmsg ? " class='logsubject'" : ""); }
htmlf("<td%s>", ctx.qry.showmsg ? " class='logsubject'" : "");
if (ctx.qry.showmsg) {
/* line-wrap long commit subjects instead of truncating them */
size_t subject_len = strlen(info->subject);
if (subject_len > ctx.cfg.max_msg_len &&
ctx.cfg.max_msg_len >= 15) {
/* symbol for signaling line-wrap (in PAGE_ENCODING) */
const char wrap_symbol[] = { ' ', 0xE2, 0x86, 0xB5, 0 };
int i = ctx.cfg.max_msg_len - strlen(wrap_symbol);
/* Rewind i to preceding space character */
while (i > 0 && !isspace(info->subject[i]))
--i;
if (!i) /* Oops, zero spaces. Reset i */
i = ctx.cfg.max_msg_len - strlen(wrap_symbol);
/* add remainder starting at i to msgbuf */
strbuf_add(&msgbuf, info->subject + i, subject_len - i);
strbuf_trim(&msgbuf);
strbuf_add(&msgbuf, "\n\n", 2);
/* Place wrap_symbol at position i in info->subject */
strcpy(info->subject + i, wrap_symbol);
}
}
cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0); sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0);
show_commit_decorations(commit); show_commit_decorations(commit);
html("</td><td>"); html("</td><td>");
html_txt(info->author); html_txt(info->author);
if (revs->graph) {
html("</td><td>");
tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp);
html_link_open(tmp, NULL, NULL);
cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
html_link_close();
}
if (ctx.repo->enable_log_filecount) { if (ctx.repo->enable_log_filecount) {
files = 0; files = 0;
add_lines = 0; add_lines = 0;
@ -111,29 +192,61 @@ void print_commit(struct commit *commit)
} }
} }
html("</td></tr>\n"); html("</td></tr>\n");
if (ctx.qry.showmsg) {
struct strbuf notes = STRBUF_INIT;
format_note(NULL, commit->object.sha1, &notes, PAGE_ENCODING, 0);
if (ctx.repo->enable_log_filecount) { if (revs->graph || ctx.qry.showmsg) { /* Print a second table row */
cols++;
if (ctx.repo->enable_log_linecount)
cols++;
}
htmlf("<tr class='nohover'><td/><td colspan='%d' class='logmsg'>",
cols);
html_txt(info->msg);
html("</td></tr>\n");
if (notes.len != 0) {
html("<tr class='nohover'>"); html("<tr class='nohover'>");
html("<td class='lognotes-label'>Notes:</td>");
htmlf("<td colspan='%d' class='lognotes'>", if (ctx.qry.showmsg) {
cols); /* Concatenate commit message + notes in msgbuf */
html_txt(notes.buf); if (info->msg && *(info->msg)) {
strbuf_addstr(&msgbuf, info->msg);
strbuf_addch(&msgbuf, '\n');
}
format_note(NULL, commit->object.sha1, &msgbuf,
PAGE_ENCODING,
NOTES_SHOW_HEADER | NOTES_INDENT);
strbuf_addch(&msgbuf, '\n');
strbuf_ltrim(&msgbuf);
}
if (revs->graph) {
int lines = 0;
/* Calculate graph padding */
if (ctx.qry.showmsg) {
/* Count #lines in commit message + notes */
const char *p = msgbuf.buf;
lines = 1;
while ((p = strchr(p, '\n'))) {
p++;
lines++;
}
}
/* Print graph padding */
html("<td class='commitgraph'>");
while (lines > 0 || !graph_is_commit_finished(revs->graph)) {
if (graphbuf.len)
html("\n");
strbuf_setlen(&graphbuf, 0);
graph_next_line(revs->graph, &graphbuf);
html(graphbuf.buf);
lines--;
}
html("</td>\n");
}
else
html("<td/>"); /* Empty 'Age' column */
/* Print msgbuf into remainder of table row */
htmlf("<td colspan='%d'%s>\n", cols,
ctx.qry.showmsg ? " class='logmsg'" : "");
html_txt(msgbuf.buf);
html("</td></tr>\n"); html("</td></tr>\n");
} }
strbuf_release(&notes);
} strbuf_release(&msgbuf);
strbuf_release(&graphbuf);
cgit_free_commitinfo(info); cgit_free_commitinfo(info);
} }
@ -172,7 +285,7 @@ static char *next_token(char **src)
} }
void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
char *path, int pager) char *path, int pager, int commit_graph)
{ {
struct rev_info rev; struct rev_info rev;
struct commit *commit; struct commit *commit;
@ -212,6 +325,14 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
} }
} }
} }
if (commit_graph) {
static const char *graph_arg = "--graph";
static const char *color_arg = "--color";
vector_push(&vec, &graph_arg, 0);
vector_push(&vec, &color_arg, 0);
graph_set_column_colors(column_colors_html,
COLUMN_COLORS_HTML_MAX);
}
if (path) { if (path) {
arg = "--"; arg = "--";
@ -238,8 +359,12 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
if (pager) if (pager)
html("<table class='list nowrap'>"); html("<table class='list nowrap'>");
html("<tr class='nohover'><th class='left'>Age</th>" html("<tr class='nohover'>");
"<th class='left'>Commit message"); if (commit_graph)
html("<th></th>");
else
html("<th class='left'>Age</th>");
html("<th class='left'>Commit message");
if (pager) { if (pager) {
html(" ("); html(" (");
cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL, cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL,
@ -249,6 +374,8 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
html(")"); html(")");
} }
html("</th><th class='left'>Author</th>"); html("</th><th class='left'>Author</th>");
if (commit_graph)
html("<th class='left'>Age</th>");
if (ctx.repo->enable_log_filecount) { if (ctx.repo->enable_log_filecount) {
html("<th class='left'>Files</th>"); html("<th class='left'>Files</th>");
columns++; columns++;
@ -270,7 +397,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
} }
for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
print_commit(commit); print_commit(commit, &rev);
free(commit->buffer); free(commit->buffer);
commit->buffer = NULL; commit->buffer = NULL;
free_commit_list(commit->parents); free_commit_list(commit->parents);

View File

@ -2,7 +2,8 @@
#define UI_LOG_H #define UI_LOG_H
extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
char *pattern, char *path, int pager); char *pattern, char *path, int pager,
int commit_graph);
extern void show_commit_decorations(struct commit *commit); extern void show_commit_decorations(struct commit *commit);
#endif /* UI_LOG_H */ #endif /* UI_LOG_H */

View File

@ -59,7 +59,7 @@ void cgit_print_summary()
if (ctx.cfg.summary_log > 0) { if (ctx.cfg.summary_log > 0) {
html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL, cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL,
NULL, NULL, 0); NULL, NULL, 0, 0);
} }
if (ctx.repo->clone_url) if (ctx.repo->clone_url)
print_urls(ctx.repo->clone_url, NULL); print_urls(ctx.repo->clone_url, NULL);