From 693c06448972f049d74addbd4942365cd37d92e4 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 19 Jul 2020 14:07:54 +0200 Subject: [PATCH] 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! --- stagit.c | 168 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 94 insertions(+), 74 deletions(-) diff --git a/stagit.c b/stagit.c index 7ff036f..b4f3b22 100644 --- 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; - return strcmp(git_reference_shorthand(r1), - git_reference_shorthand(r2)); + 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; - 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 *)))) + 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"); - refs[refcount] = ref; + 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 then shorthand name */ - qsort(refs, refcount, sizeof(git_reference *), refs_cmp); + /* sort by type, date then shorthand name */ + qsort(ris, refcount, sizeof(*ris), 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" - "" - "\n\n" - "\n", - titles[j], ids[j]); - } - - relpath = ""; - name = git_reference_shorthand(r); - - fputs("\n", fp); - - relpath = "../"; - - commitinfo_free(ci); - git_object_free(obj); - obj = NULL; - git_reference_free(dref); - dref = NULL; + for (i = 0, j = 0, count = 0; i < refcount; i++) { + if (j == 0 && git_reference_is_tag(ris[i].ref)) { + if (count) + fputs("
NameLast commit dateAuthor
", fp); - xmlencode(fp, name, strlen(name)); - fputs("", fp); - if (ci->author) - printtimeshort(fp, &(ci->author->when)); - fputs("", fp); - if (ci->author) - xmlencode(fp, ci->author->name, strlen(ci->author->name)); - fputs("

\n", fp); + count = 0; + j = 1; } - /* table footer */ - if (count) - fputs("
", fp); + + /* print header if it has an entry (first). */ + if (++count == 1) { + fprintf(fp, "

%s

" + "\n" + "" + "\n\n" + "\n", + titles[j], ids[j]); + } + + ci = ris[i].ci; + s = git_reference_shorthand(ris[i].ref); + + fputs("\n", fp); } + /* table footer */ + if (count) + fputs("
NameLast commit dateAuthor
", fp); + xmlencode(fp, s, strlen(s)); + fputs("", fp); + if (ci->author) + printtimeshort(fp, &(ci->author->when)); + fputs("", fp); + if (ci->author) + xmlencode(fp, ci->author->name, strlen(ci->author->name)); + 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; }