refactor get reference, add another feed for tags/releases - stagit - static git page generator
 (HTM) git clone git://git.codemadness.org/stagit
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit d80a163acd47df2bd9ab145be6b249814aa9eceb
 (DIR) parent 693c06448972f049d74addbd4942365cd37d92e4
 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Sun, 19 Jul 2020 16:41:10 +0200
       
       refactor get reference, add another feed for tags/releases
       
       A separate Atom feed is helpful to ports maintainers to monitor new
       tags/releases.
       
       Diffstat:
         M stagit.1                            |       6 ++++--
         M stagit.c                            |     226 +++++++++++++++++++------------
       
       2 files changed, 146 insertions(+), 86 deletions(-)
       ---
 (DIR) diff --git a/stagit.1 b/stagit.1
       @@ -1,4 +1,4 @@
       -.Dd February 6, 2019
       +.Dd July 19, 2020
        .Dt STAGIT 1
        .Os
        .Sh NAME
       @@ -42,7 +42,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.html
        List of files in the latest tree, linking to the file.
        .It log.html
 (DIR) diff --git a/stagit.c b/stagit.c
       @@ -248,6 +248,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)
        {
       @@ -361,6 +459,8 @@ writeheader(FILE *fp, const char *title)
                fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
                fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />\n",
                        name, relpath);
       +        fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed (tags)\" href=\"%stags.xml\" />\n",
       +                name, relpath);
                fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
                fputs("</head>\n<body>\n<table><tr><td>", fp);
                fprintf(fp, "<a href=\"../%s\"><img src=\"%slogo.png\" alt=\"\" width=\"32\" height=\"32\" /></a>",
       @@ -680,7 +780,7 @@ err:
        }
        
        void
       -printcommitatom(FILE *fp, struct commitinfo *ci)
       +printcommitatom(FILE *fp, struct commitinfo *ci, const char *tag)
        {
                fputs("<entry>\n", fp);
        
       @@ -697,6 +797,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);
                }
       @@ -732,8 +837,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;
       @@ -746,17 +853,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);
        
       @@ -942,85 +1066,18 @@ writefiles(FILE *fp, const git_oid *id)
        }
        
        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
        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;
                size_t count, i, j, refcount;
                const char *titles[] = { "Branches", "Tags" };
                const char *ids[] = { "branches", "tags" };
                const char *s;
        
       -        if (git_reference_iterator_new(&it, repo))
       +        if (getrefs(&ris, &refcount) == -1)
                        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);
       -
                for (i = 0, j = 0, count = 0; i < refcount; i++) {
                        if (j == 0 && git_reference_is_tag(ris[i].ref)) {
                                if (count)
       @@ -1056,10 +1113,6 @@ writerefs(FILE *fp)
                if (count)
                        fputs("</tbody></table><br/>\n", fp);
        
       -err:
       -        git_object_free(obj);
       -        git_reference_free(dref);
       -
                for (i = 0; i < refcount; i++) {
                        commitinfo_free(ris[i].ci);
                        git_reference_free(ris[i].ref);
       @@ -1272,7 +1325,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 */