sort branches and tags by time (descending)
authorHiltjo Posthuma <hiltjo@codemadness.org>
Sun, 19 Jul 2020 12:07:54 +0000 (14:07 +0200)
committerHiltjo Posthuma <hiltjo@codemadness.org>
Sun, 19 Jul 2020 12:07:54 +0000 (14:07 +0200)
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!

stagit.c

index 7ff036fe7eef96ac854a9109d727a7e3642b9f37..b4f3b22961e67e13fceb2d38c7e8c7bd60c8ab26 100644 (file)
--- 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;
 }