refactor get reference, add another feed for tags/releases
A separate Atom feed is helpful to ports maintainers to monitor new tags/releases.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 */