sort branches and tags by time (descending) - 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 693c06448972f049d74addbd4942365cd37d92e4
 (DIR) parent 467dfeb8f4bf2dd1ddb69e5c9592147acb425aab
 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Sun, 19 Jul 2020 14:07:54 +0200
       
       sort branches and tags by time (descending)
       
       In general version tags are done in chronological order, so this will have a
       better sorting for tagged (versioned) releases.
       
       Request from Caltlgin Stsodaat and others, thanks!
       
       Diffstat:
         M stagit.c                            |     164 +++++++++++++++++--------------
       
       1 file changed, 92 insertions(+), 72 deletions(-)
       ---
 (DIR) diff --git a/stagit.c b/stagit.c
       @@ -48,6 +48,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 = "";
       @@ -938,113 +944,127 @@ 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);
       +        struct referenceinfo *r1 = (struct referenceinfo *)v1;
       +        struct referenceinfo *r2 = (struct referenceinfo *)v2;
       +        time_t t1, t2;
                int r;
        
       -        if ((r = git_reference_is_branch(r1) - git_reference_is_branch(r2)))
       +        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),
       -                      git_reference_shorthand(r2));
       +        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;
       -        git_reference **refs = NULL;
                size_t count, i, j, refcount;
                const char *titles[] = { "Branches", "Tags" };
                const char *ids[] = { "branches", "tags" };
       -        const char *name;
       +        const char *s;
        
                if (git_reference_iterator_new(&it, repo))
                        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;
       +        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(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))
       +                switch (git_reference_type(ref)) {
       +                case GIT_REF_SYMBOLIC:
       +                        if (git_reference_resolve(&dref, ref))
                                        goto err;
       -                        if (!(id = git_object_id(obj)))
       -                                goto err;
       -                        if (!(ci = commitinfo_getbyoid(id)))
       -                                break;
       +                        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;
        
       -                        /* print header if it has an entry (first). */
       -                        if (++count == 1) {
       -                                fprintf(fp, "<h2>%s</h2><table id=\"%s\">"
       -                                        "<thead>\n<tr><td><b>Name</b></td>"
       -                                        "<td><b>Last commit date</b></td>"
       -                                        "<td><b>Author</b></td>\n</tr>\n"
       -                                        "</thead><tbody>\n",
       -                                         titles[j], ids[j]);
       -                        }
       +                if (!(ris = reallocarray(ris, refcount + 1, sizeof(*ris))))
       +                        err(1, "realloc");
       +                ris[refcount].ci = ci;
       +                ris[refcount].ref = r;
       +                refcount++;
        
       -                        relpath = "";
       -                        name = git_reference_shorthand(r);
       +                git_object_free(obj);
       +                obj = NULL;
       +                git_reference_free(dref);
       +                dref = NULL;
       +        }
       +        git_reference_iterator_free(it);
        
       -                        fputs("<tr><td>", fp);
       -                        xmlencode(fp, name, strlen(name));
       -                        fputs("</td><td>", fp);
       -                        if (ci->author)
       -                                printtimeshort(fp, &(ci->author->when));
       -                        fputs("</td><td>", fp);
       -                        if (ci->author)
       -                                xmlencode(fp, ci->author->name, strlen(ci->author->name));
       -                        fputs("</td></tr>\n", fp);
       +        /* sort by type, date then shorthand name */
       +        qsort(ris, refcount, sizeof(*ris), refs_cmp);
        
       -                        relpath = "../";
       +        for (i = 0, j = 0, count = 0; i < refcount; i++) {
       +                if (j == 0 && git_reference_is_tag(ris[i].ref)) {
       +                        if (count)
       +                                fputs("</tbody></table><br/>\n", fp);
       +                        count = 0;
       +                        j = 1;
       +                }
        
       -                        commitinfo_free(ci);
       -                        git_object_free(obj);
       -                        obj = NULL;
       -                        git_reference_free(dref);
       -                        dref = NULL;
       +                /* print header if it has an entry (first). */
       +                if (++count == 1) {
       +                        fprintf(fp, "<h2>%s</h2><table id=\"%s\">"
       +                                "<thead>\n<tr><td><b>Name</b></td>"
       +                                "<td><b>Last commit date</b></td>"
       +                                "<td><b>Author</b></td>\n</tr>\n"
       +                                "</thead><tbody>\n",
       +                                 titles[j], ids[j]);
                        }
       -                /* table footer */
       -                if (count)
       -                        fputs("</tbody></table><br/>", fp);
       +
       +                ci = ris[i].ci;
       +                s = git_reference_shorthand(ris[i].ref);
       +
       +                fputs("<tr><td>", fp);
       +                xmlencode(fp, s, strlen(s));
       +                fputs("</td><td>", fp);
       +                if (ci->author)
       +                        printtimeshort(fp, &(ci->author->when));
       +                fputs("</td><td>", fp);
       +                if (ci->author)
       +                        xmlencode(fp, ci->author->name, strlen(ci->author->name));
       +                fputs("</td></tr>\n", fp);
                }
       +        /* table footer */
       +        if (count)
       +                fputs("</tbody></table><br/>\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;
        }