Merge branch 'ro/ssdiff'
此提交包含在:
		
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							| @@ -93,6 +93,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 | ||||
|   | ||||
							
								
								
									
										5
									
								
								cgit.c
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								cgit.c
									
									
									
									
									
								
							| @@ -184,6 +184,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")) | ||||
| @@ -242,6 +244,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); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -284,6 +288,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")); | ||||
|   | ||||
							
								
								
									
										99
									
								
								cgit.css
									
									
									
									
									
								
							
							
						
						
									
										99
									
								
								cgit.css
									
									
									
									
									
								
							| @@ -606,3 +606,102 @@ table.hgraph div.bar { | ||||
| 	background-color: #eee; | ||||
| 	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: #cfc; | ||||
| 	min-width: 50%; | ||||
| } | ||||
|  | ||||
| table.ssdiff td.add_dark { | ||||
| 	color: black; | ||||
| 	background: #aca; | ||||
| 	min-width: 50%; | ||||
| } | ||||
|  | ||||
| table.ssdiff span.add { | ||||
| 	background: #cfc; | ||||
| 	font-weight: bold; | ||||
| } | ||||
|  | ||||
| table.ssdiff td.del { | ||||
| 	color: black; | ||||
| 	background: #fcc; | ||||
| 	min-width: 50%; | ||||
| } | ||||
|  | ||||
| table.ssdiff td.del_dark { | ||||
| 	color: black; | ||||
| 	background: #caa; | ||||
| 	min-width: 50%; | ||||
| } | ||||
|  | ||||
| table.ssdiff span.del { | ||||
| 	background: #fcc; | ||||
| 	font-weight: bold; | ||||
| } | ||||
|  | ||||
| table.ssdiff td.changed { | ||||
| 	color: black; | ||||
| 	background: #ffc; | ||||
| 	min-width: 50%; | ||||
| } | ||||
|  | ||||
| table.ssdiff td.changed_dark { | ||||
| 	color: black; | ||||
| 	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; | ||||
| } | ||||
							
								
								
									
										2
									
								
								cgit.h
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								cgit.h
									
									
									
									
									
								
							| @@ -143,6 +143,7 @@ struct cgit_query { | ||||
| 	int nohead; | ||||
| 	char *sort; | ||||
| 	int showmsg; | ||||
| 	int ssdiff; | ||||
| }; | ||||
|  | ||||
| struct cgit_config { | ||||
| @@ -195,6 +196,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; | ||||
|   | ||||
| @@ -245,6 +245,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 | ||||
|   | ||||
							
								
								
									
										11
									
								
								ui-commit.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								ui-commit.c
									
									
									
									
									
								
							| @@ -58,9 +58,14 @@ void cgit_print_commit(char *hex) | ||||
| 	html("</td></tr>\n"); | ||||
| 	html("<tr><th>commit</th><td colspan='2' class='sha1'>"); | ||||
| 	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(")</td></tr>\n"); | ||||
| 	html("<tr><th>tree</th><td colspan='2' class='sha1'>"); | ||||
| 	tmp = xstrdup(hex); | ||||
| @@ -78,10 +83,10 @@ void cgit_print_commit(char *hex) | ||||
| 		html("<tr><th>parent</th>" | ||||
| 		     "<td colspan='2' class='sha1'>"); | ||||
| 		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(")</td></tr>"); | ||||
| 		parents++; | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										62
									
								
								ui-diff.c
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								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) | ||||
| { | ||||
| @@ -83,7 +85,7 @@ static void print_fileinfo(struct fileinfo *info) | ||||
| 	} | ||||
| 	htmlf("</td><td class='%s'>", 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", | ||||
| @@ -158,7 +160,7 @@ void cgit_print_diffstat(const unsigned char *old_sha1, | ||||
|  | ||||
| 	html("<div class='diffstat-header'>"); | ||||
| 	cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1, | ||||
| 		       ctx.qry.sha2, NULL); | ||||
| 		       ctx.qry.sha2, NULL, 0); | ||||
| 	html("</div>"); | ||||
| 	html("<table summary='diffstat' class='diffstat'>"); | ||||
| 	max_changes = 0; | ||||
| @@ -246,26 +248,54 @@ static void header(unsigned char *sha1, char *path1, int mode1, | ||||
| 	html("</div>"); | ||||
| } | ||||
|  | ||||
| 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, ctx.qry.path, 1); | ||||
| 		else | ||||
| 			cgit_diff_link("Side-by-side diff", NULL, NULL, | ||||
| 				       ctx.qry.head, ctx.qry.sha1, | ||||
| 				       ctx.qry.sha2, ctx.qry.path, 1); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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; | ||||
|  | ||||
| 	if (use_ssdiff) { | ||||
| 		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); | ||||
| 		if (use_ssdiff) | ||||
| 			cgit_ssdiff_footer(); | ||||
| 		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 (binary) { | ||||
| 		if (use_ssdiff) | ||||
| 			html("<tr><td colspan='4'>Binary files differ</td></tr>"); | ||||
| 		else | ||||
| 			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) | ||||
| @@ -303,11 +333,21 @@ 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("<table summary='diff' class='diff'>"); | ||||
| 	html("<tr><td>"); | ||||
| 	if (use_ssdiff) { | ||||
| 		html("<table summary='ssdiff' class='ssdiff'>"); | ||||
| 	} else { | ||||
| 		html("<table summary='diff' class='diff'>"); | ||||
| 		html("<tr><td>"); | ||||
| 	} | ||||
| 	cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix); | ||||
| 	html("</td></tr>"); | ||||
| 	if (!use_ssdiff) | ||||
| 		html("</td></tr>"); | ||||
| 	html("</table>"); | ||||
| } | ||||
|   | ||||
							
								
								
									
										4
									
								
								ui-log.c
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								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("</td><td%s>", | ||||
| 		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("</td><td>"); | ||||
| 	html_txt(info->author); | ||||
|   | ||||
| @@ -74,7 +74,7 @@ static int print_branch(struct refinfo *ref) | ||||
| 	html("</td><td>"); | ||||
|  | ||||
| 	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("</td><td>"); | ||||
| 		html_txt(info->author); | ||||
| 		html("</td><td colspan='2'>"); | ||||
|   | ||||
							
								
								
									
										34
									
								
								ui-shared.c
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								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("</a>"); | ||||
| } | ||||
|  | ||||
| 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); | ||||
|   | ||||
| @@ -23,7 +23,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, | ||||
| @@ -31,7 +31,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); | ||||
|   | ||||
							
								
								
									
										369
									
								
								ui-ssdiff.c
									
									
									
									
									
										一般檔案
									
								
							
							
						
						
									
										369
									
								
								ui-ssdiff.c
									
									
									
									
									
										一般檔案
									
								
							| @@ -0,0 +1,369 @@ | ||||
| #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 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; | ||||
| 	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 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)); | ||||
| 	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_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("<span class='%s'>", class); | ||||
| 			} | ||||
| 		} else if (line[i] == lcs[j]) { | ||||
| 			same = 1; | ||||
| 			htmlf("</span>"); | ||||
| 			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("<tr>"); | ||||
| 	if (old_line_no > 0) | ||||
| 		htmlf("<td class='lineno'>%d</td><td class='%s'>", | ||||
| 		      old_line_no, class); | ||||
| 	else if (old_line) | ||||
| 		htmlf("<td class='lineno'></td><td class='%s'>", class); | ||||
| 	else | ||||
| 		htmlf("<td class='lineno'></td><td class='%s_dark'>", class); | ||||
| 	if (old_line) { | ||||
| 		if (lcs) | ||||
| 			print_part_with_lcs("del", old_line, lcs); | ||||
| 		else | ||||
| 			html_txt(old_line); | ||||
| 	} | ||||
|  | ||||
| 	html("</td>"); | ||||
| 	if (new_line_no > 0) | ||||
| 		htmlf("<td class='lineno'>%d</td><td class='%s'>", | ||||
| 		      new_line_no, class); | ||||
| 	else if (new_line) | ||||
| 		htmlf("<td class='lineno'></td><td class='%s'>", class); | ||||
| 	else | ||||
| 		htmlf("<td class='lineno'></td><td class='%s_dark'>", class); | ||||
| 	if (new_line) { | ||||
| 		if (lcs) | ||||
| 			print_part_with_lcs("add", new_line, lcs); | ||||
| 		else | ||||
| 			html_txt(new_line); | ||||
| 	} | ||||
|  | ||||
| 	html("</td></tr>"); | ||||
| 	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, 0); | ||||
| 		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, 0); | ||||
| 		tmp = iter_new->next; | ||||
| 		free(iter_new); | ||||
| 		iter_new = tmp; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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; | ||||
| 	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, | ||||
| 					  individual_chars); | ||||
| 		else if (iter_old) | ||||
| 			print_ssdiff_line("changed", iter_old->line_no, | ||||
| 					  iter_old->line, -1, NULL, 0); | ||||
| 		else if (iter_new) | ||||
| 			print_ssdiff_line("changed", -1, NULL, | ||||
| 					  iter_new->line_no, iter_new->line, 0); | ||||
| 		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, 0); | ||||
| 		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("<tr><td colspan='4' class='hunk'>"); | ||||
| 		html_txt(line); | ||||
| 		html("</td></tr>"); | ||||
| 	} else { | ||||
| 		html("<tr><td colspan='4' class='ctx'>"); | ||||
| 		html_txt(line); | ||||
| 		html("</td></tr>"); | ||||
| 	} | ||||
| 	line[len - 1] = c; | ||||
| } | ||||
|  | ||||
| void cgit_ssdiff_header_begin() | ||||
| { | ||||
| 	current_old_line = -1; | ||||
| 	current_new_line = -1; | ||||
| 	html("<tr><td class='space' colspan='4'><div></div></td></tr>"); | ||||
| 	html("<tr><td class='head' colspan='4'>"); | ||||
| } | ||||
|  | ||||
| void cgit_ssdiff_header_end() | ||||
| { | ||||
| 	html("</td><tr>"); | ||||
| } | ||||
|  | ||||
| void cgit_ssdiff_footer() | ||||
| { | ||||
| 	if (deferred_old || deferred_new) | ||||
| 		cgit_ssdiff_print_deferred_lines(); | ||||
| 	html("<tr><td class='foot' colspan='4'></td></tr>"); | ||||
| } | ||||
							
								
								
									
										13
									
								
								ui-ssdiff.h
									
									
									
									
									
										一般檔案
									
								
							
							
						
						
									
										13
									
								
								ui-ssdiff.h
									
									
									
									
									
										一般檔案
									
								
							| @@ -0,0 +1,13 @@ | ||||
| #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_begin(); | ||||
| extern void cgit_ssdiff_header_end(); | ||||
|  | ||||
| extern void cgit_ssdiff_footer(); | ||||
|  | ||||
| #endif /* UI_SSDIFF_H */ | ||||
		新增問題並參考
	
	封鎖使用者
	 Lars Hjemli
					Lars Hjemli