ref: 3334f29a9e020b0447d81ac4afc8e1b31452276f
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); break; case -1: /* error */ werrstr("unable to fork: %r"); return FAIL; break; default: /* parent */ 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 gitcommit(char *msg, char *file, ...) { va_list args; char *files[32]; char *f; int nfile = 1; checkinitialized(); files[nfile++] = "-m"; files[nfile++] = msg; files[nfile++] = file; va_start(args, file); while (f = va_arg(args, char*)) { files[nfile++] = f; if (nfile >= sizeof(files)) { files[nfile] = nil; if (!gitcmd(COMMIT, files, nil, nil)) return FAIL; nfile = 3; } } va_end(args); if (nfile > 3) { files[nfile] = nil; if (!gitcmd(COMMIT, files, nil, nil)) return FAIL; } return SUCCESS; } static int gitaddrm(char *F, char *file, va_list args) { char *ofile; int fd; ofile = strdup(file); file = cleanname(ofile); fd = open(indexfile, OWRITE); if (fd < 0) return FAIL; seek(fd, 0, 2); fprint(fd, "%s NOQID 0 %s\n", F, file); free(ofile); while ((ofile = va_arg(args, char*)) != nil) { ofile = strdup(ofile); file = cleanname(ofile); fprint(fd, "%s NOQID 0 %s\n", F, file); free(ofile); } va_end(args); close(fd); return SUCCESS; } int gitadd(char *file, ...) { va_list args; checkinitialized(); va_start(args, file); return gitaddrm("A", file, args); } int gitrm(char *file, ...) { va_list args; checkinitialized(); va_start(args, file); return gitaddrm("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 gitlog(Gitlog **logs, int n, char *commit, ...) { va_list args; char *file; char *files[64]; char num[5]; int ret; int nfile; Logparams p; 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; nfile = 1; snprint(num, sizeof(num), "%d", n); files[nfile++] = "-n"; files[nfile++] = num; if (commit) { files[nfile++] = "-c"; files[nfile++] = commit; } va_start(args, commit); while (file = va_arg(args, char*)) { files[nfile++] = file; if (nfile >= sizeof(files)) { goto Toomanyfiles; } } va_end(args); files[nfile] = nil; ret = gitcmd(LOG, files, gitlogline, &p); if (!ret) return FAIL; return ret; Toomanyfiles: va_end(args); werrstr("too many files in log command"); return FAIL; } 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