summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--git-hist.c196
-rw-r--r--git-hist.h36
-rw-r--r--main.c19
4 files changed, 250 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 8cd3ea4..f37033e 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@
NAME = eeshow
OBJS = main.o sch-parse.o sch-render.o lib-parse.o lib-render.o \
gui.o gui-over.o gui-aoi.o \
- file.o git-file.o \
+ file.o git-file.o git-hist.o \
style.o fig.o record.o cro.o diff.o gfx.o dwg.o text.o misc.o
CFLAGS = -g -Wall -Wextra -Wno-unused-parameter -Wshadow \
diff --git a/git-hist.c b/git-hist.c
new file mode 100644
index 0000000..b4b48a6
--- /dev/null
+++ b/git-hist.c
@@ -0,0 +1,196 @@
+/*
+ * git-hist.c - Retrieve revision history from GIT repo
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <alloca.h>
+
+#include "util.h"
+#include "main.h"
+#include "git-hist.h"
+
+
+/*
+ * @@@ we assume to have a single head. That isn't necessarily true, since
+ * each open branch has its own head. Getting this right is for further study.
+ */
+
+
+static struct hist *new_commit(unsigned branch)
+{
+ struct hist *h;
+
+ h = alloc_type(struct hist);
+ h->commit = NULL;
+ h->branch = branch;
+ h->newer = NULL;
+ h->n_newer = 0;
+ h->older = NULL;
+ h->n_older = 0;
+ return h;
+}
+
+
+static void uplink(struct hist *down, struct hist *up)
+{
+ down->newer = realloc(down->newer,
+ sizeof(struct hist *) * (down->n_newer + 1));
+ if (!down->newer) {
+ perror("realloc");
+ exit(1);
+ }
+ down->newer[down->n_newer++] = up;
+}
+
+
+static struct hist *find_commit(struct hist *h, const git_commit *commit)
+{
+ unsigned i;
+ struct hist *found;
+
+ /*
+ * @@@ should probably use
+ * git_oid_equal(git_object_id(a), git_object_id(b))
+ */
+ if (h->commit == commit)
+ return h;
+ for (i = 0; i != h->n_older; i++) {
+ if (h->older[i]->newer[0] != h)
+ continue;
+ found = find_commit(h->older[i], commit);
+ if (found)
+ return found;
+ }
+ return NULL;
+}
+
+
+static void recurse(struct hist *h,
+ unsigned n_branches, struct hist **branches)
+{
+ unsigned n, i, j;
+ struct hist **b;
+ const git_error *e;
+
+ n = git_commit_parentcount(h->commit);
+ if (verbose > 2)
+ fprintf(stderr, "commit %p: %u + %u\n",
+ h->commit, n_branches, n);
+
+ b = alloca(sizeof(struct hist) * (n_branches - 1 + n));
+ n_branches--;
+ memcpy(b, branches, sizeof(struct hist *) * n_branches);
+
+ h->older = alloc_size(sizeof(struct hist *) * n);
+ h->n_older = n;
+
+ for (i = 0; i != n; i++) {
+ git_commit *commit;
+ struct hist *found = NULL;
+
+ if (git_commit_parent(&commit, h->commit, i)) {
+ e = giterr_last();
+ fprintf(stderr, "git_commit_parent: %s\n", e->message);
+ exit(1);
+ }
+ for (j = 0; j != n_branches; j++) {
+ found = find_commit(b[j], commit);
+ if (found)
+ break;
+ }
+ if (found) {
+ uplink(found, h);
+ h->older[i] = found;
+ } else {
+ struct hist *new;
+
+ new = new_commit(n_branches);
+ new->commit = commit;
+ h->older[i] = new;
+ b[n_branches++] = new;
+ uplink(new, h);
+ recurse(new, n_branches, b);
+ }
+ }
+}
+
+
+struct hist *vcs_git_hist(const char *path)
+{
+ struct hist *head;
+ git_repository *repo;
+ git_oid oid;
+ const git_error *e;
+
+ head = new_commit(0);
+
+ git_libgit2_init(); /* @@@ */
+ if (git_repository_open_ext(&repo, path,
+ GIT_REPOSITORY_OPEN_CROSS_FS, NULL)) {
+ e = giterr_last();
+ fprintf(stderr, "%s: %s\n", path, e->message);
+ exit(1);
+ }
+
+ if (git_reference_name_to_id(&oid, repo, "HEAD")) {
+ e = giterr_last();
+ fprintf(stderr, "%s: %s\n",
+ git_repository_path(repo), e->message);
+ exit(1);
+ }
+
+ if (git_commit_lookup(&head->commit, repo, &oid)) {
+ e = giterr_last();
+ fprintf(stderr, "%s: %s\n",
+ git_repository_path(repo), e->message);
+ exit(1);
+ }
+
+ recurse(head, 1, &head);
+ return head;
+}
+
+
+const char *vcs_git_summary(struct hist *h)
+{
+ const char *summary;
+ const git_error *e;
+
+ summary = git_commit_summary(h->commit);
+ if (summary)
+ return summary;
+
+ e = giterr_last();
+ fprintf(stderr, "git_commit_summary: %s\n", e->message);
+ exit(1);
+}
+
+
+void dump_hist(struct hist *h)
+{
+ git_buf buf = { 0 };
+ const git_error *e;
+ unsigned i;
+
+ if (git_object_short_id(&buf, (git_object *) h->commit)) {
+ e = giterr_last();
+ fprintf(stderr, "git_object_short_id: %s\n", e->message);
+ exit(1);
+ }
+ printf("%*s%s %s\n", 2 * h->branch, "", buf.ptr, vcs_git_summary(h));
+ git_buf_free(&buf);
+
+ for (i = 0; i != h->n_older; i++)
+ if (h->older[i]->newer[h->older[i]->n_newer - 1] == h)
+ dump_hist(h->older[i]);
+}
diff --git a/git-hist.h b/git-hist.h
new file mode 100644
index 0000000..838ff3b
--- /dev/null
+++ b/git-hist.h
@@ -0,0 +1,36 @@
+/*
+ * git-hist.h - Retrieve revision history from GIT repo
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef GIT_HIST_H
+#define GIT_HIST_H
+
+#include <git2.h>
+
+
+struct hist {
+ struct git_commit *commit;
+
+ unsigned branch; /* branch index */
+
+ struct hist **newer;
+ unsigned n_newer;
+
+ struct hist **older;
+ unsigned n_older;
+};
+
+
+struct hist *vcs_git_hist(const char *path);
+const char *vcs_git_summary(struct hist *hist);
+void dump_hist(struct hist *h);
+
+#endif /* !GIT_HIST_H */
diff --git a/main.c b/main.c
index 4c3a39c..07e7338 100644
--- a/main.c
+++ b/main.c
@@ -27,6 +27,7 @@
#include "file.h"
#include "lib.h"
#include "sch.h"
+#include "git-hist.h"
#include "gui.h"
#include "main.h"
@@ -48,11 +49,13 @@ void usage(const char *name)
"usage: %s [-r] [-v ...] [[rev:]file.lib ...] [rev:]file.sch\n"
" %*s[-- driver_spec]\n"
" %s [-v ...] -C [rev:]file\n"
+" %s [-v ...] -H path_into_repo\n"
"\n"
" rev git revision\n"
" -r recurse into sub-sheets\n"
" -v increase verbosity of diagnostic output\n"
" -C 'cat' the file to standard output\n"
+" -H show history of repository on standard output\n"
"\n"
"FIG driver spec:\n"
" fig [-t template.fig] [var=value ...]\n"
@@ -75,7 +78,7 @@ void usage(const char *name)
" diff [-o output.pdf] [-s scale] [file.lib ...] file.sch\n"
"\n"
" see PNG\n"
- , name, (int) strlen(name) + 1, "", name);
+ , name, (int) strlen(name) + 1, "", name, name);
exit(1);
}
@@ -87,6 +90,7 @@ int main(int argc, char **argv)
struct file sch_file;
bool recurse = 0;
const char *cat = NULL;
+ const char *history = NULL;
char c;
int arg, dashdash;
bool have_dashdash = 0;
@@ -102,7 +106,7 @@ int main(int argc, char **argv)
break;
}
- while ((c = getopt(dashdash, argv, "rvC:")) != EOF)
+ while ((c = getopt(dashdash, argv, "rvC:H:")) != EOF)
switch (c) {
case 'r':
recurse = 1;
@@ -113,6 +117,9 @@ int main(int argc, char **argv)
case 'C':
cat = optarg;
break;
+ case 'H':
+ history = optarg;
+ break;
default:
usage(*argv);
}
@@ -128,6 +135,14 @@ int main(int argc, char **argv)
return 0;
}
+ if (history) {
+ struct hist *h;
+
+ h = vcs_git_hist(history);
+ dump_hist(h);
+ return 0;
+ }
+
if (dashdash - optind < 1)
usage(*argv);