ref: 254e6855efd805fda2826f324169009c8b0a4105
dir: /git.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "git.h" char *Enoinit = "not initialized"; //#define DEBUG char *gitdir = "/mnt/git"; int initialized = 0; char *repodir = nil; char *indexfile = nil; enum { FAIL = 0, SUCCESS = 1, }; enum { ADD, BRANCH, COMMIT, DIFF, LOG, RM, }; char *cmds[] = { [ADD] nil, [BRANCH] nil, [COMMIT] "/bin/git/commit", [DIFF] nil, [LOG] "/bin/git/log", [RM] nil, }; char *cname[] = { [ADD] nil, [BRANCH] nil, [COMMIT] "commit", [DIFF] nil, [LOG] "log", [RM] nil, }; static int isinitialized(void) { return initialized; } #define checkinitialized() if (!isinitialized()){ werrstr(Enoinit); return FAIL; } int initgit(char *dir) { if (initialized) { werrstr("already initialized"); return FAIL; } repodir = strdup(dir); indexfile = smprint("%s/.git/INDEX9", dir); if (!repodir || !indexfile) { if (repodir) free(repodir); if (indexfile) free(indexfile); return FAIL; } switch (fork()) { case 0: /* child */ if (chdir(dir) < 0) sysfatal("unable to chdir: %r"); execl("/bin/git/fs", "fs", "-m", gitdir, nil); sysfatal("unable to exec: %r"); break; case -1: /* error */ werrstr("unable to fork: %r"); return FAIL; break; default: /* parent */ break; } initialized = 1; return SUCCESS; } /* args[0] reserved for argv0 */ static int gitcmd(int cmd, char **args, void (*f)(char *line, int n, void *aux), void *aux) { int pid; char *c; Waitmsg *wmsg; int p[2]; Biobuf *bin; char *s; c = cmds[cmd]; if (!c) { werrstr("not implemented"); return FAIL; } args[0] = cname[cmd]; if (pipe(p) < 0) { werrstr("gitcmd: %r"); return FAIL; } // debug output #ifdef DEBUG fprint(2, "command: %s", c); for (char **a = args; *a; a++) fprint(2, " '%s'", *a); fprint(2, "\n"); #endif switch (pid = fork()) { case 0: /* child */ if (chdir(repodir) < 0) sysfatal("%r"); dup(p[1], 1); close(p[1]); close(p[0]); exec(c, args); free(args); break; case -1: /* error */ werrstr("unable to fork: %r"); return FAIL; break; default: /* parent */ free(args); // TODO crashes on gitlog break; } close(p[1]); bin = Bfdopen(p[0], OREAD); while (s = Brdstr(bin, '\n', 1)) { if (f) f(s, Blinelen(bin), aux); } Bterm(bin); for (;;) { wmsg = wait(); if (wmsg->pid == pid) break; } if (wmsg->msg && *wmsg->msg) { werrstr("%s", wmsg->msg); free(wmsg); return FAIL; } if (wmsg) free(wmsg); return SUCCESS; } int gitcommitl(char *msg, char **files) { char **args; char **f; int n, i; checkinitialized(); for (f = files, n = 0; *f; f++) n++; args = mallocz((n + 1 + 3) * sizeof(char*), 1); i = 0; args[i++] = nil; args[i++] = "-m"; args[i++] = msg; memcpy(&args[i], files, n * sizeof(char*)); i = gitcmd(COMMIT, args, nil, nil); return i; } static void filllist(char ***list, int start, int len, va_list args) { char *f; if (*list == nil) { start = 0; len = 32; *list = mallocz(len * sizeof(char*), 1); if (*list == nil) sysfatal("%r"); } while (f = va_arg(args, char*)) { if (start > (len - 2)) { len += 32; *list = realloc(*list, len * sizeof(char*)); if (*list == nil) sysfatal("%r"); (*list)[start++] = f; } } (*list)[start] = nil; } int gitcommit(char *msg, char *file, ...) { va_list args; char **files; int n, l; checkinitialized(); n = 0; l = 32; files = mallocz(l, 1); files[n++] = file; va_start(args, file); filllist(&files, n, l, args); va_end(args); files[n] = nil; return gitcommitl(msg, files); } static int gitaddrm(char *F, char **files) { char **f; int fd; fd = open(indexfile, OWRITE); if (fd < 0) return FAIL; seek(fd, 0, 2); for (f = files; *f; f++) { fprint(fd, "%s NOQID 0 %s\n", F, *f); } close(fd); return SUCCESS; } static int gitaddrmargs(char *F, char *file, va_list args) { char **files; int i, l; l = 32; i = 0; files = mallocz(l * sizeof(char*), 1); files[i++] = file; filllist(&files, i, l, args); va_end(args); i = gitaddrm(F, files); free(files); return i; } int gitaddl(char **files) { checkinitialized(); return gitaddrm("A", files); } int gitadd(char *file, ...) { va_list args; checkinitialized(); va_start(args, file); return gitaddrmargs("A", file, args); } int gitrml(char **files) { checkinitialized(); return gitaddrm("R", files); } int gitrm(char *file, ...) { va_list args; checkinitialized(); va_start(args, file); return gitaddrmargs("R", file, args); } typedef struct Logparams Logparams; struct Logparams { Gitlog *logs; int n; int last; }; static void gitlogline(char *s, int n, void *aux) { char *toks[2]; Gitlog *l; Logparams *p = (Logparams *)aux; char *o; if (n > 5 && strncmp("Hash:", s, 5) == 0) { /* new entry */ p->last++; l = &p->logs[p->last]; tokenize(s, toks, 2); l->hash = strdup(toks[1]); free(s); return; } if (n > 7 && strncmp("Author:", s, 7) == 0) { l = &p->logs[p->last]; tokenize(s, toks, 2); l->author = strdup(toks[1]); free(s); return; } if (n > 5 && strncmp("Date:", s, 5) == 0) { l = &p->logs[p->last]; tokenize(s, toks, 2); l->date = strdup(toks[1]); free(s); return; } if (n >= 1 && s[0] == '\t') { l = &p->logs[p->last]; if (!l->message) { l->message = strdup(s+1); free(s); return; } o = l->message; l->message = mallocz(strlen(o) + strlen(s+1) + 2, 1); // + newline + nil sprint(l->message, "%s\n%s", o, s+1); free(o); free(s); return; } } int gitlogl(Gitlog **logs, int n, char *commit, char **files) { Logparams p; char num[5]; char **argv; int nfiles; int l, i; checkinitialized(); if (n <= 0) { sysfatal("gitlog: n <= 0"); } if (!logs) { sysfatal("No gitlog target pointer"); } *logs = mallocz((n+1) * sizeof(Gitlog), 1); if (!*logs) { sysfatal("%r"); } p.logs = *logs; p.n = n; p.last = -1; for (argv = files, nfiles = 0; *argv; argv++) nfiles++; l = nfiles; l += 2; /* ending nil, starting argv0 */ l += 2; /* num args */ if (commit) l += 2; argv = mallocz(l * sizeof(char*), 1); i = 1; /* reserve [0] for argv0 */ snprint(num, sizeof(num), "%d", n); argv[i++] = "-n"; argv[i++] = num; if (commit) { argv[i++] = "-c"; argv[i++] = commit; } memcpy(&argv[i], files, nfiles * sizeof(char*)); return gitcmd(LOG, argv, gitlogline, &p); } int gitlog(Gitlog **logs, int n, char *commit, ...) { va_list args; char **files = nil; int ret; checkinitialized(); va_start(args, commit); filllist(&files, 0, 0, args); va_end(args); ret = gitlogl(logs, n, commit, files); free(files); return ret; } int freegitlogs(Gitlog *logs) { for (Gitlog *l = logs; l->hash; l++) { if (l->hash) free(l->hash); if (l->author) free(l->author); if (l->date) free(l->date); if (l->message) free(l->message); } free(logs); return SUCCESS; } #ifdef DEBUG #undef DEBUG #endif