sort branches and tags by time (descending) and add tags.xml for releases - 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 4dded587b089f4c2fda9694a908157a549c6cc1a
 (DIR) parent de86bac090f236501ee9f211d2bd55aa39c38ac7
 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Sun, 19 Jul 2020 18:56:10 +0200
       
       sort branches and tags by time (descending) and add tags.xml for releases
       
       Similar to the recent changes to stagit (HTML version).
       
       Diffstat:
         M stagit-gopher.1                     |       6 ++++--
         M stagit-gopher.c                     |     294 ++++++++++++++++++++-----------
       
       2 files changed, 192 insertions(+), 108 deletions(-)
       ---
 (DIR) diff --git a/stagit-gopher.1 b/stagit-gopher.1
       @@ -1,4 +1,4 @@
       -.Dd February 6, 2019
       +.Dd July 19, 2020
        .Dt STAGIT-GOPHER 1
        .Os
        .Sh NAME
       @@ -46,7 +46,9 @@ cannot be used at the same time.
        The following files will be written:
        .Bl -tag -width Ds
        .It atom.xml
       -Atom XML feed
       +Atom XML feed of the last 100 commits.
       +.It tags.xml
       +Atom XML feed of the tags.
        .It files.gph
        List of files in the latest tree, linking to the file.
        .It log.gph
 (DIR) diff --git a/stagit-gopher.c b/stagit-gopher.c
       @@ -50,6 +50,12 @@ struct commitinfo {
                size_t ndeltas;
        };
        
       +/* reference and associated data for sorting */
       +struct referenceinfo {
       +        struct git_reference *ref;
       +        struct commitinfo *ci;
       +};
       +
        static git_repository *repo;
        
        static const char *relpath = "";
       @@ -288,6 +294,104 @@ err:
                return NULL;
        }
        
       +int
       +refs_cmp(const void *v1, const void *v2)
       +{
       +        struct referenceinfo *r1 = (struct referenceinfo *)v1;
       +        struct referenceinfo *r2 = (struct referenceinfo *)v2;
       +        time_t t1, t2;
       +        int r;
       +
       +        if ((r = git_reference_is_tag(r1->ref) - git_reference_is_tag(r2->ref)))
       +                return r;
       +
       +        t1 = r1->ci->author ? r1->ci->author->when.time : 0;
       +        t2 = r2->ci->author ? r2->ci->author->when.time : 0;
       +        if ((r = t1 > t2 ? -1 : (t1 == t2 ? 0 : 1)))
       +                return r;
       +
       +        return strcmp(git_reference_shorthand(r1->ref),
       +                      git_reference_shorthand(r2->ref));
       +}
       +
       +int
       +getrefs(struct referenceinfo **pris, size_t *prefcount)
       +{
       +        struct referenceinfo *ris = NULL;
       +        struct commitinfo *ci = NULL;
       +        git_reference_iterator *it = NULL;
       +        const git_oid *id = NULL;
       +        git_object *obj = NULL;
       +        git_reference *dref = NULL, *r, *ref = NULL;
       +        size_t i, refcount;
       +
       +        *pris = NULL;
       +        *prefcount = 0;
       +
       +        if (git_reference_iterator_new(&it, repo))
       +                return -1;
       +
       +        for (refcount = 0; !git_reference_next(&ref, it); ) {
       +                if (!git_reference_is_branch(ref) && !git_reference_is_tag(ref)) {
       +                        git_reference_free(ref);
       +                        ref = NULL;
       +                        continue;
       +                }
       +
       +                switch (git_reference_type(ref)) {
       +                case GIT_REF_SYMBOLIC:
       +                        if (git_reference_resolve(&dref, ref))
       +                                goto err;
       +                        r = dref;
       +                        break;
       +                case GIT_REF_OID:
       +                        r = ref;
       +                        break;
       +                default:
       +                        continue;
       +                }
       +                if (!git_reference_target(r) ||
       +                    git_reference_peel(&obj, r, GIT_OBJ_ANY))
       +                        goto err;
       +                if (!(id = git_object_id(obj)))
       +                        goto err;
       +                if (!(ci = commitinfo_getbyoid(id)))
       +                        break;
       +
       +                if (!(ris = reallocarray(ris, refcount + 1, sizeof(*ris))))
       +                        err(1, "realloc");
       +                ris[refcount].ci = ci;
       +                ris[refcount].ref = r;
       +                refcount++;
       +
       +                git_object_free(obj);
       +                obj = NULL;
       +                git_reference_free(dref);
       +                dref = NULL;
       +        }
       +        git_reference_iterator_free(it);
       +
       +        /* sort by type, date then shorthand name */
       +        qsort(ris, refcount, sizeof(*ris), refs_cmp);
       +
       +        *pris = ris;
       +        *prefcount = refcount;
       +
       +        return 0;
       +
       +err:
       +        git_object_free(obj);
       +        git_reference_free(dref);
       +        commitinfo_free(ci);
       +        for (i = 0; i < refcount; i++) {
       +                commitinfo_free(ris[i].ci);
       +                git_reference_free(ris[i].ref);
       +        }
       +        free(ris);
       +
       +        return -1;
       +}
       +
        FILE *
        efopen(const char *name, const char *flags)
        {
       @@ -760,7 +864,7 @@ err:
        }
        
        void
       -printcommitatom(FILE *fp, struct commitinfo *ci)
       +printcommitatom(FILE *fp, struct commitinfo *ci, const char *tag)
        {
                fputs("<entry>\n", fp);
        
       @@ -777,6 +881,11 @@ printcommitatom(FILE *fp, struct commitinfo *ci)
                }
                if (ci->summary) {
                        fputs("<title type=\"text\">", fp);
       +                if (tag) {
       +                        fputs("[", fp);
       +                        xmlencode(fp, tag, strlen(tag));
       +                        fputs("] ", fp);
       +                }
                        xmlencode(fp, ci->summary, strlen(ci->summary));
                        fputs("</title>\n", fp);
                }
       @@ -812,8 +921,10 @@ printcommitatom(FILE *fp, struct commitinfo *ci)
        }
        
        int
       -writeatom(FILE *fp)
       +writeatom(FILE *fp, int all)
        {
       +        struct referenceinfo *ris = NULL;
       +        size_t refcount = 0;
                struct commitinfo *ci;
                git_revwalk *w = NULL;
                git_oid id;
       @@ -826,17 +937,34 @@ writeatom(FILE *fp)
                xmlencode(fp, description, strlen(description));
                fputs("</subtitle>\n", fp);
        
       -        git_revwalk_new(&w, repo);
       -        git_revwalk_push_head(w);
       -        git_revwalk_simplify_first_parent(w);
       +        /* all commits or only tags? */
       +        if (all) {
       +                git_revwalk_new(&w, repo);
       +                git_revwalk_push_head(w);
       +                git_revwalk_simplify_first_parent(w);
       +                for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
       +                        if (!(ci = commitinfo_getbyoid(&id)))
       +                                break;
       +                        printcommitatom(fp, ci, "");
       +                        commitinfo_free(ci);
       +                }
       +                git_revwalk_free(w);
       +        } else {
       +                /* references: tags */
       +                if (getrefs(&ris, &refcount) != -1) {
       +                        for (i = 0; i < refcount; i++) {
       +                                if (!git_reference_is_tag(ris[i].ref))
       +                                        continue;
        
       -        for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
       -                if (!(ci = commitinfo_getbyoid(&id)))
       -                        break;
       -                printcommitatom(fp, ci);
       -                commitinfo_free(ci);
       +                                printcommitatom(fp, ris[i].ci,
       +                                                git_reference_shorthand(ris[i].ref));
       +
       +                                commitinfo_free(ris[i].ci);
       +                                git_reference_free(ris[i].ref);
       +                        }
       +                        free(ris);
       +                }
                }
       -        git_revwalk_free(w);
        
                fputs("</feed>\n", fp);
        
       @@ -1012,115 +1140,63 @@ writefiles(FILE *fp, const git_oid *id)
        }
        
        int
       -refs_cmp(const void *v1, const void *v2)
       -{
       -        git_reference *r1 = (*(git_reference **)v1);
       -        git_reference *r2 = (*(git_reference **)v2);
       -        int r;
       -
       -        if ((r = git_reference_is_branch(r1) - git_reference_is_branch(r2)))
       -                return r;
       -
       -        return strcmp(git_reference_shorthand(r1),
       -                      git_reference_shorthand(r2));
       -}
       -
       -int
        writerefs(FILE *fp)
        {
       +        struct referenceinfo *ris = NULL;
                struct commitinfo *ci;
       -        const git_oid *id = NULL;
       -        git_object *obj = NULL;
       -        git_reference *dref = NULL, *r, *ref = NULL;
       -        git_reference_iterator *it = NULL;
       -        git_reference **refs = NULL;
                size_t count, i, j, refcount;
                const char *titles[] = { "Branches", "Tags" };
       -        const char *name;
       +        const char *ids[] = { "branches", "tags" };
       +        const char *s;
                char buf[256];
        
       -        if (git_reference_iterator_new(&it, repo))
       +        if (getrefs(&ris, &refcount) == -1)
                        return -1;
        
       -        for (refcount = 0; !git_reference_next(&ref, it); refcount++) {
       -                if (!(refs = reallocarray(refs, refcount + 1, sizeof(git_reference *))))
       -                        err(1, "realloc");
       -                refs[refcount] = ref;
       -        }
       -        git_reference_iterator_free(it);
       -
       -        /* sort by type then shorthand name */
       -        qsort(refs, refcount, sizeof(git_reference *), refs_cmp);
       -
       -        for (j = 0; j < 2; j++) {
       -                for (i = 0, count = 0; i < refcount; i++) {
       -                        if (!(git_reference_is_branch(refs[i]) && j == 0) &&
       -                            !(git_reference_is_tag(refs[i]) && j == 1))
       -                                continue;
       -
       -                        switch (git_reference_type(refs[i])) {
       -                        case GIT_REF_SYMBOLIC:
       -                                if (git_reference_resolve(&dref, refs[i]))
       -                                        goto err;
       -                                r = dref;
       -                                break;
       -                        case GIT_REF_OID:
       -                                r = refs[i];
       -                                break;
       -                        default:
       -                                continue;
       -                        }
       -                        if (!git_reference_target(r) ||
       -                            git_reference_peel(&obj, r, GIT_OBJ_ANY))
       -                                goto err;
       -                        if (!(id = git_object_id(obj)))
       -                                goto err;
       -                        if (!(ci = commitinfo_getbyoid(id)))
       -                                break;
       -
       -                        /* print header if it has an entry (first). */
       -                        if (++count == 1) {
       -                                fprintf(fp, "%s\n", titles[j]);
       -                                fprintf(fp, "  %-32.32s", "Name");
       -                                fprintf(fp, "  %-16.16s", "Last commit date");
       -                                fprintf(fp, "  %s\n", "Author");
       -                        }
       +        for (i = 0, j = 0, count = 0; i < refcount; i++) {
       +                if (j == 0 && git_reference_is_tag(ris[i].ref)) {
       +                        /* table footer */
       +                        if (count)
       +                                fputs("\n", fp);
       +                        count = 0;
       +                        j = 1;
       +                }
        
       -                        name = git_reference_shorthand(r);
       +                /* print header if it has an entry (first). */
       +                if (++count == 1) {
       +                        fprintf(fp, "%s\n", titles[j]);
       +                        fprintf(fp, "  %-32.32s", "Name");
       +                        fprintf(fp, "  %-16.16s", "Last commit date");
       +                        fprintf(fp, "  %s\n", "Author");
       +                }
        
       -                        fputs("  ", fp);
       -                        utf8pad(buf, sizeof(buf), name, 32, ' ');
       +                ci = ris[i].ci;
       +                s = git_reference_shorthand(ris[i].ref);
       +
       +                fputs("  ", fp);
       +                utf8pad(buf, sizeof(buf), s, 32, ' ');
       +                gphlink(fp, buf, strlen(buf));
       +                fputs("  ", fp);
       +                if (ci->author)
       +                        printtimeshort(fp, &(ci->author->when));
       +                else
       +                        fputs("                ", fp);
       +                fputs("  ", fp);
       +                if (ci->author) {
       +                        utf8pad(buf, sizeof(buf), ci->author->name, 25, '\0');
                                gphlink(fp, buf, strlen(buf));
       -                        fputs("  ", fp);
       -                        if (ci->author)
       -                                printtimeshort(fp, &(ci->author->when));
       -                        else
       -                                fputs("                ", fp);
       -                        fputs("  ", fp);
       -                        if (ci->author) {
       -                                utf8pad(buf, sizeof(buf), ci->author->name, 25, '\0');
       -                                gphlink(fp, buf, strlen(buf));
       -                        }
       -                        fputs("\n", fp);
       -
       -                        commitinfo_free(ci);
       -                        git_object_free(obj);
       -                        obj = NULL;
       -                        git_reference_free(dref);
       -                        dref = NULL;
                        }
       -                /* table footer */
       -                if (count)
       -                        fputs("\n", fp);
       +                fputs("\n", fp);
                }
       +        /* table footer */
       +        if (count)
       +                fputs("\n", fp);
        
       -err:
       -        git_object_free(obj);
       -        git_reference_free(dref);
       -
       -        for (i = 0; i < refcount; i++)
       -                git_reference_free(refs[i]);
       -        free(refs);
       +        for (i = 0; i < refcount; i++) {
       +                commitinfo_free(ris[i].ci);
       +                git_reference_free(ris[i].ref);
       +        }
       +        free(ris);
        
                return 0;
        }
       @@ -1311,6 +1387,7 @@ main(int argc, char *argv[])
                                writelog(fp, head);
                }
                fprintf(fp, "\n[0|Atom feed|%s/atom.xml|server|port]\n", relpath);
       +        fprintf(fp, "\n[0|Atom feed (tags)|%s/tags.xml|server|port]\n", relpath);
                writefooter(fp);
                fclose(fp);
        
       @@ -1331,7 +1408,12 @@ main(int argc, char *argv[])
        
                /* Atom feed */
                fp = efopen("atom.xml", "w");
       -        writeatom(fp);
       +        writeatom(fp, 1);
       +        fclose(fp);
       +
       +        /* Atom feed for tags / releases */
       +        fp = efopen("tags.xml", "w");
       +        writeatom(fp, 0);
                fclose(fp);
        
                /* rename new cache file on success */