From 40e174d5364910750413d94b5417e57d108190ef Mon Sep 17 00:00:00 2001 From: Ragnar Ouchterlony Date: Sun, 13 Sep 2009 19:36:35 +0200 Subject: [PATCH 1/5] First version of side-by-side diff. This constitutes the first prototype of a side-by-side diff. It is not possible to switch between unidiff and side-by-side diff at all at this stage. Signed-off-by: Ragnar Ouchterlony Signed-off-by: Lars Hjemli --- Makefile | 1 + cgit.css | 35 +++++++ ui-diff.c | 15 ++- ui-ssdiff.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ui-ssdiff.h | 12 +++ 5 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 ui-ssdiff.c create mode 100644 ui-ssdiff.h diff --git a/Makefile b/Makefile index 60d8c58..cb7875e 100644 --- a/Makefile +++ b/Makefile @@ -90,6 +90,7 @@ OBJECTS += ui-refs.o OBJECTS += ui-repolist.o OBJECTS += ui-shared.o OBJECTS += ui-snapshot.o +OBJECTS += ui-ssdiff.o OBJECTS += ui-stats.o OBJECTS += ui-summary.o OBJECTS += ui-tag.o diff --git a/cgit.css b/cgit.css index c47ebc9..bf58b8a 100644 --- a/cgit.css +++ b/cgit.css @@ -601,3 +601,38 @@ table.hgraph div.bar { background-color: #eee; height: 1em; } + +table.ssdiff td.add { + color: black; + background: #afa; +} + +table.ssdiff td.add_dark { + color: black; + background: #9c9; +} + +table.ssdiff td.del { + color: black; + background: #faa; +} + +table.ssdiff td.del_dark { + color: black; + background: #c99; +} + +table.ssdiff td.changed { + color: black; + background: #ffa; +} + +table.ssdiff td.changed_dark { + color: black; + background: #cc9; +} + +table.ssdiff td.hunk { + color: #black; + background: #ccf; +} diff --git a/ui-diff.c b/ui-diff.c index 2196745..0c6f8d7 100644 --- a/ui-diff.c +++ b/ui-diff.c @@ -9,6 +9,7 @@ #include "cgit.h" #include "html.h" #include "ui-shared.h" +#include "ui-ssdiff.h" unsigned char old_rev_sha1[20]; unsigned char new_rev_sha1[20]; @@ -32,6 +33,7 @@ static struct fileinfo { int binary:1; } *items; +static int use_ssdiff = 0; static void print_fileinfo(struct fileinfo *info) { @@ -244,6 +246,8 @@ static void header(unsigned char *sha1, char *path1, int mode1, html_txt(path2); } html(""); + if (use_ssdiff) + cgit_ssdiff_header(); } static void filepair_cb(struct diff_filepair *pair) @@ -251,9 +255,14 @@ static void filepair_cb(struct diff_filepair *pair) unsigned long old_size = 0; unsigned long new_size = 0; int binary = 0; + linediff_fn print_line_fn = print_line; header(pair->one->sha1, pair->one->path, pair->one->mode, pair->two->sha1, pair->two->path, pair->two->mode); + if (use_ssdiff) { + cgit_ssdiff_header(); + print_line_fn = cgit_ssdiff_line_cb; + } if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { if (S_ISGITLINK(pair->one->mode)) print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); @@ -261,11 +270,13 @@ static void filepair_cb(struct diff_filepair *pair) print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); return; } - if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, - &new_size, &binary, print_line)) + if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, + &new_size, &binary, print_line_fn)) cgit_print_error("Error running diff"); if (binary) html("Binary files differ"); + if (use_ssdiff) + cgit_ssdiff_footer(); } void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix) diff --git a/ui-ssdiff.c b/ui-ssdiff.c new file mode 100644 index 0000000..3591ab4 --- /dev/null +++ b/ui-ssdiff.c @@ -0,0 +1,264 @@ +#include "cgit.h" +#include "html.h" +#include "ui-shared.h" + +extern int use_ssdiff; + +static int current_old_line, current_new_line; + +struct deferred_lines { + int line_no; + char *line; + struct deferred_lines *next; +}; + +static struct deferred_lines *deferred_old, *deferred_old_last; +static struct deferred_lines *deferred_new, *deferred_new_last; + +static int line_from_hunk(char *line, char type) +{ + char *buf1, *buf2; + int len; + + buf1 = strchr(line, type); + if (buf1 == NULL) + return 0; + buf1 += 1; + buf2 = strchr(buf1, ','); + if (buf2 == NULL) + return 0; + len = buf2 - buf1; + buf2 = xmalloc(len + 1); + strncpy(buf2, buf1, len); + buf2[len] = '\0'; + int res = atoi(buf2); + free(buf2); + return res; +} + +static char *replace_tabs(char *line) +{ + char *prev_buf = line; + char *cur_buf; + int linelen = strlen(line); + int n_tabs = 0; + int i; + char *result; + char *spaces = " "; + + if (linelen == 0) { + result = xmalloc(1); + result[0] = '\0'; + return result; + } + + for (i = 0; i < linelen; i++) + if (line[i] == '\t') + n_tabs += 1; + result = xmalloc(linelen + n_tabs * 8 + 1); + result[0] = '\0'; + + while (1) { + cur_buf = strchr(prev_buf, '\t'); + if (!cur_buf) { + strcat(result, prev_buf); + break; + } else { + strcat(result, " "); + strncat(result, spaces, 8 - (strlen(result) % 8)); + strncat(result, prev_buf, cur_buf - prev_buf); + } + prev_buf = cur_buf + 1; + } + return result; +} + +static void deferred_old_add(char *line, int line_no) +{ + struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); + item->line = xstrdup(line); + item->line_no = line_no; + item->next = NULL; + if (deferred_old) { + deferred_old_last->next = item; + deferred_old_last = item; + } else { + deferred_old = deferred_old_last = item; + } +} + +static void deferred_new_add(char *line, int line_no) +{ + struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); + item->line = xstrdup(line); + item->line_no = line_no; + item->next = NULL; + if (deferred_new) { + deferred_new_last->next = item; + deferred_new_last = item; + } else { + deferred_new = deferred_new_last = item; + } +} + +static void print_ssdiff_line(char *class, int old_line_no, char *old_line, + int new_line_no, char *new_line) +{ + html(""); + if (old_line_no > 0) + htmlf("%d ", class, + old_line_no, class); + else + htmlf(" ", class, class); + + if (old_line) { + old_line = replace_tabs(old_line + 1); + html_txt(old_line); + free(old_line); + } + + html(" "); + + if (new_line_no > 0) + htmlf(" %d ", class, + new_line_no, class); + else + htmlf(" ", class, class); + + if (new_line) { + new_line = replace_tabs(new_line + 1); + html_txt(new_line); + free(new_line); + } + + html(""); +} + +static void print_deferred_old_lines() +{ + struct deferred_lines *iter_old, *tmp; + + iter_old = deferred_old; + while (iter_old) { + print_ssdiff_line("del", iter_old->line_no, + iter_old->line, -1, NULL); + tmp = iter_old->next; + free(iter_old); + iter_old = tmp; + } +} + +static void print_deferred_new_lines() +{ + struct deferred_lines *iter_new, *tmp; + + iter_new = deferred_new; + while (iter_new) { + print_ssdiff_line("add", -1, NULL, iter_new->line_no, + iter_new->line); + tmp = iter_new->next; + free(iter_new); + iter_new = tmp; + } +} + +static void print_deferred_changed_lines() +{ + struct deferred_lines *iter_old, *iter_new, *tmp; + + iter_old = deferred_old; + iter_new = deferred_new; + while (iter_old || iter_new) { + if (iter_old && iter_new) + print_ssdiff_line("changed", iter_old->line_no, + iter_old->line, + iter_new->line_no, iter_new->line); + else if (iter_old) + print_ssdiff_line("changed", iter_old->line_no, + iter_old->line, -1, NULL); + else if (iter_new) + print_ssdiff_line("changed", -1, NULL, + iter_new->line_no, iter_new->line); + + if (iter_old) { + tmp = iter_old->next; + free(iter_old); + iter_old = tmp; + } + + if (iter_new) { + tmp = iter_new->next; + free(iter_new); + iter_new = tmp; + } + } +} + +void cgit_ssdiff_print_deferred_lines() +{ + if (!deferred_old && !deferred_new) + return; + + if (deferred_old && !deferred_new) + print_deferred_old_lines(); + else if (!deferred_old && deferred_new) + print_deferred_new_lines(); + else + print_deferred_changed_lines(); + + deferred_old = deferred_old_last = NULL; + deferred_new = deferred_new_last = NULL; +} + +/* + * print a single line returned from xdiff + */ +void cgit_ssdiff_line_cb(char *line, int len) +{ + char c = line[len - 1]; + + line[len - 1] = '\0'; + + if (line[0] == '@') { + current_old_line = line_from_hunk(line, '-'); + current_new_line = line_from_hunk(line, '+'); + } + + if (line[0] == ' ') { + if (deferred_old || deferred_new) + cgit_ssdiff_print_deferred_lines(); + print_ssdiff_line("ctx", current_old_line, line, + current_new_line, line); + current_old_line += 1; + current_new_line += 1; + } else if (line[0] == '+') { + deferred_new_add(line, current_new_line); + current_new_line += 1; + } else if (line[0] == '-') { + deferred_old_add(line, current_old_line); + current_old_line += 1; + } else if (line[0] == '@') { + html(""); + html_txt(line); + html(""); + } else { + html(""); + html_txt(line); + html(""); + } + line[len - 1] = c; +} + +void cgit_ssdiff_header() +{ + current_old_line = 0; + current_new_line = 0; + html(""); +} + +void cgit_ssdiff_footer() +{ + if (deferred_old || deferred_new) + cgit_ssdiff_print_deferred_lines(); + html("
"); +} diff --git a/ui-ssdiff.h b/ui-ssdiff.h new file mode 100644 index 0000000..a0b1efe --- /dev/null +++ b/ui-ssdiff.h @@ -0,0 +1,12 @@ +#ifndef UI_SSDIFF_H +#define UI_SSDIFF_H + +extern void cgit_ssdiff_print_deferred_lines(); + +extern void cgit_ssdiff_line_cb(char *line, int len); + +extern void cgit_ssdiff_header(); + +extern void cgit_ssdiff_footer(); + +#endif /* UI_SSDIFF_H */ From c358aa3dfebf4fc1f3005dd960aa5c1c020eed76 Mon Sep 17 00:00:00 2001 From: Ragnar Ouchterlony Date: Mon, 14 Sep 2009 20:19:02 +0200 Subject: [PATCH 2/5] Add possibility to switch between unidiff and side-by-side-diff. A new config option side-by-side-diffs added, defaulting to 0, meaning unidiff. Also a query option (ss) is used toggle this. In the commit page you can switch between the two diff formats by clicking on the link on the "commit"-row, to the right of (patch). In the diff page you can switch by using the link at the start of the page. All commit-links and diff-links will remember the choice. Signed-off-by: Ragnar Ouchterlony Signed-off-by: Lars Hjemli --- cgit.c | 5 +++++ cgit.h | 2 ++ cgitrc.5.txt | 4 ++++ ui-commit.c | 11 ++++++++--- ui-diff.c | 22 ++++++++++++++++++++-- ui-log.c | 4 ++-- ui-refs.c | 2 +- ui-shared.c | 34 ++++++++++++++++++++++++++++------ ui-shared.h | 5 +++-- 9 files changed, 73 insertions(+), 16 deletions(-) diff --git a/cgit.c b/cgit.c index bd37788..ff678fb 100644 --- a/cgit.c +++ b/cgit.c @@ -182,6 +182,8 @@ void config_cb(const char *name, const char *value) ctx.cfg.summary_branches = atoi(value); else if (!strcmp(name, "summary-tags")) ctx.cfg.summary_tags = atoi(value); + else if (!strcmp(name, "side-by-side-diffs")) + ctx.cfg.ssdiff = atoi(value); else if (!strcmp(name, "agefile")) ctx.cfg.agefile = xstrdup(value); else if (!strcmp(name, "renamelimit")) @@ -238,6 +240,8 @@ static void querystring_cb(const char *name, const char *value) ctx.qry.showmsg = atoi(value); } else if (!strcmp(name, "period")) { ctx.qry.period = xstrdup(value); + } else if (!strcmp(name, "ss")) { + ctx.qry.ssdiff = atoi(value); } } @@ -279,6 +283,7 @@ static void prepare_context(struct cgit_context *ctx) ctx->cfg.summary_branches = 10; ctx->cfg.summary_log = 10; ctx->cfg.summary_tags = 10; + ctx->cfg.ssdiff = 0; ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); ctx->env.https = xstrdupn(getenv("HTTPS")); diff --git a/cgit.h b/cgit.h index 6c6c460..b7b0adb 100644 --- a/cgit.h +++ b/cgit.h @@ -143,6 +143,7 @@ struct cgit_query { int nohead; char *sort; int showmsg; + int ssdiff; }; struct cgit_config { @@ -194,6 +195,7 @@ struct cgit_config { int summary_branches; int summary_log; int summary_tags; + int ssdiff; struct string_list mimetypes; struct cgit_filter *about_filter; struct cgit_filter *commit_filter; diff --git a/cgitrc.5.txt b/cgitrc.5.txt index 4dc383d..252d546 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -238,6 +238,10 @@ section:: after this option will inherit the current section name. Default value: none. +side-by-side-diffs:: + If set to "1" shows side-by-side diffs instead of unidiffs per + default. Default value: "0". + snapshots:: Text which specifies the default set of snapshot formats generated by cgit. The value is a space-separated list of zero or more of the diff --git a/ui-commit.c b/ui-commit.c index f5b0ae5..b5e3c01 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -58,9 +58,14 @@ void cgit_print_commit(char *hex) html("\n"); html("commit"); tmp = sha1_to_hex(commit->object.sha1); - cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp); + cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, 0); html(" ("); cgit_patch_link("patch", NULL, NULL, NULL, tmp); + html(") ("); + if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff)) + cgit_commit_link("unidiff", NULL, NULL, ctx.qry.head, tmp, 1); + else + cgit_commit_link("side-by-side diff", NULL, NULL, ctx.qry.head, tmp, 1); html(")\n"); html("tree"); tmp = xstrdup(hex); @@ -78,10 +83,10 @@ void cgit_print_commit(char *hex) html("parent" ""); cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, - ctx.qry.head, sha1_to_hex(p->item->object.sha1)); + ctx.qry.head, sha1_to_hex(p->item->object.sha1), 0); html(" ("); cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, - sha1_to_hex(p->item->object.sha1), NULL); + sha1_to_hex(p->item->object.sha1), NULL, 0); html(")"); parents++; } diff --git a/ui-diff.c b/ui-diff.c index 0c6f8d7..42e81ac 100644 --- a/ui-diff.c +++ b/ui-diff.c @@ -85,7 +85,7 @@ static void print_fileinfo(struct fileinfo *info) } htmlf("", class); cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, - ctx.qry.sha2, info->new_path); + ctx.qry.sha2, info->new_path, 0); if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) htmlf(" (%s from %s)", info->status == DIFF_STATUS_COPIED ? "copied" : "renamed", @@ -160,7 +160,7 @@ void cgit_print_diffstat(const unsigned char *old_sha1, html("
"); cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1, - ctx.qry.sha2, NULL); + ctx.qry.sha2, NULL, 0); html("
"); html(""); max_changes = 0; @@ -250,6 +250,19 @@ static void header(unsigned char *sha1, char *path1, int mode1, cgit_ssdiff_header(); } +static void print_ssdiff_link() +{ + if (!strcmp(ctx.qry.page, "diff")) { + if (use_ssdiff) + cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head, + ctx.qry.sha1, ctx.qry.sha2, NULL, 1); + else + cgit_diff_link("Side-by-side diff", NULL, NULL, + ctx.qry.head, ctx.qry.sha1, + ctx.qry.sha2, NULL, 1); + } +} + static void filepair_cb(struct diff_filepair *pair) { unsigned long old_size = 0; @@ -314,6 +327,11 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefi if (!commit2 || parse_commit(commit2)) cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(old_rev_sha1))); } + + if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff)) + use_ssdiff = 1; + + print_ssdiff_link(); cgit_print_diffstat(old_rev_sha1, new_rev_sha1); html("
"); diff --git a/ui-log.c b/ui-log.c index f3132c9..0947604 100644 --- a/ui-log.c +++ b/ui-log.c @@ -66,7 +66,7 @@ void show_commit_decorations(struct commit *commit) else { strncpy(buf, deco->name, sizeof(buf) - 1); cgit_commit_link(buf, NULL, "deco", ctx.qry.head, - sha1_to_hex(commit->object.sha1)); + sha1_to_hex(commit->object.sha1), 0); } deco = deco->next; } @@ -89,7 +89,7 @@ void print_commit(struct commit *commit) htmlf("", ctx.qry.showmsg ? " class='logsubject'" : ""); cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, - sha1_to_hex(commit->object.sha1)); + sha1_to_hex(commit->object.sha1), 0); show_commit_decorations(commit); html("
"); html_txt(info->author); diff --git a/ui-refs.c b/ui-refs.c index d3b4f6e..33d9bec 100644 --- a/ui-refs.c +++ b/ui-refs.c @@ -74,7 +74,7 @@ static int print_branch(struct refinfo *ref) html(""); if (ref->object->type == OBJ_COMMIT) { - cgit_commit_link(info->subject, NULL, NULL, name, NULL); + cgit_commit_link(info->subject, NULL, NULL, name, NULL, 0); html(""); html_txt(info->author); html(""); diff --git a/ui-shared.c b/ui-shared.c index 07d5dd4..de55eff 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -317,7 +317,7 @@ void cgit_log_link(char *name, char *title, char *class, char *head, } void cgit_commit_link(char *name, char *title, char *class, char *head, - char *rev) + char *rev, int toggle_ssdiff) { if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { name[ctx.cfg.max_msg_len] = '\0'; @@ -325,7 +325,23 @@ void cgit_commit_link(char *name, char *title, char *class, char *head, name[ctx.cfg.max_msg_len - 2] = '.'; name[ctx.cfg.max_msg_len - 3] = '.'; } - reporevlink("commit", name, title, class, head, rev, NULL); + + char *delim; + + delim = repolink(title, class, "commit", head, NULL); + if (rev && strcmp(rev, ctx.qry.head)) { + html(delim); + html("id="); + html_url_arg(rev); + delim = "&"; + } + if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) { + html(delim); + html("ss=1"); + } + html("'>"); + html_txt(name); + html(""); } void cgit_refs_link(char *name, char *title, char *class, char *head, @@ -341,7 +357,8 @@ void cgit_snapshot_link(char *name, char *title, char *class, char *head, } void cgit_diff_link(char *name, char *title, char *class, char *head, - char *new_rev, char *old_rev, char *path) + char *new_rev, char *old_rev, char *path, + int toggle_ssdiff) { char *delim; @@ -356,6 +373,11 @@ void cgit_diff_link(char *name, char *title, char *class, char *head, html(delim); html("id2="); html_url_arg(old_rev); + delim = "&"; + } + if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) { + html(delim); + html("ss=1"); } html("'>"); html_txt(name); @@ -383,7 +405,7 @@ void cgit_object_link(struct object *obj) shortrev[10] = '\0'; if (obj->type == OBJ_COMMIT) { cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL, - ctx.qry.head, fullrev); + ctx.qry.head, fullrev, 0); return; } else if (obj->type == OBJ_TREE) page = "tree"; @@ -695,9 +717,9 @@ void cgit_print_pageheader(struct cgit_context *ctx) cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head, ctx->qry.sha1, NULL); cgit_commit_link("commit", NULL, hc(cmd, "commit"), - ctx->qry.head, ctx->qry.sha1); + ctx->qry.head, ctx->qry.sha1, 0); cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, - ctx->qry.sha1, ctx->qry.sha2, NULL); + ctx->qry.sha1, ctx->qry.sha2, NULL, 0); if (ctx->repo->max_stats) cgit_stats_link("stats", NULL, hc(cmd, "stats"), ctx->qry.head, NULL); diff --git a/ui-shared.h b/ui-shared.h index bff4826..166246d 100644 --- a/ui-shared.h +++ b/ui-shared.h @@ -22,7 +22,7 @@ extern void cgit_log_link(char *name, char *title, char *class, char *head, char *rev, char *path, int ofs, char *grep, char *pattern, int showmsg); extern void cgit_commit_link(char *name, char *title, char *class, char *head, - char *rev); + char *rev, int toggle_ssdiff); extern void cgit_patch_link(char *name, char *title, char *class, char *head, char *rev); extern void cgit_refs_link(char *name, char *title, char *class, char *head, @@ -30,7 +30,8 @@ extern void cgit_refs_link(char *name, char *title, char *class, char *head, extern void cgit_snapshot_link(char *name, char *title, char *class, char *head, char *rev, char *archivename); extern void cgit_diff_link(char *name, char *title, char *class, char *head, - char *new_rev, char *old_rev, char *path); + char *new_rev, char *old_rev, char *path, + int toggle_ssdiff); extern void cgit_stats_link(char *name, char *title, char *class, char *head, char *path); extern void cgit_object_link(struct object *obj); From 207cc34711039329b41345f716bf421a88a6fd0a Mon Sep 17 00:00:00 2001 From: Ragnar Ouchterlony Date: Tue, 15 Sep 2009 19:44:37 +0200 Subject: [PATCH 3/5] Polishing of how the side-by-side diff looks. Aligned all different files, so that all side-by-side tables look the same. Also made sure that the tables take up the whole browser width. Also various changes to the css to make things easier on the eye. Signed-off-by: Ragnar Ouchterlony Signed-off-by: Lars Hjemli --- cgit.css | 66 ++++++++++++++++++++++++++++++++++++++++++++++++----- ui-diff.c | 27 +++++++++++++--------- ui-ssdiff.c | 30 ++++++++++++++---------- ui-ssdiff.h | 3 ++- 4 files changed, 96 insertions(+), 30 deletions(-) diff --git a/cgit.css b/cgit.css index bf58b8a..3f37165 100644 --- a/cgit.css +++ b/cgit.css @@ -602,37 +602,91 @@ table.hgraph div.bar { height: 1em; } +table.ssdiff { + width: 100%; +} + +table.ssdiff td { + font-size: 75%; + font-family: monospace; + white-space: pre; + padding: 1px 4px 1px 4px; + border-left: solid 1px #aaa; + border-right: solid 1px #aaa; +} + table.ssdiff td.add { color: black; - background: #afa; + background: #cfc; + min-width: 50%; } table.ssdiff td.add_dark { color: black; - background: #9c9; + background: #aca; + min-width: 50%; } table.ssdiff td.del { color: black; - background: #faa; + background: #fcc; + min-width: 50%; } table.ssdiff td.del_dark { color: black; - background: #c99; + background: #caa; + min-width: 50%; } table.ssdiff td.changed { color: black; - background: #ffa; + background: #ffc; + min-width: 50%; } table.ssdiff td.changed_dark { color: black; - background: #cc9; + background: #cca; + min-width: 50%; +} + +table.ssdiff td.lineno { + color: black; + background: #eee; + text-align: right; + width: 3em; + min-width: 3em; } table.ssdiff td.hunk { color: #black; background: #ccf; + border-top: solid 1px #aaa; + border-bottom: solid 1px #aaa; } + +table.ssdiff td.head { + border-top: solid 1px #aaa; + border-bottom: solid 1px #aaa; +} + +table.ssdiff td.head div.head { + font-weight: bold; + color: black; +} + +table.ssdiff td.foot { + border-top: solid 1px #aaa; + border-left: none; + border-right: none; + border-bottom: none; +} + +table.ssdiff td.space { + border: none; +} + +table.ssdiff td.space div { + min-height: 3em; +} \ No newline at end of file diff --git a/ui-diff.c b/ui-diff.c index 42e81ac..b21c2c1 100644 --- a/ui-diff.c +++ b/ui-diff.c @@ -246,8 +246,6 @@ static void header(unsigned char *sha1, char *path1, int mode1, html_txt(path2); } html(""); - if (use_ssdiff) - cgit_ssdiff_header(); } static void print_ssdiff_link() @@ -270,24 +268,26 @@ static void filepair_cb(struct diff_filepair *pair) int binary = 0; linediff_fn print_line_fn = print_line; - header(pair->one->sha1, pair->one->path, pair->one->mode, - pair->two->sha1, pair->two->path, pair->two->mode); if (use_ssdiff) { - cgit_ssdiff_header(); + cgit_ssdiff_header_begin(); print_line_fn = cgit_ssdiff_line_cb; } + header(pair->one->sha1, pair->one->path, pair->one->mode, + pair->two->sha1, pair->two->path, pair->two->mode); + if (use_ssdiff) + cgit_ssdiff_header_end(); if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { if (S_ISGITLINK(pair->one->mode)) - print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); + print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); if (S_ISGITLINK(pair->two->mode)) - print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); + print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); return; } if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size, &binary, print_line_fn)) cgit_print_error("Error running diff"); if (binary) - html("Binary files differ"); + print_line_fn(" Binary files differ", 20); if (use_ssdiff) cgit_ssdiff_footer(); } @@ -334,9 +334,14 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefi print_ssdiff_link(); cgit_print_diffstat(old_rev_sha1, new_rev_sha1); - html(""); - html(""); if (old_line_no > 0) - htmlf(""); + html(""); if (new_line_no > 0) - htmlf(""); } diff --git a/ui-ssdiff.h b/ui-ssdiff.h index a0b1efe..64b4b12 100644 --- a/ui-ssdiff.h +++ b/ui-ssdiff.h @@ -5,7 +5,8 @@ extern void cgit_ssdiff_print_deferred_lines(); extern void cgit_ssdiff_line_cb(char *line, int len); -extern void cgit_ssdiff_header(); +extern void cgit_ssdiff_header_begin(); +extern void cgit_ssdiff_header_end(); extern void cgit_ssdiff_footer(); From 4a198e4b8ee62a9a8b5156a46bfce46dc7223fe9 Mon Sep 17 00:00:00 2001 From: Ragnar Ouchterlony Date: Wed, 16 Sep 2009 18:56:26 +0200 Subject: [PATCH 4/5] Fixed side-by-side diff bugs related to binary diff and more. The fixed bugs: * "Binary files differ" did not show up either in unidiff or side-by-side-diff. * Subproject diffs did not work for side-by-side diffs. * The ssdiff link on diff pages did not conserve the path. Signed-off-by: Ragnar Ouchterlony Signed-off-by: Lars Hjemli --- ui-diff.c | 14 ++++++++++---- ui-ssdiff.c | 8 ++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/ui-diff.c b/ui-diff.c index b21c2c1..a92a768 100644 --- a/ui-diff.c +++ b/ui-diff.c @@ -253,11 +253,11 @@ static void print_ssdiff_link() if (!strcmp(ctx.qry.page, "diff")) { if (use_ssdiff) cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head, - ctx.qry.sha1, ctx.qry.sha2, NULL, 1); + ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1); else cgit_diff_link("Side-by-side diff", NULL, NULL, ctx.qry.head, ctx.qry.sha1, - ctx.qry.sha2, NULL, 1); + ctx.qry.sha2, ctx.qry.path, 1); } } @@ -281,13 +281,19 @@ static void filepair_cb(struct diff_filepair *pair) print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); if (S_ISGITLINK(pair->two->mode)) print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); + if (use_ssdiff) + cgit_ssdiff_footer(); return; } if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size, &binary, print_line_fn)) cgit_print_error("Error running diff"); - if (binary) - print_line_fn(" Binary files differ", 20); + if (binary) { + if (use_ssdiff) + html(""); + else + html("Binary files differ"); + } if (use_ssdiff) cgit_ssdiff_footer(); } diff --git a/ui-ssdiff.c b/ui-ssdiff.c index 8215051..5673642 100644 --- a/ui-ssdiff.c +++ b/ui-ssdiff.c @@ -108,6 +108,8 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, if (old_line_no > 0) htmlf(""); html(""); if (old_line_no > 0) htmlf(""); - if (new_line_no > 0) htmlf(""); + if (lcs) + free(lcs); + if (new_line) + free(new_line); + if (old_line) + free(old_line); } static void print_deferred_old_lines() { struct deferred_lines *iter_old, *tmp; - iter_old = deferred_old; while (iter_old) { print_ssdiff_line("del", iter_old->line_no, - iter_old->line, -1, NULL); + iter_old->line, -1, NULL, 0); tmp = iter_old->next; free(iter_old); iter_old = tmp; @@ -155,11 +252,10 @@ static void print_deferred_old_lines() static void print_deferred_new_lines() { struct deferred_lines *iter_new, *tmp; - iter_new = deferred_new; while (iter_new) { - print_ssdiff_line("add", -1, NULL, iter_new->line_no, - iter_new->line); + print_ssdiff_line("add", -1, NULL, + iter_new->line_no, iter_new->line, 0); tmp = iter_new->next; free(iter_new); iter_new = tmp; @@ -169,6 +265,9 @@ static void print_deferred_new_lines() static void print_deferred_changed_lines() { struct deferred_lines *iter_old, *iter_new, *tmp; + int n_old_lines = calc_deferred_lines(deferred_old); + int n_new_lines = calc_deferred_lines(deferred_new); + int individual_chars = (n_old_lines == n_new_lines ? 1 : 0); iter_old = deferred_old; iter_new = deferred_new; @@ -176,14 +275,14 @@ static void print_deferred_changed_lines() if (iter_old && iter_new) print_ssdiff_line("changed", iter_old->line_no, iter_old->line, - iter_new->line_no, iter_new->line); + iter_new->line_no, iter_new->line, + individual_chars); else if (iter_old) print_ssdiff_line("changed", iter_old->line_no, - iter_old->line, -1, NULL); + iter_old->line, -1, NULL, 0); else if (iter_new) print_ssdiff_line("changed", -1, NULL, - iter_new->line_no, iter_new->line); - + iter_new->line_no, iter_new->line, 0); if (iter_old) { tmp = iter_old->next; free(iter_old); @@ -202,14 +301,12 @@ void cgit_ssdiff_print_deferred_lines() { if (!deferred_old && !deferred_new) return; - if (deferred_old && !deferred_new) print_deferred_old_lines(); else if (!deferred_old && deferred_new) print_deferred_new_lines(); else print_deferred_changed_lines(); - deferred_old = deferred_old_last = NULL; deferred_new = deferred_new_last = NULL; } @@ -220,9 +317,7 @@ void cgit_ssdiff_print_deferred_lines() void cgit_ssdiff_line_cb(char *line, int len) { char c = line[len - 1]; - line[len - 1] = '\0'; - if (line[0] == '@') { current_old_line = line_from_hunk(line, '-'); current_new_line = line_from_hunk(line, '+'); @@ -232,7 +327,7 @@ void cgit_ssdiff_line_cb(char *line, int len) if (deferred_old || deferred_new) cgit_ssdiff_print_deferred_lines(); print_ssdiff_line("ctx", current_old_line, line, - current_new_line, line); + current_new_line, line, 0); current_old_line += 1; current_new_line += 1; } else if (line[0] == '+') {
"); + if (use_ssdiff) { + html(""); + } else { + html("
"); + html(""); + if (!use_ssdiff) + html(""); html("
"); + } cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix); - html("
"); } diff --git a/ui-ssdiff.c b/ui-ssdiff.c index 3591ab4..8215051 100644 --- a/ui-ssdiff.c +++ b/ui-ssdiff.c @@ -40,9 +40,9 @@ static char *replace_tabs(char *line) { char *prev_buf = line; char *cur_buf; - int linelen = strlen(line); + int linelen = strlen(line); int n_tabs = 0; - int i; + int i; char *result; char *spaces = " "; @@ -52,10 +52,10 @@ static char *replace_tabs(char *line) return result; } - for (i = 0; i < linelen; i++) + for (i = 0; i < linelen; i++) if (line[i] == '\t') n_tabs += 1; - result = xmalloc(linelen + n_tabs * 8 + 1); + result = xmalloc(linelen + n_tabs * 8 + 1); result[0] = '\0'; while (1) { @@ -106,10 +106,10 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, { html("
%d ", class, + htmlf("%d", old_line_no, class); else - htmlf(" ", class, class); + htmlf("", class); if (old_line) { old_line = replace_tabs(old_line + 1); @@ -117,13 +117,13 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, free(old_line); } - html(" %d ", class, + htmlf("%d", new_line_no, class); else - htmlf(" ", class, class); + htmlf("", class); if (new_line) { new_line = replace_tabs(new_line + 1); @@ -249,16 +249,22 @@ void cgit_ssdiff_line_cb(char *line, int len) line[len - 1] = c; } -void cgit_ssdiff_header() +void cgit_ssdiff_header_begin() { current_old_line = 0; current_new_line = 0; - html(""); + html(""); + html(""); } void cgit_ssdiff_footer() { if (deferred_old || deferred_new) cgit_ssdiff_print_deferred_lines(); - html("
"); +} + +void cgit_ssdiff_header_end() +{ + html("
"); + html("
Binary files differ
%d", old_line_no, class); + else if (old_line) + htmlf("", class); else htmlf("", class); @@ -122,6 +124,8 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, if (new_line_no > 0) htmlf("%d", new_line_no, class); + else if (new_line) + htmlf("", class); else htmlf("", class); @@ -251,8 +255,8 @@ void cgit_ssdiff_line_cb(char *line, int len) void cgit_ssdiff_header_begin() { - current_old_line = 0; - current_new_line = 0; + current_old_line = -1; + current_new_line = -1; html("
"); } From 735e15e38a484bf0daa98776fa7cde270a271cda Mon Sep 17 00:00:00 2001 From: Ragnar Ouchterlony Date: Sun, 25 Oct 2009 18:13:22 +0100 Subject: [PATCH 5/5] In side-by-side diff, add support for marking individual characters. Refuses to do so if the left hand side of the diff has different amount of differing lines to the right hand side to avoid confusion. Note that I use the naive dynamic programming approach for calculating the longest common subsequence. We could probably be more efficient by using a better algorithm. The LCS calculating function is O(n*m) and uses up n*m amount of memory too (so if we we compare two strings of length 100, I use an array of 10000 for calculating the LCS). Might want to not calculate LCS if the length of the line is too large. Signed-off-by: Ragnar Ouchterlony --- cgit.css | 10 ++++ ui-ssdiff.c | 145 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 130 insertions(+), 25 deletions(-) diff --git a/cgit.css b/cgit.css index 3f37165..9e6d2a4 100644 --- a/cgit.css +++ b/cgit.css @@ -627,6 +627,11 @@ table.ssdiff td.add_dark { min-width: 50%; } +table.ssdiff span.add { + background: #cfc; + font-weight: bold; +} + table.ssdiff td.del { color: black; background: #fcc; @@ -639,6 +644,11 @@ table.ssdiff td.del_dark { min-width: 50%; } +table.ssdiff span.del { + background: #fcc; + font-weight: bold; +} + table.ssdiff td.changed { color: black; background: #ffc; diff --git a/ui-ssdiff.c b/ui-ssdiff.c index 5673642..408e620 100644 --- a/ui-ssdiff.c +++ b/ui-ssdiff.c @@ -15,6 +15,52 @@ struct deferred_lines { static struct deferred_lines *deferred_old, *deferred_old_last; static struct deferred_lines *deferred_new, *deferred_new_last; +static char *longest_common_subsequence(char *A, char *B) +{ + int i, j, ri; + int m = strlen(A); + int n = strlen(B); + int L[m + 1][n + 1]; + int tmp1, tmp2; + int lcs_length; + char *result; + + for (i = m; i >= 0; i--) { + for (j = n; j >= 0; j--) { + if (A[i] == '\0' || B[j] == '\0') { + L[i][j] = 0; + } else if (A[i] == B[j]) { + L[i][j] = 1 + L[i + 1][j + 1]; + } else { + tmp1 = L[i + 1][j]; + tmp2 = L[i][j + 1]; + L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2); + } + } + } + + lcs_length = L[0][0]; + result = xmalloc(lcs_length + 2); + memset(result, 0, sizeof(*result) * (lcs_length + 2)); + + ri = 0; + i = 0; + j = 0; + while (i < m && j < n) { + if (A[i] == B[j]) { + result[ri] = A[i]; + ri += 1; + i += 1; + j += 1; + } else if (L[i + 1][j] >= L[i][j + 1]) { + i += 1; + } else { + j += 1; + } + } + return result; +} + static int line_from_hunk(char *line, char type) { char *buf1, *buf2; @@ -73,6 +119,17 @@ static char *replace_tabs(char *line) return result; } +static int calc_deferred_lines(struct deferred_lines *start) +{ + struct deferred_lines *item = start; + int result = 0; + while (item) { + result += 1; + item = item->next; + } + return result; +} + static void deferred_old_add(char *line, int line_no) { struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); @@ -101,9 +158,45 @@ static void deferred_new_add(char *line, int line_no) } } -static void print_ssdiff_line(char *class, int old_line_no, char *old_line, - int new_line_no, char *new_line) +static void print_part_with_lcs(char *class, char *line, char *lcs) { + int line_len = strlen(line); + int i, j; + char c[2] = " "; + int same = 1; + + j = 0; + for (i = 0; i < line_len; i++) { + c[0] = line[i]; + if (same) { + if (line[i] == lcs[j]) + j += 1; + else { + same = 0; + htmlf("", class); + } + } else if (line[i] == lcs[j]) { + same = 1; + htmlf(""); + j += 1; + } + html_txt(c); + } +} + +static void print_ssdiff_line(char *class, + int old_line_no, + char *old_line, + int new_line_no, + char *new_line, int individual_chars) +{ + char *lcs = NULL; + if (old_line) + old_line = replace_tabs(old_line + 1); + if (new_line) + new_line = replace_tabs(new_line + 1); + if (individual_chars && old_line && new_line) + lcs = longest_common_subsequence(old_line, new_line); html("
%d", @@ -112,15 +205,14 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, htmlf("", class); else htmlf("", class); - if (old_line) { - old_line = replace_tabs(old_line + 1); - html_txt(old_line); - free(old_line); + if (lcs) + print_part_with_lcs("del", old_line, lcs); + else + html_txt(old_line); } html("%d", new_line_no, class); @@ -128,24 +220,29 @@ static void print_ssdiff_line(char *class, int old_line_no, char *old_line, htmlf("", class); else htmlf("", class); - if (new_line) { - new_line = replace_tabs(new_line + 1); - html_txt(new_line); - free(new_line); + if (lcs) + print_part_with_lcs("add", new_line, lcs); + else + html_txt(new_line); } html("