initial diff support, fix log link - 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 a95a439aaa7d048c25e988b0d9770f5040f0a17f
 (DIR) parent 04eeb7f000b14af31b9923ea7216edcaf0915586
 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Mon,  7 Dec 2015 14:07:58 +0100
       
       initial diff support, fix log link
       
       Diffstat:
         M urmoms.c                            |     244 ++++++++++++++++++++++---------
       
       1 file changed, 176 insertions(+), 68 deletions(-)
       ---
 (DIR) diff --git a/urmoms.c b/urmoms.c
       @@ -1,3 +1,5 @@
       +#include <sys/stat.h>
       +
        #include <err.h>
        #include <libgen.h>
        #include <limits.h>
       @@ -9,13 +11,48 @@
        
        static git_repository *repo;
        
       -static const char *relpath;
       -static const char *repodir = ".";
       +static const char *relpath = "";
       +static const char *repodir;
        
        static char name[255];
        static char description[255];
        static int hasreadme, haslicense;
        
       +int
       +writeheader(FILE *fp)
       +{
       +        fprintf(fp, "<!DOCTYPE HTML>"
       +                "<html dir=\"ltr\" lang=\"en\"><head>"
       +                "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
       +                "<meta http-equiv=\"Content-Language\" content=\"en\" />");
       +        fprintf(fp, "<title>%s%s%s</title>", name, description[0] ? " - " : "", description);
       +        fprintf(fp, "<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />", relpath);
       +        fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />",
       +                name, relpath);
       +        fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
       +                "</head><body><center>");
       +        fprintf(fp, "<h1><img src=\"%slogo.png\" alt=\"\" /> %s</h1>", relpath, name);
       +        fprintf(fp, "<span class=\"desc\">%s</span><br/>", description);
       +        fprintf(fp, "<a href=\"%slog.html\">Log</a> |", relpath);
       +        fprintf(fp, "<a href=\"%sfiles.html\">Files</a>| ", relpath);
       +        fprintf(fp, "<a href=\"%sstats.html\">Stats</a>", relpath);
       +        if (hasreadme)
       +                fprintf(fp, " | <a href=\"%sreadme.html\">README</a>", relpath);
       +        if (haslicense)
       +                fprintf(fp, " | <a href=\"%slicense.html\">LICENSE</a>", relpath);
       +        fprintf(fp, "</center><hr/><pre>");
       +
       +        return 0;
       +}
       +
       +int
       +writefooter(FILE *fp)
       +{
       +        fprintf(fp, "</pre></body></html>");
       +
       +        return 0;
       +}
       +
        FILE *
        efopen(const char *name, const char *flags)
        {
       @@ -67,8 +104,8 @@ xbasename(const char *path)
                return b;
        }
        
       -static void
       -printtime(FILE *fp, const git_time *intime, const char *prefix)
       +void
       +printtime(FILE *fp, const git_time *intime)
        {
                struct tm *intm;
                time_t t;
       @@ -91,10 +128,10 @@ printtime(FILE *fp, const git_time *intime, const char *prefix)
                intm = gmtime(&t);
                strftime(out, sizeof(out), "%a %b %e %T %Y", intm);
        
       -        fprintf(fp, "%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
       +        fprintf(fp, "%s %c%02d%02d\n", out, sign, hours, minutes);
        }
        
       -static void
       +void
        printcommit(FILE *fp, git_commit *commit)
        {
                const git_signature *sig;
       @@ -103,22 +140,31 @@ printcommit(FILE *fp, git_commit *commit)
                const char *scan, *eol;
        
                git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
       -        fprintf(fp, "commit <a href=\"commit/%s.html\">%s</a>\n", buf, buf);
       +        fprintf(fp, "commit <a href=\"%scommit/%s.html\">%s</a>\n",
       +                relpath, buf, buf);
       +
       +        if (git_oid_tostr(buf, sizeof(buf), git_commit_parent_id(commit, 0)))
       +                fprintf(fp, "parent <a href=\"%scommit/%s.html\">%s</a>\n",
       +                        relpath, buf, buf);
        
                if ((count = (int)git_commit_parentcount(commit)) > 1) {
                        fprintf(fp, "Merge:");
                        for (i = 0; i < count; ++i) {
                                git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
       -                        fprintf(fp, " %s", buf);
       +                        fprintf(fp, " <a href=\"%scommit/%s.html\">%s</a>",
       +                                relpath, buf, buf);
                        }
       -                fprintf(fp, "\n");
       +                fputc('\n', fp);
                }
                if ((sig = git_commit_author(commit)) != NULL) {
       -                fprintf(fp, "Author: <a href=\"author/%s.html\">%s</a> <%s>\n",
       -                        sig->name, sig->name, sig->email);
       -                printtime(fp, &sig->when, "Date:   ");
       +                fprintf(fp, "Author: ");
       +                xmlencode(fp, sig->name, strlen(sig->name));
       +                fprintf(fp, " &lt;");
       +                xmlencode(fp, sig->email, strlen(sig->email));
       +                fprintf(fp, "&gt;\nDate:   ");
       +                printtime(fp, &sig->when);
                }
       -        fprintf(fp, "\n");
       +        fputc('\n', fp);
        
                for (scan = git_commit_message(commit); scan && *scan;) {
                        for (eol = scan; *eol && *eol != '\n'; ++eol)        /* find eol */
       @@ -127,79 +173,131 @@ printcommit(FILE *fp, git_commit *commit)
                        fprintf(fp, "    %.*s\n", (int) (eol - scan), scan);
                        scan = *eol ? eol + 1 : NULL;
                }
       -        fprintf(fp, "\n");
       +        fputc('\n', fp);
        }
        
       -static void
       -printcommitdiff(FILE *fp, git_commit *commit)
       +void
       +printshowfile(git_commit *commit)
        {
       -        const git_signature *sig;
       -        char buf[GIT_OID_HEXSZ + 1];
       -        int i, count;
       -        const char *scan, *eol;
       +        const git_diff_delta *delta = NULL;
       +        const git_diff_hunk *hunk = NULL;
       +        const git_diff_line *line = NULL;
       +        git_commit *parent = NULL;
       +        git_tree *commit_tree = NULL, *parent_tree = NULL;
       +        git_patch *patch = NULL;
       +        git_diff *diff = NULL;
       +        size_t i, j, k, ndeltas, nhunks = 0, nhunklines = 0;
       +        char buf[GIT_OID_HEXSZ + 1], path[PATH_MAX];
       +        FILE *fp;
       +        int error;
        
                git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
       -        fprintf(fp, "commit <a href=\"commit/%s.html\">%s</a>\n", buf, buf);
        
       -        if ((count = (int)git_commit_parentcount(commit)) > 1) {
       -                fprintf(fp, "Merge:");
       -                for (i = 0; i < count; ++i) {
       -                        git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
       -                        fprintf(fp, " %s", buf);
       +        snprintf(path, sizeof(path), "commit/%s.html", buf);
       +        fp = efopen(path, "w+b");
       +
       +        writeheader(fp);
       +        printcommit(fp, commit);
       +
       +        error = git_commit_parent(&parent, commit, 0);
       +        if (error)
       +                return;
       +
       +        error = git_commit_tree(&commit_tree, commit);
       +        if (error)
       +                return;
       +        error = git_commit_tree(&parent_tree, parent);
       +        if (error)
       +                return;
       +
       +        error = git_diff_tree_to_tree(&diff, repo, commit_tree, parent_tree, NULL);
       +        if (error)
       +                return;
       +
       +        /* TODO: diff stat (files list and insertions/deletions) */
       +
       +        ndeltas = git_diff_num_deltas(diff);
       +        for (i = 0; i < ndeltas; i++) {
       +                if (git_patch_from_diff(&patch, diff, i)) {
       +                        git_patch_free(patch);
       +                        break; /* TODO: handle error */
                        }
       -                fprintf(fp, "\n");
       -        }
       -        if ((sig = git_commit_author(commit)) != NULL) {
       -                fprintf(fp, "Author: <a href=\"author/%s.html\">%s</a> <%s>\n",
       -                        sig->name, sig->name, sig->email);
       -                printtime(fp, &sig->when, "Date:   ");
       -        }
       -        fprintf(fp, "\n");
        
       -        for (scan = git_commit_message(commit); scan && *scan;) {
       -                for (eol = scan; *eol && *eol != '\n'; ++eol)        /* find eol */
       -                        ;
       +                delta = git_patch_get_delta(patch);
       +                fprintf(fp, "diff --git a/<a href=\"%s%s\">%s</a> b/<a href=\"%s%s\">%s</a>\n",
       +                        relpath, delta->old_file.path, delta->old_file.path,
       +                        relpath, delta->new_file.path, delta->new_file.path);
        
       -                fprintf(fp, "    %.*s\n", (int) (eol - scan), scan);
       -                scan = *eol ? eol + 1 : NULL;
       +#if 0
       +                switch (delta->flags) {
       +                case GIT_DIFF_FLAG_BINARY:       continue; /* TODO: binary data */
       +                case GIT_DIFF_FLAG_NOT_BINARY:   break;
       +                case GIT_DIFF_FLAG_VALID_ID:     break; /* TODO: check */
       +                case GIT_DIFF_FLAG_EXISTS:       break; /* TODO: check */
       +                }
       +#endif
       +
       +                nhunks = git_patch_num_hunks(patch);
       +                for (j = 0; j < nhunks; j++) {
       +                        if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
       +                                break; /* TODO: handle error ? */
       +
       +                        fprintf(fp, "%s\n", hunk->header);
       +
       +                        for (k = 0; ; k++) {
       +                                if (git_patch_get_line_in_hunk(&line, patch, j, k))
       +                                        break;
       +                                if (line->old_lineno == -1)
       +                                        fputc('+', fp);
       +                                else if (line->new_lineno == -1)
       +                                        fputc('-', fp);
       +                                else
       +                                        fputc(' ', fp);
       +                                xmlencode(fp, line->content, line->content_len);
       +                        }
       +                }
       +                git_patch_free(patch);
                }
       -        fprintf(fp, "\n");
       +        git_diff_free(diff);
       +
       +        writefooter(fp);
       +        fclose(fp);
        }
        
        int
       -writeheader(FILE *fp)
       +writelog(FILE *fp)
        {
       -        fprintf(fp, "<!DOCTYPE HTML>"
       -                "<html dir=\"ltr\" lang=\"en\"><head>"
       -                "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
       -                "<meta http-equiv=\"Content-Language\" content=\"en\" />");
       -        fprintf(fp, "<title>%s%s%s</title>", name, description[0] ? " - " : "", description);
       -        fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
       -                "</head><body><center>");
       -        fprintf(fp, "<h1><img src=\"%slogo.png\" alt=\"\" /> %s</h1>", relpath, name);
       -        fprintf(fp, "<span class=\"desc\">%s</span><br/>", description);
       -        fprintf(fp, "<a href=\"%slog.html\">Log</a> |", relpath);
       -        fprintf(fp, "<a href=\"%sfiles.html\">Files</a>| ", relpath);
       -        fprintf(fp, "<a href=\"%sstats.html\">Stats</a>", relpath);
       -        if (hasreadme)
       -                fprintf(fp, " | <a href=\"%sreadme.html\">README</a>", relpath);
       -        if (haslicense)
       -                fprintf(fp, " | <a href=\"%slicense.html\">LICENSE</a>", relpath);
       -        fprintf(fp, "</center><hr/><pre>");
       +        git_revwalk *w = NULL;
       +        git_oid id;
       +        git_commit *c = NULL;
       +        size_t i;
        
       -        return 0;
       -}
       +        mkdir("commit", 0755);
        
       -int
       -writefooter(FILE *fp)
       -{
       -        fprintf(fp, "</pre></body></html>");
       +        git_revwalk_new(&w, repo);
       +        git_revwalk_push_head(w);
       +
       +        i = 0;
       +        while (!git_revwalk_next(&id, w)) {
       +                if (git_commit_lookup(&c, repo, &id))
       +                        return 1; /* TODO: error */
       +                printcommit(fp, c);
       +                printshowfile(c);
       +                git_commit_free(c);
       +
       +                /* DEBUG */
       +                i++;
       +                if (i > 100)
       +                        break;
       +        }
       +        git_revwalk_free(w);
        
                return 0;
        }
        
       +#if 0
        int
       -writelog(FILE *fp)
       +writeatom(FILE *fp)
        {
                git_revwalk *w = NULL;
                git_oid id;
       @@ -210,14 +308,16 @@ writelog(FILE *fp)
        
                while (!git_revwalk_next(&id, w)) {
                        if (git_commit_lookup(&c, repo, &id))
       -                        return 1;
       +                        return 1; /* TODO: error */
                        printcommit(fp, c);
       +                printshowfile(c);
                        git_commit_free(c);
                }
                git_revwalk_free(w);
        
                return 0;
        }
       +#endif
        
        int
        writefiles(FILE *fp)
       @@ -251,7 +351,9 @@ writebranches(FILE *fp)
                git_branch_iterator_new(&branchit, repo, GIT_BRANCH_LOCAL);
        
                while ((status = git_branch_next(&branchref, &branchtype, branchit)) == GIT_ITEROVER) {
       -                git_reference_normalize_name(branchbuf, sizeof(branchbuf), git_reference_name(branchref), GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND);
       +                git_reference_normalize_name(branchbuf, sizeof(branchbuf),
       +                        git_reference_name(branchref),
       +                        GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND);
        
                        /* fprintf(fp, "branch: |%s|\n", branchbuf); */
                }
       @@ -337,12 +439,18 @@ main(int argc, char *argv[])
                        hasreadme = 1;
                }
        
       -        fp = efopen("logs.html", "w+b");
       +        fp = efopen("log.html", "w+b");
                writeheader(fp);
                writelog(fp);
                writefooter(fp);
                fclose(fp);
        
       +#if 0
       +        fp = efopen("atom.xml", "w+b");
       +        writeatom(fp);
       +        fclose(fp);
       +#endif
       +
                fp = efopen("files.html", "w+b");
                writeheader(fp);
                writefiles(fp);