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 ;)).
Diffstat:
M | stagit.c | | | 186 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
M | style.css | | | 2 | ++ |
2 files changed, 149 insertions(+), 39 deletions(-)
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(" -> ", 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;
}