ref: 485586299c3f6bb71a58ab5348210ca40ee9e8d2
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