|
|
refactor get reference, add another feed for tags/releases - stagit - static git page generator |
|
|
 |
git clone git://git.codemadness.org/stagit (git://git.codemadness.org) |
|
|
 |
Log |
|
|
 |
Files |
|
|
 |
Refs |
|
|
 |
README |
|
|
 |
LICENSE |
|
|
|
--- |
|
|
 |
commit d80a163acd47df2bd9ab145be6b249814aa9eceb |
|
|
 |
parent 693c06448972f049d74addbd4942365cd37d92e4 |
|
|
 |
Author: Hiltjo Posthuma <hiltjo@codemadness.org> (mailto://) |
application/vnd.lotus-organizer |
|
|
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(-) |
|
|
|
--- |
|
|
 |
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 |
|
|
 |
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 */ |
|