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