optimization: read stats once and remember it - stagit-gopher - A git gopher frontend. (mirror)
 (HTM) git clone git://bitreich.org/stagit-gopher/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/stagit-gopher/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 914880f31b04222fa2815f4f35543b72f881d5e4
 (DIR) parent 0bc47da0f7b66614cdf499755ceca1dc13ff91cd
 (HTM) 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(-)
       ---
 (DIR) 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
 (DIR) 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;
        }