ctucx.git: stagit

ctucx' stagit fork

commit a95a439aaa7d048c25e988b0d9770f5040f0a17f
parent 04eeb7f000b14af31b9923ea7216edcaf0915586
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Mon, 7 Dec 2015 14:07:58 +0100

initial diff support, fix log link
1 file changed, 176 insertions(+), 68 deletions(-)
M
urmoms.c
|
244
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
diff --git a/urmoms.c b/urmoms.c
@@ -1,3 +1,5 @@
+#include <sys/stat.h>
+
 #include <err.h>
 #include <libgen.h>
 #include <limits.h>

@@ -9,13 +11,48 @@
 
 static git_repository *repo;
 
-static const char *relpath;
-static const char *repodir = ".";
+static const char *relpath = "";
+static const char *repodir;
 
 static char name[255];
 static char description[255];
 static int hasreadme, haslicense;
 
+int
+writeheader(FILE *fp)
+{
+	fprintf(fp, "<!DOCTYPE HTML>"
+		"<html dir=\"ltr\" lang=\"en\"><head>"
+		"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
+		"<meta http-equiv=\"Content-Language\" content=\"en\" />");
+	fprintf(fp, "<title>%s%s%s</title>", name, description[0] ? " - " : "", description);
+	fprintf(fp, "<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />", relpath);
+	fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />",
+		name, relpath);
+	fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
+		"</head><body><center>");
+	fprintf(fp, "<h1><img src=\"%slogo.png\" alt=\"\" /> %s</h1>", relpath, name);
+	fprintf(fp, "<span class=\"desc\">%s</span><br/>", description);
+	fprintf(fp, "<a href=\"%slog.html\">Log</a> |", relpath);
+	fprintf(fp, "<a href=\"%sfiles.html\">Files</a>| ", relpath);
+	fprintf(fp, "<a href=\"%sstats.html\">Stats</a>", relpath);
+	if (hasreadme)
+		fprintf(fp, " | <a href=\"%sreadme.html\">README</a>", relpath);
+	if (haslicense)
+		fprintf(fp, " | <a href=\"%slicense.html\">LICENSE</a>", relpath);
+	fprintf(fp, "</center><hr/><pre>");
+
+	return 0;
+}
+
+int
+writefooter(FILE *fp)
+{
+	fprintf(fp, "</pre></body></html>");
+
+	return 0;
+}
+
 FILE *
 efopen(const char *name, const char *flags)
 {

@@ -67,8 +104,8 @@ xbasename(const char *path)
 	return b;
 }
 
-static void
-printtime(FILE *fp, const git_time *intime, const char *prefix)
+void
+printtime(FILE *fp, const git_time *intime)
 {
 	struct tm *intm;
 	time_t t;

@@ -91,10 +128,10 @@ printtime(FILE *fp, const git_time *intime, const char *prefix)
 	intm = gmtime(&t);
 	strftime(out, sizeof(out), "%a %b %e %T %Y", intm);
 
-	fprintf(fp, "%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
+	fprintf(fp, "%s %c%02d%02d\n", out, sign, hours, minutes);
 }
 
-static void
+void
 printcommit(FILE *fp, git_commit *commit)
 {
 	const git_signature *sig;

@@ -103,22 +140,31 @@ printcommit(FILE *fp, git_commit *commit)
 	const char *scan, *eol;
 
 	git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
-	fprintf(fp, "commit <a href=\"commit/%s.html\">%s</a>\n", buf, buf);
+	fprintf(fp, "commit <a href=\"%scommit/%s.html\">%s</a>\n",
+		relpath, buf, buf);
+
+	if (git_oid_tostr(buf, sizeof(buf), git_commit_parent_id(commit, 0)))
+		fprintf(fp, "parent <a href=\"%scommit/%s.html\">%s</a>\n",
+			relpath, buf, buf);
 
 	if ((count = (int)git_commit_parentcount(commit)) > 1) {
 		fprintf(fp, "Merge:");
 		for (i = 0; i < count; ++i) {
 			git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
-			fprintf(fp, " %s", buf);
+			fprintf(fp, " <a href=\"%scommit/%s.html\">%s</a>",
+				relpath, buf, buf);
 		}
-		fprintf(fp, "\n");
+		fputc('\n', fp);
 	}
 	if ((sig = git_commit_author(commit)) != NULL) {
-		fprintf(fp, "Author: <a href=\"author/%s.html\">%s</a> <%s>\n",
-			sig->name, sig->name, sig->email);
-		printtime(fp, &sig->when, "Date:   ");
+		fprintf(fp, "Author: ");
+		xmlencode(fp, sig->name, strlen(sig->name));
+		fprintf(fp, " &lt;");
+		xmlencode(fp, sig->email, strlen(sig->email));
+		fprintf(fp, "&gt;\nDate:   ");
+		printtime(fp, &sig->when);
 	}
-	fprintf(fp, "\n");
+	fputc('\n', fp);
 
 	for (scan = git_commit_message(commit); scan && *scan;) {
 		for (eol = scan; *eol && *eol != '\n'; ++eol)	/* find eol */

@@ -127,79 +173,131 @@ printcommit(FILE *fp, git_commit *commit)
 		fprintf(fp, "    %.*s\n", (int) (eol - scan), scan);
 		scan = *eol ? eol + 1 : NULL;
 	}
-	fprintf(fp, "\n");
+	fputc('\n', fp);
 }
 
-static void
-printcommitdiff(FILE *fp, git_commit *commit)
+void
+printshowfile(git_commit *commit)
 {
-	const git_signature *sig;
-	char buf[GIT_OID_HEXSZ + 1];
-	int i, count;
-	const char *scan, *eol;
+	const git_diff_delta *delta = NULL;
+	const git_diff_hunk *hunk = NULL;
+	const git_diff_line *line = NULL;
+	git_commit *parent = NULL;
+	git_tree *commit_tree = NULL, *parent_tree = NULL;
+	git_patch *patch = NULL;
+	git_diff *diff = NULL;
+	size_t i, j, k, ndeltas, nhunks = 0, nhunklines = 0;
+	char buf[GIT_OID_HEXSZ + 1], path[PATH_MAX];
+	FILE *fp;
+	int error;
 
 	git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
-	fprintf(fp, "commit <a href=\"commit/%s.html\">%s</a>\n", buf, buf);
 
-	if ((count = (int)git_commit_parentcount(commit)) > 1) {
-		fprintf(fp, "Merge:");
-		for (i = 0; i < count; ++i) {
-			git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
-			fprintf(fp, " %s", buf);
+	snprintf(path, sizeof(path), "commit/%s.html", buf);
+	fp = efopen(path, "w+b");
+
+	writeheader(fp);
+	printcommit(fp, commit);
+
+	error = git_commit_parent(&parent, commit, 0);
+	if (error)
+		return;
+
+	error = git_commit_tree(&commit_tree, commit);
+	if (error)
+		return;
+	error = git_commit_tree(&parent_tree, parent);
+	if (error)
+		return;
+
+	error = git_diff_tree_to_tree(&diff, repo, commit_tree, parent_tree, NULL);
+	if (error)
+		return;
+
+	/* TODO: diff stat (files list and insertions/deletions) */
+
+	ndeltas = git_diff_num_deltas(diff);
+	for (i = 0; i < ndeltas; i++) {
+		if (git_patch_from_diff(&patch, diff, i)) {
+			git_patch_free(patch);
+			break; /* TODO: handle error */
 		}
-		fprintf(fp, "\n");
-	}
-	if ((sig = git_commit_author(commit)) != NULL) {
-		fprintf(fp, "Author: <a href=\"author/%s.html\">%s</a> <%s>\n",
-			sig->name, sig->name, sig->email);
-		printtime(fp, &sig->when, "Date:   ");
-	}
-	fprintf(fp, "\n");
 
-	for (scan = git_commit_message(commit); scan && *scan;) {
-		for (eol = scan; *eol && *eol != '\n'; ++eol)	/* find eol */
-			;
+		delta = git_patch_get_delta(patch);
+		fprintf(fp, "diff --git a/<a href=\"%s%s\">%s</a> b/<a href=\"%s%s\">%s</a>\n",
+			relpath, delta->old_file.path, delta->old_file.path,
+			relpath, delta->new_file.path, delta->new_file.path);
 
-		fprintf(fp, "    %.*s\n", (int) (eol - scan), scan);
-		scan = *eol ? eol + 1 : NULL;
+#if 0
+		switch (delta->flags) {
+		case GIT_DIFF_FLAG_BINARY:       continue; /* TODO: binary data */
+		case GIT_DIFF_FLAG_NOT_BINARY:   break;
+		case GIT_DIFF_FLAG_VALID_ID:     break; /* TODO: check */
+		case GIT_DIFF_FLAG_EXISTS:       break; /* TODO: check */
+		}
+#endif
+
+		nhunks = git_patch_num_hunks(patch);
+		for (j = 0; j < nhunks; j++) {
+			if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
+				break; /* TODO: handle error ? */
+
+			fprintf(fp, "%s\n", hunk->header);
+
+			for (k = 0; ; k++) {
+				if (git_patch_get_line_in_hunk(&line, patch, j, k))
+					break;
+				if (line->old_lineno == -1)
+					fputc('+', fp);
+				else if (line->new_lineno == -1)
+					fputc('-', fp);
+				else
+					fputc(' ', fp);
+				xmlencode(fp, line->content, line->content_len);
+			}
+		}
+		git_patch_free(patch);
 	}
-	fprintf(fp, "\n");
+	git_diff_free(diff);
+
+	writefooter(fp);
+	fclose(fp);
 }
 
 int
-writeheader(FILE *fp)
+writelog(FILE *fp)
 {
-	fprintf(fp, "<!DOCTYPE HTML>"
-		"<html dir=\"ltr\" lang=\"en\"><head>"
-		"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
-		"<meta http-equiv=\"Content-Language\" content=\"en\" />");
-	fprintf(fp, "<title>%s%s%s</title>", name, description[0] ? " - " : "", description);
-	fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
-		"</head><body><center>");
-	fprintf(fp, "<h1><img src=\"%slogo.png\" alt=\"\" /> %s</h1>", relpath, name);
-	fprintf(fp, "<span class=\"desc\">%s</span><br/>", description);
-	fprintf(fp, "<a href=\"%slog.html\">Log</a> |", relpath);
-	fprintf(fp, "<a href=\"%sfiles.html\">Files</a>| ", relpath);
-	fprintf(fp, "<a href=\"%sstats.html\">Stats</a>", relpath);
-	if (hasreadme)
-		fprintf(fp, " | <a href=\"%sreadme.html\">README</a>", relpath);
-	if (haslicense)
-		fprintf(fp, " | <a href=\"%slicense.html\">LICENSE</a>", relpath);
-	fprintf(fp, "</center><hr/><pre>");
+	git_revwalk *w = NULL;
+	git_oid id;
+	git_commit *c = NULL;
+	size_t i;
 
-	return 0;
-}
+	mkdir("commit", 0755);
 
-int
-writefooter(FILE *fp)
-{
-	fprintf(fp, "</pre></body></html>");
+	git_revwalk_new(&w, repo);
+	git_revwalk_push_head(w);
+
+	i = 0;
+	while (!git_revwalk_next(&id, w)) {
+		if (git_commit_lookup(&c, repo, &id))
+			return 1; /* TODO: error */
+		printcommit(fp, c);
+		printshowfile(c);
+		git_commit_free(c);
+
+		/* DEBUG */
+		i++;
+		if (i > 100)
+			break;
+	}
+	git_revwalk_free(w);
 
 	return 0;
 }
 
+#if 0
 int
-writelog(FILE *fp)
+writeatom(FILE *fp)
 {
 	git_revwalk *w = NULL;
 	git_oid id;

@@ -210,14 +308,16 @@ writelog(FILE *fp)
 
 	while (!git_revwalk_next(&id, w)) {
 		if (git_commit_lookup(&c, repo, &id))
-			return 1;
+			return 1; /* TODO: error */
 		printcommit(fp, c);
+		printshowfile(c);
 		git_commit_free(c);
 	}
 	git_revwalk_free(w);
 
 	return 0;
 }
+#endif
 
 int
 writefiles(FILE *fp)

@@ -251,7 +351,9 @@ writebranches(FILE *fp)
 	git_branch_iterator_new(&branchit, repo, GIT_BRANCH_LOCAL);
 
 	while ((status = git_branch_next(&branchref, &branchtype, branchit)) == GIT_ITEROVER) {
-		git_reference_normalize_name(branchbuf, sizeof(branchbuf), git_reference_name(branchref), GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND);
+		git_reference_normalize_name(branchbuf, sizeof(branchbuf),
+			git_reference_name(branchref),
+			GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND);
 
 		/* fprintf(fp, "branch: |%s|\n", branchbuf); */
 	}

@@ -337,12 +439,18 @@ main(int argc, char *argv[])
 		hasreadme = 1;
 	}
 
-	fp = efopen("logs.html", "w+b");
+	fp = efopen("log.html", "w+b");
 	writeheader(fp);
 	writelog(fp);
 	writefooter(fp);
 	fclose(fp);
 
+#if 0
+	fp = efopen("atom.xml", "w+b");
+	writeatom(fp);
+	fclose(fp);
+#endif
+
 	fp = efopen("files.html", "w+b");
 	writeheader(fp);
 	writefiles(fp);