ctucx.git: stagit

ctucx' stagit fork

commit 914880f31b04222fa2815f4f35543b72f881d5e4
parent 0bc47da0f7b66614cdf499755ceca1dc13ff91cd
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Sat, 30 Apr 2016 11:54:33 +0200

optimization: read stats once and remember it

for an initial run and new commits this speeds stagit up a bit:
on an initial run of sbase goes from about 4 seconds to 2.8 on my machine.

now we can't use statsbuf, so create the stats string ourselves, while at it
color the + and - using a style (can be disabled for the color-haters out
there ;)).
2 files changed, 149 insertions(+), 39 deletions(-)
M
stagit.c
|
186
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
M
style.css
|
2
++
diff --git a/stagit.c b/stagit.c
@@ -15,6 +15,13 @@
 #include "compat.h"
 #include "config.h"
 
+struct deltainfo {
+	git_patch *patch;
+
+	size_t addcount;
+	size_t delcount;
+};
+
 struct commitinfo {
 	const git_oid *id;
 

@@ -25,16 +32,18 @@ struct commitinfo {
 	const char          *summary;
 	const char          *msg;
 
-	git_diff_stats *stats;
-	git_diff       *diff;
-	git_commit     *commit;
-	git_commit     *parent;
-	git_tree       *commit_tree;
-	git_tree       *parent_tree;
+	git_diff   *diff;
+	git_commit *commit;
+	git_commit *parent;
+	git_tree   *commit_tree;
+	git_tree   *parent_tree;
 
 	size_t addcount;
 	size_t delcount;
 	size_t filecount;
+
+	struct deltainfo **deltas;
+	size_t ndeltas;
 };
 
 static git_repository *repo;

@@ -49,12 +58,95 @@ static char cloneurl[1024];
 static int haslicense, hasreadme, hassubmodules;
 
 void
+deltainfo_free(struct deltainfo *di)
+{
+	if (!di)
+		return;
+	git_patch_free(di->patch);
+	di->patch = NULL;
+	free(di);
+}
+
+int
+commitinfo_getstats(struct commitinfo *ci)
+{
+	struct deltainfo *di;
+	const git_diff_delta *delta;
+	const git_diff_hunk *hunk;
+	const git_diff_line *line;
+	git_patch *patch = NULL;
+	size_t ndeltas, nhunks, nhunklines;
+	size_t i, j, k;
+
+	ndeltas = git_diff_num_deltas(ci->diff);
+	if (ndeltas && !(ci->deltas = calloc(ndeltas, sizeof(struct deltainfo *))))
+		err(1, "calloc");
+
+	for (i = 0; i < ndeltas; i++) {
+		if (!(di = calloc(1, sizeof(struct deltainfo))))
+			err(1, "calloc");
+		if (git_patch_from_diff(&patch, ci->diff, i)) {
+			git_patch_free(patch);
+			goto err;
+		}
+		di->patch = patch;
+		ci->deltas[i] = di;
+
+		delta = git_patch_get_delta(patch);
+
+		/* check binary data */
+		if (delta->flags & GIT_DIFF_FLAG_BINARY)
+			continue;
+
+		nhunks = git_patch_num_hunks(patch);
+		for (j = 0; j < nhunks; j++) {
+			if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
+				break;
+			for (k = 0; ; k++) {
+				if (git_patch_get_line_in_hunk(&line, patch, j, k))
+					break;
+				if (line->old_lineno == -1) {
+					di->addcount++;
+					ci->addcount++;
+				} else if (line->new_lineno == -1) {
+					di->delcount++;
+					ci->delcount++;
+				}
+			}
+		}
+	}
+	ci->ndeltas = i;
+	ci->filecount = i;
+
+	return 0;
+
+err:
+	if (ci->deltas)
+		for (i = 0; i < ci->ndeltas; i++)
+			deltainfo_free(ci->deltas[i]);
+	free(ci->deltas);
+	ci->deltas = NULL;
+	ci->ndeltas = 0;
+	ci->addcount = 0;
+	ci->delcount = 0;
+	ci->filecount = 0;
+
+	return -1;
+}
+
+void
 commitinfo_free(struct commitinfo *ci)
 {
+	size_t i;
+
 	if (!ci)
 		return;
 
-	git_diff_stats_free(ci->stats);
+	if (ci->deltas)
+		for (i = 0; i < ci->ndeltas; i++)
+			deltainfo_free(ci->deltas[i]);
+	free(ci->deltas);
+	ci->deltas = NULL;
 	git_diff_free(ci->diff);
 	git_tree_free(ci->commit_tree);
 	git_tree_free(ci->parent_tree);

@@ -66,6 +158,7 @@ commitinfo_getbyoid(const git_oid *id)
 {
 	struct commitinfo *ci;
 	git_diff_options opts;
+	const git_oid *oid;
 	int error;
 
 	if (!(ci = calloc(1, sizeof(struct commitinfo))))

@@ -82,26 +175,24 @@ commitinfo_getbyoid(const git_oid *id)
 	ci->summary = git_commit_summary(ci->commit);
 	ci->msg = git_commit_message(ci->commit);
 
-	if ((error = git_commit_tree(&(ci->commit_tree), ci->commit)))
+	oid = git_commit_tree_id(ci->commit);
+	if ((error = git_tree_lookup(&(ci->commit_tree), repo, oid)))
 		goto err;
 	if (!(error = git_commit_parent(&(ci->parent), ci->commit, 0))) {
-		if ((error = git_commit_tree(&(ci->parent_tree), ci->parent)))
-			goto err;
-	} else {
-		ci->parent = NULL;
-		ci->parent_tree = NULL;
+		oid = git_commit_tree_id(ci->parent);
+		if ((error = git_tree_lookup(&(ci->parent_tree), repo, oid))) {
+			ci->parent = NULL;
+			ci->parent_tree = NULL;
+		}
 	}
 
 	git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION);
 	opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
 	if ((error = git_diff_tree_to_tree(&(ci->diff), repo, ci->parent_tree, ci->commit_tree, &opts)))
 		goto err;
-	if (git_diff_get_stats(&(ci->stats), ci->diff))
-		goto err;
 
-	ci->addcount = git_diff_stats_insertions(ci->stats);
-	ci->delcount = git_diff_stats_deletions(ci->stats);
-	ci->filecount = git_diff_stats_files_changed(ci->stats);
+	if (commitinfo_getstats(ci) == -1)
+		goto err;
 
 	return ci;
 

@@ -332,33 +423,55 @@ printshowfile(FILE *fp, struct commitinfo *ci)
 	const git_diff_hunk *hunk;
 	const git_diff_line *line;
 	git_patch *patch;
-	git_buf statsbuf;
-	size_t ndeltas, nhunks, nhunklines;
+	size_t nhunks, nhunklines, changed, add, del, total;
 	size_t i, j, k;
+	char linestr[80];
 
 	printcommit(fp, ci);
 
-	memset(&statsbuf, 0, sizeof(statsbuf));
+	if (!ci->deltas)
+		return;
 
 	/* diff stat */
-	if (ci->stats &&
-	    !git_diff_stats_to_buf(&statsbuf, ci->stats,
-                                   GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_SHORT, 80)) {
-		if (statsbuf.ptr && statsbuf.ptr[0]) {
-			fputs("<b>Diffstat:</b>\n", fp);
-			xmlencode(fp, statsbuf.ptr, strlen(statsbuf.ptr));
+	fputs("<b>Diffstat:</b>\n<table>", fp);
+	for (i = 0; i < ci->ndeltas; i++) {
+		delta = git_patch_get_delta(ci->deltas[i]->patch);
+		fputs("<tr><td>", fp);
+		xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
+		if (strcmp(delta->old_file.path, delta->new_file.path)) {
+			fputs(" -&gt; ", fp);
+			xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
+		}
+
+		add = ci->deltas[i]->addcount;
+		del = ci->deltas[i]->delcount;
+		changed = add + del;
+		total = sizeof(linestr) - 2;
+		if (changed > total) {
+			if (add)
+				add = ((float)total / changed * add) + 1;
+			if (del)
+				del = ((float)total / changed * del) + 1;
 		}
+		memset(&linestr, '+', add);
+		memset(&linestr[add], '-', del);
+
+		fprintf(fp, "</td><td> | %zu <span class=\"i\">",
+		        ci->deltas[i]->addcount + ci->deltas[i]->delcount);
+		fwrite(&linestr, 1, add, fp);
+		fputs("</span><span class=\"d\">", fp);
+		fwrite(&linestr[add], 1, del, fp);
+		fputs("</span></td></tr>\n", fp);
 	}
+	fprintf(fp, "</table>%zu file%s changed, %zu insertion%s(+), %zu deletion%s(-)\n",
+		ci->filecount, ci->filecount == 1 ? "" : "s",
+	        ci->addcount,  ci->addcount  == 1 ? "" : "s",
+	        ci->delcount,  ci->delcount  == 1 ? "" : "s");
 
 	fputs("<hr/>", fp);
 
-	ndeltas = git_diff_num_deltas(ci->diff);
-	for (i = 0; i < ndeltas; i++) {
-		if (git_patch_from_diff(&patch, ci->diff, i)) {
-			git_patch_free(patch);
-			break;
-		}
-
+	for (i = 0; i < ci->ndeltas; i++) {
+		patch = ci->deltas[i]->patch;
 		delta = git_patch_get_delta(patch);
 		fprintf(fp, "<b>diff --git a/<a href=\"%sfile/%s.html\">%s</a> b/<a href=\"%sfile/%s.html\">%s</a></b>\n",
 			relpath, delta->old_file.path, delta->old_file.path,

@@ -367,7 +480,6 @@ printshowfile(FILE *fp, struct commitinfo *ci)
 		/* check binary data */
 		if (delta->flags & GIT_DIFF_FLAG_BINARY) {
 			fputs("Binary files differ\n", fp);
-			git_patch_free(patch);
 			continue;
 		}
 

@@ -396,11 +508,7 @@ printshowfile(FILE *fp, struct commitinfo *ci)
 					fputs("</a>", fp);
 			}
 		}
-		git_patch_free(patch);
 	}
-	git_buf_free(&statsbuf);
-
-	return;
 }
 
 int
diff --git a/style.css b/style.css
@@ -79,10 +79,12 @@ pre a.h {
 	color: #00a;
 }
 
+span.i,
 pre a.i {
 	color: #070;
 }
 
+span.d,
 pre a.d {
 	color: #e00;
 }