stagit

initial diff support, fix log link

Hiltjo Posthuma contact@arjunchoudhary.com

commit: 69650ca parent: 285cd64
1 files changed, 176 insertions(+), 68 deletions(-)
Murmoms.c+176-68
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, " &lt;");
112+		xmlencode(fp, sig->email, strlen(sig->email));
113+		fprintf(fp, "&gt;\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);