sort branches and tags by time (descending) and add tags.xml for releases - stagit-gopher - A git gopher frontend. (mirror)
git clone git://bitreich.org/stagit-gopher/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/stagit-gopher/
Log
Files
Refs
Tags
README
LICENSE
---
commit 4dded587b089f4c2fda9694a908157a549c6cc1a
parent de86bac090f236501ee9f211d2bd55aa39c38ac7
Author: Hiltjo Posthuma 
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(-)
---
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
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("\n", fp);
 
@@ -777,6 +881,11 @@ printcommitatom(FILE *fp, struct commitinfo *ci)
         }
         if (ci->summary) {
                 fputs("", fp);
+                if (tag) {
+                        fputs("[", fp);
+                        xmlencode(fp, tag, strlen(tag));
+                        fputs("] ", fp);
+                }
                 xmlencode(fp, ci->summary, strlen(ci->summary));
                 fputs("\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("\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("\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 */