refactor get reference, add another feed for tags/releases

A separate Atom feed is helpful to ports maintainers to monitor new
tags/releases.
This commit is contained in:
Hiltjo Posthuma 2020-07-19 16:41:10 +02:00
parent 693c064489
commit d80a163acd
2 changed files with 146 additions and 86 deletions

View file

@ -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

226
stagit.c
View file

@ -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);
@ -941,86 +1065,19 @@ writefiles(FILE *fp, const git_oid *id)
return ret;
}
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 */