initial diff support, fix log link
1 files changed, 176 insertions(+), 68 deletions(-) | |||
---|---|---|---|
M | urmoms.c | +176 | -68 |
1@@ -1,3 +1,5 @@
2+#include <sys/stat.h>
3+
4 #include <err.h>
5 #include <libgen.h>
6 #include <limits.h>
7@@ -9,13 +11,48 @@
8
9 static git_repository *repo;
10
11-static const char *relpath;
12-static const char *repodir = ".";
13+static const char *relpath = "";
14+static const char *repodir;
15
16 static char name[255];
17 static char description[255];
18 static int hasreadme, haslicense;
19
20+int
21+writeheader(FILE *fp)
22+{
23+ fprintf(fp, "<!DOCTYPE HTML>"
24+ "<html dir=\"ltr\" lang=\"en\"><head>"
25+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
26+ "<meta http-equiv=\"Content-Language\" content=\"en\" />");
27+ fprintf(fp, "<title>%s%s%s</title>", name, description[0] ? " - " : "", description);
28+ fprintf(fp, "<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />", relpath);
29+ fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />",
30+ name, relpath);
31+ fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
32+ "</head><body><center>");
33+ fprintf(fp, "<h1><img src=\"%slogo.png\" alt=\"\" /> %s</h1>", relpath, name);
34+ fprintf(fp, "<span class=\"desc\">%s</span><br/>", description);
35+ fprintf(fp, "<a href=\"%slog.html\">Log</a> |", relpath);
36+ fprintf(fp, "<a href=\"%sfiles.html\">Files</a>| ", relpath);
37+ fprintf(fp, "<a href=\"%sstats.html\">Stats</a>", relpath);
38+ if (hasreadme)
39+ fprintf(fp, " | <a href=\"%sreadme.html\">README</a>", relpath);
40+ if (haslicense)
41+ fprintf(fp, " | <a href=\"%slicense.html\">LICENSE</a>", relpath);
42+ fprintf(fp, "</center><hr/><pre>");
43+
44+ return 0;
45+}
46+
47+int
48+writefooter(FILE *fp)
49+{
50+ fprintf(fp, "</pre></body></html>");
51+
52+ return 0;
53+}
54+
55 FILE *
56 efopen(const char *name, const char *flags)
57 {
58@@ -67,8 +104,8 @@ xbasename(const char *path)
59 return b;
60 }
61
62-static void
63-printtime(FILE *fp, const git_time *intime, const char *prefix)
64+void
65+printtime(FILE *fp, const git_time *intime)
66 {
67 struct tm *intm;
68 time_t t;
69@@ -91,10 +128,10 @@ printtime(FILE *fp, const git_time *intime, const char *prefix)
70 intm = gmtime(&t);
71 strftime(out, sizeof(out), "%a %b %e %T %Y", intm);
72
73- fprintf(fp, "%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
74+ fprintf(fp, "%s %c%02d%02d\n", out, sign, hours, minutes);
75 }
76
77-static void
78+void
79 printcommit(FILE *fp, git_commit *commit)
80 {
81 const git_signature *sig;
82@@ -103,22 +140,31 @@ printcommit(FILE *fp, git_commit *commit)
83 const char *scan, *eol;
84
85 git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
86- fprintf(fp, "commit <a href=\"commit/%s.html\">%s</a>\n", buf, buf);
87+ fprintf(fp, "commit <a href=\"%scommit/%s.html\">%s</a>\n",
88+ relpath, buf, buf);
89+
90+ if (git_oid_tostr(buf, sizeof(buf), git_commit_parent_id(commit, 0)))
91+ fprintf(fp, "parent <a href=\"%scommit/%s.html\">%s</a>\n",
92+ relpath, buf, buf);
93
94 if ((count = (int)git_commit_parentcount(commit)) > 1) {
95 fprintf(fp, "Merge:");
96 for (i = 0; i < count; ++i) {
97 git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
98- fprintf(fp, " %s", buf);
99+ fprintf(fp, " <a href=\"%scommit/%s.html\">%s</a>",
100+ relpath, buf, buf);
101 }
102- fprintf(fp, "\n");
103+ fputc('\n', fp);
104 }
105 if ((sig = git_commit_author(commit)) != NULL) {
106- fprintf(fp, "Author: <a href=\"author/%s.html\">%s</a> <%s>\n",
107- sig->name, sig->name, sig->email);
108- printtime(fp, &sig->when, "Date: ");
109+ fprintf(fp, "Author: ");
110+ xmlencode(fp, sig->name, strlen(sig->name));
111+ fprintf(fp, " <");
112+ xmlencode(fp, sig->email, strlen(sig->email));
113+ fprintf(fp, ">\nDate: ");
114+ printtime(fp, &sig->when);
115 }
116- fprintf(fp, "\n");
117+ fputc('\n', fp);
118
119 for (scan = git_commit_message(commit); scan && *scan;) {
120 for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */
121@@ -127,79 +173,131 @@ printcommit(FILE *fp, git_commit *commit)
122 fprintf(fp, " %.*s\n", (int) (eol - scan), scan);
123 scan = *eol ? eol + 1 : NULL;
124 }
125- fprintf(fp, "\n");
126+ fputc('\n', fp);
127 }
128
129-static void
130-printcommitdiff(FILE *fp, git_commit *commit)
131+void
132+printshowfile(git_commit *commit)
133 {
134- const git_signature *sig;
135- char buf[GIT_OID_HEXSZ + 1];
136- int i, count;
137- const char *scan, *eol;
138+ const git_diff_delta *delta = NULL;
139+ const git_diff_hunk *hunk = NULL;
140+ const git_diff_line *line = NULL;
141+ git_commit *parent = NULL;
142+ git_tree *commit_tree = NULL, *parent_tree = NULL;
143+ git_patch *patch = NULL;
144+ git_diff *diff = NULL;
145+ size_t i, j, k, ndeltas, nhunks = 0, nhunklines = 0;
146+ char buf[GIT_OID_HEXSZ + 1], path[PATH_MAX];
147+ FILE *fp;
148+ int error;
149
150 git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
151- fprintf(fp, "commit <a href=\"commit/%s.html\">%s</a>\n", buf, buf);
152
153- if ((count = (int)git_commit_parentcount(commit)) > 1) {
154- fprintf(fp, "Merge:");
155- for (i = 0; i < count; ++i) {
156- git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
157- fprintf(fp, " %s", buf);
158+ snprintf(path, sizeof(path), "commit/%s.html", buf);
159+ fp = efopen(path, "w+b");
160+
161+ writeheader(fp);
162+ printcommit(fp, commit);
163+
164+ error = git_commit_parent(&parent, commit, 0);
165+ if (error)
166+ return;
167+
168+ error = git_commit_tree(&commit_tree, commit);
169+ if (error)
170+ return;
171+ error = git_commit_tree(&parent_tree, parent);
172+ if (error)
173+ return;
174+
175+ error = git_diff_tree_to_tree(&diff, repo, commit_tree, parent_tree, NULL);
176+ if (error)
177+ return;
178+
179+ /* TODO: diff stat (files list and insertions/deletions) */
180+
181+ ndeltas = git_diff_num_deltas(diff);
182+ for (i = 0; i < ndeltas; i++) {
183+ if (git_patch_from_diff(&patch, diff, i)) {
184+ git_patch_free(patch);
185+ break; /* TODO: handle error */
186 }
187- fprintf(fp, "\n");
188- }
189- if ((sig = git_commit_author(commit)) != NULL) {
190- fprintf(fp, "Author: <a href=\"author/%s.html\">%s</a> <%s>\n",
191- sig->name, sig->name, sig->email);
192- printtime(fp, &sig->when, "Date: ");
193- }
194- fprintf(fp, "\n");
195
196- for (scan = git_commit_message(commit); scan && *scan;) {
197- for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */
198- ;
199+ delta = git_patch_get_delta(patch);
200+ fprintf(fp, "diff --git a/<a href=\"%s%s\">%s</a> b/<a href=\"%s%s\">%s</a>\n",
201+ relpath, delta->old_file.path, delta->old_file.path,
202+ relpath, delta->new_file.path, delta->new_file.path);
203
204- fprintf(fp, " %.*s\n", (int) (eol - scan), scan);
205- scan = *eol ? eol + 1 : NULL;
206+#if 0
207+ switch (delta->flags) {
208+ case GIT_DIFF_FLAG_BINARY: continue; /* TODO: binary data */
209+ case GIT_DIFF_FLAG_NOT_BINARY: break;
210+ case GIT_DIFF_FLAG_VALID_ID: break; /* TODO: check */
211+ case GIT_DIFF_FLAG_EXISTS: break; /* TODO: check */
212+ }
213+#endif
214+
215+ nhunks = git_patch_num_hunks(patch);
216+ for (j = 0; j < nhunks; j++) {
217+ if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
218+ break; /* TODO: handle error ? */
219+
220+ fprintf(fp, "%s\n", hunk->header);
221+
222+ for (k = 0; ; k++) {
223+ if (git_patch_get_line_in_hunk(&line, patch, j, k))
224+ break;
225+ if (line->old_lineno == -1)
226+ fputc('+', fp);
227+ else if (line->new_lineno == -1)
228+ fputc('-', fp);
229+ else
230+ fputc(' ', fp);
231+ xmlencode(fp, line->content, line->content_len);
232+ }
233+ }
234+ git_patch_free(patch);
235 }
236- fprintf(fp, "\n");
237+ git_diff_free(diff);
238+
239+ writefooter(fp);
240+ fclose(fp);
241 }
242
243 int
244-writeheader(FILE *fp)
245+writelog(FILE *fp)
246 {
247- fprintf(fp, "<!DOCTYPE HTML>"
248- "<html dir=\"ltr\" lang=\"en\"><head>"
249- "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
250- "<meta http-equiv=\"Content-Language\" content=\"en\" />");
251- fprintf(fp, "<title>%s%s%s</title>", name, description[0] ? " - " : "", description);
252- fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
253- "</head><body><center>");
254- fprintf(fp, "<h1><img src=\"%slogo.png\" alt=\"\" /> %s</h1>", relpath, name);
255- fprintf(fp, "<span class=\"desc\">%s</span><br/>", description);
256- fprintf(fp, "<a href=\"%slog.html\">Log</a> |", relpath);
257- fprintf(fp, "<a href=\"%sfiles.html\">Files</a>| ", relpath);
258- fprintf(fp, "<a href=\"%sstats.html\">Stats</a>", relpath);
259- if (hasreadme)
260- fprintf(fp, " | <a href=\"%sreadme.html\">README</a>", relpath);
261- if (haslicense)
262- fprintf(fp, " | <a href=\"%slicense.html\">LICENSE</a>", relpath);
263- fprintf(fp, "</center><hr/><pre>");
264+ git_revwalk *w = NULL;
265+ git_oid id;
266+ git_commit *c = NULL;
267+ size_t i;
268
269- return 0;
270-}
271+ mkdir("commit", 0755);
272
273-int
274-writefooter(FILE *fp)
275-{
276- fprintf(fp, "</pre></body></html>");
277+ git_revwalk_new(&w, repo);
278+ git_revwalk_push_head(w);
279+
280+ i = 0;
281+ while (!git_revwalk_next(&id, w)) {
282+ if (git_commit_lookup(&c, repo, &id))
283+ return 1; /* TODO: error */
284+ printcommit(fp, c);
285+ printshowfile(c);
286+ git_commit_free(c);
287+
288+ /* DEBUG */
289+ i++;
290+ if (i > 100)
291+ break;
292+ }
293+ git_revwalk_free(w);
294
295 return 0;
296 }
297
298+#if 0
299 int
300-writelog(FILE *fp)
301+writeatom(FILE *fp)
302 {
303 git_revwalk *w = NULL;
304 git_oid id;
305@@ -210,14 +308,16 @@ writelog(FILE *fp)
306
307 while (!git_revwalk_next(&id, w)) {
308 if (git_commit_lookup(&c, repo, &id))
309- return 1;
310+ return 1; /* TODO: error */
311 printcommit(fp, c);
312+ printshowfile(c);
313 git_commit_free(c);
314 }
315 git_revwalk_free(w);
316
317 return 0;
318 }
319+#endif
320
321 int
322 writefiles(FILE *fp)
323@@ -251,7 +351,9 @@ writebranches(FILE *fp)
324 git_branch_iterator_new(&branchit, repo, GIT_BRANCH_LOCAL);
325
326 while ((status = git_branch_next(&branchref, &branchtype, branchit)) == GIT_ITEROVER) {
327- git_reference_normalize_name(branchbuf, sizeof(branchbuf), git_reference_name(branchref), GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND);
328+ git_reference_normalize_name(branchbuf, sizeof(branchbuf),
329+ git_reference_name(branchref),
330+ GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND);
331
332 /* fprintf(fp, "branch: |%s|\n", branchbuf); */
333 }
334@@ -337,12 +439,18 @@ main(int argc, char *argv[])
335 hasreadme = 1;
336 }
337
338- fp = efopen("logs.html", "w+b");
339+ fp = efopen("log.html", "w+b");
340 writeheader(fp);
341 writelog(fp);
342 writefooter(fp);
343 fclose(fp);
344
345+#if 0
346+ fp = efopen("atom.xml", "w+b");
347+ writeatom(fp);
348+ fclose(fp);
349+#endif
350+
351 fp = efopen("files.html", "w+b");
352 writeheader(fp);
353 writefiles(fp);