| toptimization: read stats once and remember it - stagit - static git page gener… | |
| git clone git://src.adamsgaard.dk/stagit | |
| Log | |
| Files | |
| Refs | |
| README | |
| LICENSE | |
| --- | |
| commit 914880f31b04222fa2815f4f35543b72f881d5e4 | |
| parent 0bc47da0f7b66614cdf499755ceca1dc13ff91cd | |
| Author: Hiltjo Posthuma <[email protected]> | |
| 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 | |
| tthere ;)). | |
| Diffstat: | |
| M stagit.c | 186 ++++++++++++++++++++++++-----… | |
| M style.css | 2 ++ | |
| 2 files changed, 149 insertions(+), 39 deletions(-) | |
| --- | |
| diff --git a/stagit.c b/stagit.c | |
| t@@ -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; | |
| t@@ -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; | |
| t@@ -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… | |
| + 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); | |
| t@@ -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)))) | |
| t@@ -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,… | |
| 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; | |
| t@@ -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,… | |
| - 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.pat… | |
| + if (strcmp(delta->old_file.path, delta->new_file.path)) { | |
| + fputs(" -> ", fp); | |
| + xmlencode(fp, delta->new_file.path, strlen(delta->new_… | |
| + } | |
| + | |
| + 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 delet… | |
| + 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>… | |
| relpath, delta->old_file.path, delta->old_file.path, | |
| t@@ -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; | |
| } | |
| t@@ -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 | |
| t@@ -79,10 +79,12 @@ pre a.h { | |
| color: #00a; | |
| } | |
| +span.i, | |
| pre a.i { | |
| color: #070; | |
| } | |
| +span.d, | |
| pre a.d { | |
| color: #e00; | |
| } |