stagit

refactor get reference, add another feed for tags/releases

A separate Atom feed is helpful to ports maintainers to monitor new
tags/releases.

Hiltjo Posthuma contact@arjunchoudhary.com

commit: ed5ebde parent: e654152
2 files changed, 146 insertions(+), 86 deletions(-)
Mstagit.1+4-2
Mstagit.c+142-84
M · stagit.1 +4, -2
 1@@ -1,4 +1,4 @@
 2-.Dd February 6, 2019
 3+.Dd July 19, 2020
 4 .Dt STAGIT 1
 5 .Os
 6 .Sh NAME
 7@@ -42,7 +42,9 @@ cannot be used at the same time.
 8 The following files will be written:
 9 .Bl -tag -width Ds
10 .It atom.xml
11-Atom XML feed
12+Atom XML feed of the last 100 commits.
13+.It tags.xml
14+Atom XML feed of the tags.
15 .It files.html
16 List of files in the latest tree, linking to the file.
17 .It log.html
M · stagit.c +142, -84
  1@@ -248,6 +248,104 @@ err:
  2 	return NULL;
  3 }
  4 
  5+int
  6+refs_cmp(const void *v1, const void *v2)
  7+{
  8+	struct referenceinfo *r1 = (struct referenceinfo *)v1;
  9+	struct referenceinfo *r2 = (struct referenceinfo *)v2;
 10+	time_t t1, t2;
 11+	int r;
 12+
 13+	if ((r = git_reference_is_tag(r1->ref) - git_reference_is_tag(r2->ref)))
 14+		return r;
 15+
 16+	t1 = r1->ci->author ? r1->ci->author->when.time : 0;
 17+	t2 = r2->ci->author ? r2->ci->author->when.time : 0;
 18+	if ((r = t1 > t2 ? -1 : (t1 == t2 ? 0 : 1)))
 19+		return r;
 20+
 21+	return strcmp(git_reference_shorthand(r1->ref),
 22+	              git_reference_shorthand(r2->ref));
 23+}
 24+
 25+int
 26+getrefs(struct referenceinfo **pris, size_t *prefcount)
 27+{
 28+	struct referenceinfo *ris = NULL;
 29+	struct commitinfo *ci = NULL;
 30+	git_reference_iterator *it = NULL;
 31+	const git_oid *id = NULL;
 32+	git_object *obj = NULL;
 33+	git_reference *dref = NULL, *r, *ref = NULL;
 34+	size_t i, refcount;
 35+
 36+	*pris = NULL;
 37+	*prefcount = 0;
 38+
 39+	if (git_reference_iterator_new(&it, repo))
 40+		return -1;
 41+
 42+	for (refcount = 0; !git_reference_next(&ref, it); ) {
 43+		if (!git_reference_is_branch(ref) && !git_reference_is_tag(ref)) {
 44+			git_reference_free(ref);
 45+			ref = NULL;
 46+			continue;
 47+		}
 48+
 49+		switch (git_reference_type(ref)) {
 50+		case GIT_REF_SYMBOLIC:
 51+			if (git_reference_resolve(&dref, ref))
 52+				goto err;
 53+			r = dref;
 54+			break;
 55+		case GIT_REF_OID:
 56+			r = ref;
 57+			break;
 58+		default:
 59+			continue;
 60+		}
 61+		if (!git_reference_target(r) ||
 62+		    git_reference_peel(&obj, r, GIT_OBJ_ANY))
 63+			goto err;
 64+		if (!(id = git_object_id(obj)))
 65+			goto err;
 66+		if (!(ci = commitinfo_getbyoid(id)))
 67+			break;
 68+
 69+		if (!(ris = reallocarray(ris, refcount + 1, sizeof(*ris))))
 70+			err(1, "realloc");
 71+		ris[refcount].ci = ci;
 72+		ris[refcount].ref = r;
 73+		refcount++;
 74+
 75+		git_object_free(obj);
 76+		obj = NULL;
 77+		git_reference_free(dref);
 78+		dref = NULL;
 79+	}
 80+	git_reference_iterator_free(it);
 81+
 82+	/* sort by type, date then shorthand name */
 83+	qsort(ris, refcount, sizeof(*ris), refs_cmp);
 84+
 85+	*pris = ris;
 86+	*prefcount = refcount;
 87+
 88+	return 0;
 89+
 90+err:
 91+	git_object_free(obj);
 92+	git_reference_free(dref);
 93+	commitinfo_free(ci);
 94+	for (i = 0; i < refcount; i++) {
 95+		commitinfo_free(ris[i].ci);
 96+		git_reference_free(ris[i].ref);
 97+	}
 98+	free(ris);
 99+
100+	return -1;
101+}
102+
103 FILE *
104 efopen(const char *name, const char *flags)
105 {
106@@ -361,6 +459,8 @@ writeheader(FILE *fp, const char *title)
107 	fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
108 	fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />\n",
109 		name, relpath);
110+	fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed (tags)\" href=\"%stags.xml\" />\n",
111+		name, relpath);
112 	fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
113 	fputs("</head>\n<body>\n<table><tr><td>", fp);
114 	fprintf(fp, "<a href=\"../%s\"><img src=\"%slogo.png\" alt=\"\" width=\"32\" height=\"32\" /></a>",
115@@ -680,7 +780,7 @@ err:
116 }
117 
118 void
119-printcommitatom(FILE *fp, struct commitinfo *ci)
120+printcommitatom(FILE *fp, struct commitinfo *ci, const char *tag)
121 {
122 	fputs("<entry>\n", fp);
123 
124@@ -697,6 +797,11 @@ printcommitatom(FILE *fp, struct commitinfo *ci)
125 	}
126 	if (ci->summary) {
127 		fputs("<title type=\"text\">", fp);
128+		if (tag) {
129+			fputs("[", fp);
130+			xmlencode(fp, tag, strlen(tag));
131+			fputs("] ", fp);
132+		}
133 		xmlencode(fp, ci->summary, strlen(ci->summary));
134 		fputs("</title>\n", fp);
135 	}
136@@ -732,8 +837,10 @@ printcommitatom(FILE *fp, struct commitinfo *ci)
137 }
138 
139 int
140-writeatom(FILE *fp)
141+writeatom(FILE *fp, int all)
142 {
143+	struct referenceinfo *ris = NULL;
144+	size_t refcount = 0;
145 	struct commitinfo *ci;
146 	git_revwalk *w = NULL;
147 	git_oid id;
148@@ -746,17 +853,34 @@ writeatom(FILE *fp)
149 	xmlencode(fp, description, strlen(description));
150 	fputs("</subtitle>\n", fp);
151 
152-	git_revwalk_new(&w, repo);
153-	git_revwalk_push_head(w);
154-	git_revwalk_simplify_first_parent(w);
155+	/* all commits or only tags? */
156+	if (all) {
157+		git_revwalk_new(&w, repo);
158+		git_revwalk_push_head(w);
159+		git_revwalk_simplify_first_parent(w);
160+		for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
161+			if (!(ci = commitinfo_getbyoid(&id)))
162+				break;
163+			printcommitatom(fp, ci, "");
164+			commitinfo_free(ci);
165+		}
166+		git_revwalk_free(w);
167+	} else {
168+		/* references: tags */
169+		if (getrefs(&ris, &refcount) != -1) {
170+			for (i = 0; i < refcount; i++) {
171+				if (!git_reference_is_tag(ris[i].ref))
172+					continue;
173 
174-	for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
175-		if (!(ci = commitinfo_getbyoid(&id)))
176-			break;
177-		printcommitatom(fp, ci);
178-		commitinfo_free(ci);
179+				printcommitatom(fp, ris[i].ci,
180+				                git_reference_shorthand(ris[i].ref));
181+
182+				commitinfo_free(ris[i].ci);
183+				git_reference_free(ris[i].ref);
184+			}
185+			free(ris);
186+		}
187 	}
188-	git_revwalk_free(w);
189 
190 	fputs("</feed>\n", fp);
191 
192@@ -941,86 +1065,19 @@ writefiles(FILE *fp, const git_oid *id)
193 	return ret;
194 }
195 
196-int
197-refs_cmp(const void *v1, const void *v2)
198-{
199-	struct referenceinfo *r1 = (struct referenceinfo *)v1;
200-	struct referenceinfo *r2 = (struct referenceinfo *)v2;
201-	time_t t1, t2;
202-	int r;
203-
204-	if ((r = git_reference_is_tag(r1->ref) - git_reference_is_tag(r2->ref)))
205-		return r;
206-
207-	t1 = r1->ci->author ? r1->ci->author->when.time : 0;
208-	t2 = r2->ci->author ? r2->ci->author->when.time : 0;
209-	if ((r = t1 > t2 ? -1 : (t1 == t2 ? 0 : 1)))
210-		return r;
211-
212-	return strcmp(git_reference_shorthand(r1->ref),
213-	              git_reference_shorthand(r2->ref));
214-}
215-
216 int
217 writerefs(FILE *fp)
218 {
219 	struct referenceinfo *ris = NULL;
220 	struct commitinfo *ci;
221-	const git_oid *id = NULL;
222-	git_object *obj = NULL;
223-	git_reference *dref = NULL, *r, *ref = NULL;
224-	git_reference_iterator *it = NULL;
225 	size_t count, i, j, refcount;
226 	const char *titles[] = { "Branches", "Tags" };
227 	const char *ids[] = { "branches", "tags" };
228 	const char *s;
229 
230-	if (git_reference_iterator_new(&it, repo))
231+	if (getrefs(&ris, &refcount) == -1)
232 		return -1;
233 
234-	for (refcount = 0; !git_reference_next(&ref, it); ) {
235-		if (!git_reference_is_branch(ref) && !git_reference_is_tag(ref)) {
236-			git_reference_free(ref);
237-			ref = NULL;
238-			continue;
239-		}
240-
241-		switch (git_reference_type(ref)) {
242-		case GIT_REF_SYMBOLIC:
243-			if (git_reference_resolve(&dref, ref))
244-				goto err;
245-			r = dref;
246-			break;
247-		case GIT_REF_OID:
248-			r = ref;
249-			break;
250-		default:
251-			continue;
252-		}
253-		if (!git_reference_target(r) ||
254-		    git_reference_peel(&obj, r, GIT_OBJ_ANY))
255-			goto err;
256-		if (!(id = git_object_id(obj)))
257-			goto err;
258-		if (!(ci = commitinfo_getbyoid(id)))
259-			break;
260-
261-		if (!(ris = reallocarray(ris, refcount + 1, sizeof(*ris))))
262-			err(1, "realloc");
263-		ris[refcount].ci = ci;
264-		ris[refcount].ref = r;
265-		refcount++;
266-
267-		git_object_free(obj);
268-		obj = NULL;
269-		git_reference_free(dref);
270-		dref = NULL;
271-	}
272-	git_reference_iterator_free(it);
273-
274-	/* sort by type, date then shorthand name */
275-	qsort(ris, refcount, sizeof(*ris), refs_cmp);
276-
277 	for (i = 0, j = 0, count = 0; i < refcount; i++) {
278 		if (j == 0 && git_reference_is_tag(ris[i].ref)) {
279 			if (count)
280@@ -1056,10 +1113,6 @@ writerefs(FILE *fp)
281 	if (count)
282 		fputs("</tbody></table><br/>\n", fp);
283 
284-err:
285-	git_object_free(obj);
286-	git_reference_free(dref);
287-
288 	for (i = 0; i < refcount; i++) {
289 		commitinfo_free(ris[i].ci);
290 		git_reference_free(ris[i].ref);
291@@ -1272,7 +1325,12 @@ main(int argc, char *argv[])
292 
293 	/* Atom feed */
294 	fp = efopen("atom.xml", "w");
295-	writeatom(fp);
296+	writeatom(fp, 1);
297+	fclose(fp);
298+
299+	/* Atom feed for tags / releases */
300+	fp = efopen("tags.xml", "w");
301+	writeatom(fp, 0);
302 	fclose(fp);
303 
304 	/* rename new cache file on success */