ref: b41a69d4bd25da331cb771f743a4227b0f68e5c0
parent: 4859c3f71ff0650ea39d3f545f76d1f4ab16e1ba
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Aug 30 16:43:30 EDT 2020
git/serve: you can serve repos read-only now
--- a/git.h
+++ b/git.h
@@ -230,12 +230,14 @@
/* object io */
int resolverefs(Hash **, char *);
int resolveref(Hash *, char *);
-int listrefs(Hash **);
+int listrefs(Hash **, char ***);
Object *ancestor(Object *, Object *);
+int findtwixt(Hash *, int, Hash *, int, Object ***, int *);
Object *readobject(Hash);
Object *clearedobject(Hash, int);
void parseobject(Object *);
int indexpack(char *, char *, Hash);
+int writepack(int, Object **, int, Hash*);
int hasheq(Hash *, Hash *);
Object *ref(Object *);
void unref(Object *);
@@ -244,6 +246,7 @@
/* object sets */
void osinit(Objset *);
+void osclear(Objset *);
void osadd(Objset *, Object *);
int oshas(Objset *, Hash);
Object *osfind(Objset *, Hash);
@@ -254,7 +257,7 @@
void olsfree(Objlist *);
/* util functions */
-int dprint(char *, ...);
+void dprint(int, char *, ...);
void *emalloc(ulong);
void *erealloc(void *, ulong);
char *estrdup(char *);
@@ -272,6 +275,7 @@
int writepkt(Conn*, char*, int);
int flushpkt(Conn*);
int parseuri(char *, char *, char *, char *, char *, char *);
+void initconn(Conn*, int, int);
int dialssh(Conn*, char *, char *, char *, char *);
int dialgit(Conn*, char *, char *, char *, char *);
int dialhttp(Conn*, char *, char *, char *, char *);
--- a/mkfile
+++ b/mkfile
@@ -9,6 +9,7 @@
repack\
save\
send\
+ serve\
walk
RC=\
--- a/objset.c
+++ b/objset.c
@@ -13,7 +13,7 @@
}
void
-osfree(Objset *s)
+osclear(Objset *s)
{
free(s->obj);
}
--- a/pack.c
+++ b/pack.c
@@ -4,11 +4,31 @@
#include "git.h"
-typedef struct Buf Buf;
+typedef struct Buf Buf;
+typedef struct Objmeta Objmeta;
+typedef struct Compout Compout;
+struct Objmeta {
+ int type;
+ char *path;
+ vlong mtime;
+ Hash hash;
+
+ Object *obj;
+ Object *base;
+ Delta *delta;
+ int ndelta;
+};
+
+struct Compout {
+ Biobuf *bfd;
+ DigestState *st;
+};
+
struct Buf {
int len;
int sz;
+ int off;
char *data;
};
@@ -821,9 +841,9 @@
assert(idx != nil);
o = Boffset(idx);
if(Bseek(idx, obj->off, 0) == -1)
- sysfatal("could not seek to object offset");
+ return nil;
if(readpacked(idx, obj, flag) == -1)
- sysfatal("could not reload object %H", obj->hash);
+ return nil;
if(Bseek(idx, o, 0) == -1)
sysfatal("could not restore offset");
cache(obj);
@@ -1084,4 +1104,395 @@
free(valid);
Bterm(f);
return -1;
+}
+
+static int
+dsortcmp(void *pa, void *pb)
+{
+ Objmeta *a, *b;
+ int cmp;
+
+ a = pa;
+ b = pb;
+ if(a->type != b->type)
+ return a->type - b->type;
+ cmp = strcmp(a->path, b->path);
+ if(cmp != 0)
+ return cmp;
+ if(a->mtime != b->mtime)
+ return a->mtime - b->mtime;
+ return memcmp(a->hash.h, b->hash.h, sizeof(a->hash.h));
+}
+
+static int
+timecmp(void *pa, void *pb)
+{
+ Objmeta *a, *b;
+
+ a = pa;
+ b = pb;
+ return b->mtime - a->mtime;
+}
+
+static void
+addmeta(Objmeta **m, int *nm, int type, Hash h, char *path, vlong mtime)
+{
+ static Objset os;
+
+ *m = erealloc(*m, (*nm + 1)*sizeof(Objmeta));
+ memset(&(*m)[*nm], 0, sizeof(Objmeta));
+ (*m)[*nm].type = type;
+ (*m)[*nm].path = path;
+ (*m)[*nm].mtime = mtime;
+ (*m)[*nm].hash = h;
+ *nm += 1;
+}
+
+static int
+loadtree(Objmeta **m, int *nm, Hash tree, char *dpath, vlong mtime, Objset *has)
+{
+ Object *t, *o;
+ Dirent *e;
+ char *p;
+ int i, k;
+
+ if(oshas(has, tree))
+ return 0;
+ if((t = readobject(tree)) == nil)
+ return -1;
+ osadd(has, t);
+ addmeta(m, nm, t->type, t->hash, dpath, mtime);
+ for(i = 0; i < t->tree->nent; i++){
+ e = &t->tree->ent[i];
+ if(oshas(has, e->h))
+ continue;
+ if(e->ismod)
+ continue;
+ k = (e->mode & DMDIR) ? GTree : GBlob;
+ o = clearedobject(e->h, k);
+ p = smprint("%s/%s", dpath, e->name);
+ if(k == GBlob){
+ osadd(has, o);
+ addmeta(m, nm, k, o->hash, p, mtime);
+ }else if(loadtree(m, nm, e->h, p, mtime, has) == -1)
+ return -1;
+ }
+ unref(t);
+ return 0;
+}
+
+static int
+loadcommit(Hash h, Objset *has, Objmeta **m, int *nm)
+{
+ Object *c;
+ int r;
+
+ if(oshas(has, h))
+ return 0;
+ if((c = readobject(h)) == nil)
+ return -1;
+ osadd(has, c);
+ addmeta(m, nm, c->type, c->hash, estrdup(""), c->commit->ctime);
+ r = loadtree(m, nm, c->commit->tree, "", c->commit->ctime, has);
+ unref(c);
+ return r;
+}
+
+static int
+readmeta(Object **commits, int ncommits, Objmeta **m)
+{
+ Objset has;
+ int i, nm;
+
+ *m = nil;
+ nm = 0;
+ osinit(&has);
+ for(i = 0; i < ncommits; i++){
+ dprint(2, "loading commit %H\n", commits[i]->hash);
+ if(loadcommit(commits[i]->hash, &has, m, &nm) == -1){
+ free(*m);
+ return -1;
+ }
+ }
+ osclear(&has);
+ return nm;
+}
+
+static int
+deltasz(Delta *d, int nd)
+{
+ int i, sz;
+ sz = 32;
+ for(i = 0; i < nd; i++)
+ sz += d[i].cpy ? 7 : d[i].len + 1;
+ return sz;
+}
+
+static void
+pickdeltas(Objmeta *meta, int nmeta)
+{
+ Objmeta *m, *p;
+ Object *a, *b;
+ Delta *d;
+ int i, x, nd, sz, pcnt, best;
+
+ pcnt = 0;
+ fprint(2, "deltifying %d objects: 0%%", nmeta);
+ qsort(meta, nmeta, sizeof(Objmeta), dsortcmp);
+ for(i = 0; i < nmeta; i++){
+ m = &meta[i];
+ x = (i*100) / nmeta;
+ if(x > pcnt){
+ pcnt = x;
+ if(pcnt%10 == 0)
+ fprint(2, "\b\b\b\b%3d%%", pcnt);
+ }
+ p = meta;
+ if(i > 10)
+ p = m - 10;
+ if((a = readobject(m->hash)) == nil)
+ sysfatal("missing object %H", m->hash);
+ best = a->size;
+ m->base = nil;
+ m->delta = nil;
+ m->ndelta = 0;
+ for(; p != m; p++){
+ if((b = readobject(p->hash)) == nil)
+ sysfatal("missing object %H", p->hash);
+ d = deltify(a->data, a->size, b->data, b->size, &nd);
+ sz = deltasz(d, nd);
+ if(sz + 32 < best){
+ free(m->delta);
+ best = sz;
+ m->base = b;
+ m->delta = d;
+ m->ndelta = nd;
+ }else
+ free(d);
+ unref(b);
+ }
+ unref(a);
+ }
+ fprint(2, "\b\b\b\b100%%\n");
+}
+
+static int
+compread(void *p, void *dst, int n)
+{
+ Buf *b;
+
+ b = p;
+ if(n > b->sz - b->off)
+ n = b->sz - b->off;
+ memcpy(dst, b->data + b->off, n);
+ b->off += n;
+ return n;
+}
+
+static int
+compwrite(void *p, void *buf, int n)
+{
+ return hwrite(((Compout *)p)->bfd, buf, n, &((Compout*)p)->st);
+}
+
+static int
+hcompress(Biobuf *bfd, void *buf, int sz, DigestState **st)
+{
+ int r;
+ Buf b ={
+ .off=0,
+ .data=buf,
+ .sz=sz,
+ };
+ Compout o = {
+ .bfd = bfd,
+ .st = *st,
+ };
+
+ r = deflatezlib(&o, compwrite, &b, compread, 6, 0);
+ *st = o.st;
+ return r;
+}
+
+static void
+append(char **p, int *len, int *sz, void *seg, int nseg)
+{
+ if(*len + nseg >= *sz){
+ while(*len + nseg >= *sz)
+ *sz += *sz/2;
+ *p = erealloc(*p, *sz);
+ }
+ memcpy(*p + *len, seg, nseg);
+ *len += nseg;
+}
+
+static int
+encodedelta(Objmeta *m, Object *o, Object *b, void **pp)
+{
+ char *p, *bp, buf[16];
+ int len, sz, n, i, j;
+ Delta *d;
+
+ sz = 128;
+ len = 0;
+ p = emalloc(sz);
+
+ /* base object size */
+ buf[0] = b->size & 0x7f;
+ n = b->size >> 7;
+ for(i = 1; n > 0; i++){
+ buf[i - 1] |= 0x80;
+ buf[i] = n & 0x7f;
+ n >>= 7;
+ }
+ append(&p, &len, &sz, buf, i);
+
+ /* target object size */
+ buf[0] = o->size & 0x7f;
+ n = o->size >> 7;
+ for(i = 1; n > 0; i++){
+ buf[i - 1] |= 0x80;
+ buf[i] = n & 0x7f;
+ n >>= 7;
+ }
+ append(&p, &len, &sz, buf, i);
+ for(j = 0; j < m->ndelta; j++){
+ d = &m->delta[j];
+ if(d->cpy){
+ n = d->off;
+ bp = buf + 1;
+ buf[0] = 0x81;
+ buf[1] = 0x00;
+ for(i = 0; i < sizeof(buf); i++) {
+ buf[0] |= 1<<i;
+ *bp++ = n & 0xff;
+ n >>= 8;
+ if(n == 0)
+ break;
+ }
+
+ n = d->len;
+ if(n != 0x10000) {
+ buf[0] |= 0x1<<4;
+ for(i = 0; i < sizeof(buf)-4 && n > 0; i++){
+ buf[0] |= 1<<(i + 4);
+ *bp++ = n & 0xff;
+ n >>= 8;
+ }
+ }
+ append(&p, &len, &sz, buf, bp - buf);
+ }else{
+ n = 0;
+ while(n != d->len){
+ buf[0] = (d->len - n < 127) ? d->len - n : 127;
+ append(&p, &len, &sz, buf, 1);
+ append(&p, &len, &sz, o->data + d->off + n, buf[0]);
+ n += buf[0];
+ }
+ }
+ }
+ *pp = p;
+ return len;
+}
+
+static int
+genpack(int fd, Objmeta *meta, int nmeta, Hash *h)
+{
+ int i, j, n, x, res, pcnt, len, ret;
+ DigestState *st;
+ Biobuf *bfd;
+ Objmeta *m;
+ Object *o;
+ char *p, buf[16];
+
+ st = nil;
+ ret = -1;
+ pcnt = 0;
+ if((fd = dup(fd, -1)) == -1)
+ return -1;
+ if((bfd = Bfdopen(fd, OWRITE)) == nil)
+ return -1;
+ if(hwrite(bfd, "PACK", 4, &st) == -1)
+ return -1;
+ PUTBE32(buf, 2);
+ if(hwrite(bfd, buf, 4, &st) == -1)
+ return -1;
+ PUTBE32(buf, nmeta);
+ if(hwrite(bfd, buf, 4, &st) == -1)
+ return -1;
+ qsort(meta, nmeta, sizeof(Objmeta), timecmp);
+ fprint(2, "writing %d objects: 0%%", nmeta);
+ for(i = 0; i < nmeta; i++){
+ m = &meta[i];
+ x = (i*100) / nmeta;
+ if(x > pcnt){
+ pcnt = x;
+ if(pcnt%10 == 0)
+ fprint(2, "\b\b\b\b%3d%%", pcnt);
+ }
+ if((o = readobject(m->hash)) == nil)
+ return -1;
+ if(m->delta == nil){
+ len = o->size;
+ buf[0] = o->type << 4;
+ buf[0] |= len & 0xf;
+ len >>= 4;
+ for(j = 1; len != 0; j++){
+ assert(j < sizeof(buf));
+ buf[j-1] |= 0x80;
+ buf[j] = len & 0x7f;
+ len >>= 7;
+ }
+ hwrite(bfd, buf, j, &st);
+ if(hcompress(bfd, o->data, o->size, &st) == -1)
+ goto error;
+ }else{
+ n = encodedelta(m, o, m->base, &p);
+ len = n;
+ buf[0] = GRdelta << 4;
+ buf[0] |= len & 0xf;
+ len >>= 4;
+ for(j = 1; len != 0; j++){
+ assert(j < sizeof(buf));
+ buf[j-1] |= 0x80;
+ buf[j] = len & 0x7f;
+ len >>= 7;
+ }
+ hwrite(bfd, buf, j, &st);
+ hwrite(bfd, m->base->hash.h, sizeof(m->base->hash.h), &st);
+ res = hcompress(bfd, p, n, &st);
+ free(p);
+ if(res == -1)
+ goto error;
+ }
+ unref(o);
+ }
+ fprint(2, "\b\b\b\b100%%\n");
+ sha1(nil, 0, h->h, st);
+ if(Bwrite(bfd, h->h, sizeof(h->h)) == -1)
+ goto error;
+ ret = 0;
+error:
+ if(Bterm(bfd) == -1)
+ return -1;
+ return ret;
+}
+
+int
+writepack(int fd, Object **obj, int nobj, Hash *h)
+{
+ Objmeta *meta;
+ int nmeta;
+
+ dprint(1, "reading meta\n");
+ if((nmeta = readmeta(obj, nobj, &meta)) == -1)
+ return -1;
+ dprint(1, "picking deltas\n");
+ pickdeltas(meta, nmeta);
+ dprint(1, "generating pack\n");
+ if(genpack(fd, meta, nmeta, h) == -1){
+ free(meta);
+ return -1;
+ }
+ return 0;
}
--- a/proto.c
+++ b/proto.c
@@ -286,6 +286,14 @@
return 0;
}
+void
+initconn(Conn *c, int rd, int wr)
+{
+ c->type = ConnRaw;
+ c->rfd = rd;
+ c->wfd = wr;
+}
+
int
writephase(Conn *c)
{
@@ -337,7 +345,6 @@
close(c->wfd);
switch(c->type){
case ConnRaw:
-
break;
case ConnSsh:
free(wait());
--- a/ref.c
+++ b/ref.c
@@ -6,7 +6,14 @@
typedef struct Eval Eval;
typedef struct XObject XObject;
+typedef struct Objq Objq;
+enum {
+ Blank,
+ Keep,
+ Drop,
+};
+
struct Eval {
char *str;
char *p;
@@ -22,6 +29,11 @@
XObject *next;
};
+struct Objq {
+ Objq *next;
+ Object *o;
+ int color;
+};
void
eatspace(Eval *ev)
@@ -212,8 +224,117 @@
return 0;
}
+static int
+repaint(Objset *keep, Objset *drop, Object *o)
+{
+ Object *p;
+ int i;
+ if(!oshas(keep, o->hash) && !oshas(drop, o->hash)){
+ dprint(2, "repaint: blank => drop %H\n", o->hash);
+ osadd(drop, o);
+ return 0;
+ }
+ if(oshas(keep, o->hash))
+ dprint(2, "repaint: keep => drop %H\n", o->hash);
+ osadd(drop, o);
+ for(i = 0; i < o->commit->nparent; i++){
+ if((p = readobject(o->commit->parent[i])) == nil)
+ return -1;
+ if(repaint(keep, drop, p) == -1)
+ return -1;
+ unref(p);
+ }
+ return 0;
+}
+
int
+findtwixt(Hash *head, int nhead, Hash *tail, int ntail, Object ***res, int *nres)
+{
+ Objq *q, *e, *n, **p;
+ Objset keep, drop;
+ Object *o, *c;
+ int i;
+
+ e = nil;
+ q = nil;
+ p = &q;
+ osinit(&keep);
+ osinit(&drop);
+ for(i = 0; i < nhead; i++){
+ if((o = readobject(head[i])) == nil)
+ sysfatal("read %H: %r", head[i]);
+ dprint(1, "twixt init: keep %H\n", o->hash);
+ e = emalloc(sizeof(Objq));
+ e->o = o;
+ e->color = Keep;
+ *p = e;
+ p = &e->next;
+ unref(o);
+ }
+ for(i = 0; i < ntail; i++){
+ if((o = readobject(tail[i])) == nil)
+ sysfatal("read %H: %r", tail[i]);
+ dprint(1, "init: drop %H\n", o->hash);
+ e = emalloc(sizeof(Objq));
+ e->o = o;
+ e->color = Drop;
+ *p = e;
+ p = &e->next;
+ unref(o);
+ }
+
+ while(q != nil){
+ if(oshas(&drop, q->o->hash))
+ goto next;
+
+ if(oshas(&keep, q->o->hash) && q->color == Drop){
+ if(repaint(&keep, &drop, q->o) == -1)
+ goto error;
+ }else{
+ dprint(2, "visit: %s %H\n", q->color == Keep ? "keep" : "drop", q->o->hash);
+ if(q->color == Keep)
+ osadd(&keep, q->o);
+ else
+ osadd(&drop, q->o);
+ for(i = 0; i < q->o->commit->nparent; i++){
+ if((c = readobject(q->o->commit->parent[i])) == nil)
+ goto error;
+ dprint(2, "enqueue: %s %H\n", q->color == Keep ? "keep" : "drop", c->hash);
+ n = emalloc(sizeof(Objq));
+ n->color = q->color;
+ n->next = nil;
+ n->o = c;
+ e->next = n;
+ e = n;
+ unref(c);
+ }
+ }
+next:
+ n = q->next;
+ free(q);
+ q = n;
+ }
+ *res = emalloc(keep.nobj*sizeof(Object*));
+ *nres = 0;
+ for(i = 0; i < keep.sz; i++){
+ if(keep.obj[i] != nil && !oshas(&drop, keep.obj[i]->hash)){
+ (*res)[*nres] = keep.obj[i];
+ (*nres)++;
+ }
+ }
+ osclear(&keep);
+ osclear(&drop);
+ return 0;
+error:
+ for(; q != nil; q = n) {
+ n = q->next;
+ free(q);
+ }
+ return -1;
+}
+
+static int
parent(Eval *ev)
{
Object *o, *p;
@@ -231,7 +352,7 @@
return 0;
}
-int
+static int
unwind(Eval *ev, Object **obj, int *idx, int nobj, Object **p, Objset *set, int keep)
{
int i;
@@ -252,7 +373,7 @@
return -1;
}
-int
+static int
range(Eval *ev)
{
Object *a, *b, *p, **all;
@@ -447,42 +568,46 @@
}
int
-readrefdir(Hash **refs, int *nrefs, char *dpath, char *dname)
+readrefdir(Hash **refs, char ***names, int *nrefs, char *dpath, char *dname)
{
Dir *d, *e, *dir;
- char *path, *name;
+ char *path, *name, *sep;
int ndir;
if((ndir = slurpdir(dpath, &dir)) == -1)
return -1;
+ sep = (*dname == '\0') ? "" : "/";
e = dir + ndir;
for(d = dir; d != e; d++){
path = smprint("%s/%s", dpath, d->name);
- name = smprint("%s/%s", dname, d->name);
+ name = smprint("%s%s%s", dname, sep, d->name);
if(d->mode & DMDIR) {
- if(readrefdir(refs, nrefs, path, name) == -1)
- goto nextiter;
+ if(readrefdir(refs, names, nrefs, path, name) == -1)
+ goto noref;
}else{
*refs = erealloc(*refs, (*nrefs + 1)*sizeof(Hash));
- if(resolveref(*refs + *nrefs, name) == -1)
- goto nextiter;
+ *names = erealloc(*names, (*nrefs + 1)*sizeof(char*));
+ if(resolveref(&(*refs)[*nrefs], name) == -1)
+ goto noref;
+ (*names)[*nrefs] = name;
*nrefs += 1;
+ goto next;
}
-nextiter:
- free(name);
- free(path);
+noref: free(name);
+next: free(path);
}
return 0;
}
int
-listrefs(Hash **refs)
+listrefs(Hash **refs, char ***names)
{
int nrefs;
*refs = nil;
+ *names = nil;
nrefs = 0;
- if(readrefdir(refs, &nrefs, ".git/refs", "") == -1){
+ if(readrefdir(refs, names, &nrefs, ".git/refs", "") == -1){
free(*refs);
return -1;
}
--- a/repack.c
+++ b/repack.c
@@ -6,441 +6,7 @@
#define TMPPATH(suff) (".git/objects/pack/repack."suff)
-typedef struct Objmeta Objmeta;
-typedef struct Objq Objq;
-typedef struct Buf Buf;
-typedef struct Compout Compout;
-
-struct Objmeta {
- int type;
- char *path;
- vlong mtime;
- Hash hash;
-
- Object *obj;
- Object *base;
- Delta *delta;
- int ndelta;
-};
-
-struct Objq {
- Objq *next;
- Object *obj;
-};
-
-struct Buf {
- int off;
- int sz;
- uchar *data;
-};
-
-struct Compout {
- Biobuf *bfd;
- DigestState *st;
-};
-
-void
-usage(void)
-{
- fprint(2, "usage: %s [-d]\n", argv0);
- exits("usage");
-}
-
-static int
-dsortcmp(void *pa, void *pb)
-{
- Objmeta *a, *b;
- int cmp;
-
- a = pa;
- b = pb;
- if(a->type != b->type)
- return a->type - b->type;
- cmp = strcmp(a->path, b->path);
- if(cmp != 0)
- return cmp;
- if(a->mtime != b->mtime)
- return a->mtime - b->mtime;
- return memcmp(a->hash.h, b->hash.h, sizeof(a->hash.h));
-}
-
-static int
-timecmp(void *pa, void *pb)
-{
- Objmeta *a, *b;
-
- a = pa;
- b = pb;
- return b->mtime - a->mtime;
-}
-
-static void
-addmeta(Objmeta **m, int *nm, int type, Hash h, char *path, vlong mtime)
-{
- *m = erealloc(*m, (*nm + 1)*sizeof(Objmeta));
- memset(&(*m)[*nm], 0, sizeof(Objmeta));
- (*m)[*nm].type = type;
- (*m)[*nm].path = path;
- (*m)[*nm].mtime = mtime;
- (*m)[*nm].hash = h;
- *nm += 1;
-}
-
-static int
-loadtree(Objmeta **m, int *nm, Hash tree, char *dpath, vlong mtime, Objset *has)
-{
- Object *t, *o;
- Dirent *e;
- char *p;
- int i, k;
-
- if(oshas(has, tree))
- return 0;
- if((t = readobject(tree)) == nil)
- return -1;
- osadd(has, t);
- addmeta(m, nm, t->type, t->hash, dpath, mtime);
- for(i = 0; i < t->tree->nent; i++){
- e = &t->tree->ent[i];
- if(oshas(has, e->h))
- continue;
- if(e->ismod)
- continue;
- k = (e->mode & DMDIR) ? GTree : GBlob;
- o = clearedobject(e->h, k);
- p = smprint("%s/%s", dpath, e->name);
- addmeta(m, nm, k, o->hash, p, mtime);
- if(k == GBlob)
- osadd(has, o);
- else if(loadtree(m, nm, e->h, p, mtime, has) == -1)
- return -1;
- }
- unref(t);
- return 0;
-}
-
-static int
-loadcommit(Objmeta **m, int *nm, Hash h, Objset *has)
-{
- Object *c, *p;
- Objq *q, *e, *t, *n;
- int i;
-
- if((c = readobject(h)) == nil)
- return -1;
- if(c->type != GCommit)
- sysfatal("object %H not commit", c->hash);
- q = emalloc(sizeof(Objq));
- e = q;
- q->next = nil;
- q->obj = c;
- for(; q != nil; q = n){
- c = q->obj;
- if(oshas(has, c->hash))
- goto nextiter;
- osadd(has, c);
- for(i = 0; i < c->commit->nparent; i++){
- if((p = readobject(c->commit->parent[i])) == nil)
- return -1;
- t = emalloc(sizeof(Objq));
- t->next = nil;
- t->obj = p;
- e->next = t;
- e = t;
- }
- addmeta(m, nm, c->type, c->hash, estrdup(""), c->commit->ctime);
- if(loadtree(m, nm, c->commit->tree, "", c->commit->ctime, has) == -1)
- goto error;
-nextiter:
- n = q->next;
- unref(q->obj);
- free(q);
- }
- return 0;
-error:
- for(; q != nil; q = n) {
- n = q->next;
- free(q);
- }
- return -1;
-}
-
-static int
-readmeta(Objmeta **m, Hash *roots, int nroots, Hash * /*leaves */, int /*nleaves*/)
-{
- Objset has;
- int i, nm;
-
- *m = nil;
- nm = 0;
- osinit(&has);
- for(i = 0; i < nroots; i++)
- if(loadcommit(m, &nm, roots[i], &has) == -1){
- free(*m);
- return -1;
- }
- return nm;
-}
-
-static int
-deltasz(Delta *d, int nd)
-{
- int i, sz;
- sz = 32;
- for(i = 0; i < nd; i++)
- sz += d[i].cpy ? 7 : d[i].len + 1;
- return sz;
-}
-
-static void
-pickdeltas(Objmeta *meta, int nmeta)
-{
- Objmeta *m, *p;
- Object *a, *b;
- Delta *d;
- int i, nd, sz, best;
-
- qsort(meta, nmeta, sizeof(Objmeta), dsortcmp);
- for(i = 0; i < nmeta; i++){
- m = &meta[i];
- p = meta;
- if(i > 10)
- p = m - 10;
- if((a = readobject(m->hash)) == nil)
- sysfatal("missing object %H", m->hash);
- best = a->size;
- m->base = nil;
- m->delta = nil;
- m->ndelta = 0;
- for(; p != m; p++){
- if((b = readobject(p->hash)) == nil)
- sysfatal("missing object %H", p->hash);
- d = deltify(a->data, a->size, b->data, b->size, &nd);
- sz = deltasz(d, nd);
- if(sz + 32 < best){
- free(m->delta);
- best = sz;
- m->base = b;
- m->delta = d;
- m->ndelta = nd;
- }else
- free(d);
- unref(b);
- }
- unref(a);
- }
-}
-
-static int
-hwrite(Biobuf *b, void *buf, int len, DigestState **st)
-{
- *st = sha1(buf, len, nil, *st);
- return Bwrite(b, buf, len);
-}
-
int
-compread(void *p, void *dst, int n)
-{
- Buf *b;
-
- b = p;
- if(n > b->sz - b->off)
- n = b->sz - b->off;
- memcpy(dst, b->data + b->off, n);
- b->off += n;
- return n;
-}
-
-int
-compwrite(void *p, void *buf, int n)
-{
- return hwrite(((Compout *)p)->bfd, buf, n, &((Compout*)p)->st);
-}
-
-int
-hcompress(Biobuf *bfd, void *buf, int sz, DigestState **st)
-{
- int r;
- Buf b ={
- .off=0,
- .data=buf,
- .sz=sz,
- };
- Compout o = {
- .bfd = bfd,
- .st = *st,
- };
-
- r = deflatezlib(&o, compwrite, &b, compread, 6, 0);
- *st = o.st;
- return r;
-}
-
-void
-append(char **p, int *len, int *sz, void *seg, int nseg)
-{
- if(*len + nseg >= *sz){
- while(*len + nseg >= *sz)
- *sz += *sz/2;
- *p = erealloc(*p, *sz);
- }
- memcpy(*p + *len, seg, nseg);
- *len += nseg;
-}
-
-int
-encodedelta(Objmeta *m, Object *o, Object *b, void **pp)
-{
- char *p, *bp, buf[16];
- int len, sz, n, i, j;
- Delta *d;
-
- sz = 128;
- len = 0;
- p = emalloc(sz);
-
- /* base object size */
- buf[0] = b->size & 0x7f;
- n = b->size >> 7;
- for(i = 1; n > 0; i++){
- buf[i - 1] |= 0x80;
- buf[i] = n & 0x7f;
- n >>= 7;
- }
- append(&p, &len, &sz, buf, i);
-
- /* target object size */
- buf[0] = o->size & 0x7f;
- n = o->size >> 7;
- for(i = 1; n > 0; i++){
- buf[i - 1] |= 0x80;
- buf[i] = n & 0x7f;
- n >>= 7;
- }
- append(&p, &len, &sz, buf, i);
- for(j = 0; j < m->ndelta; j++){
- d = &m->delta[j];
- if(d->cpy){
- n = d->off;
- bp = buf + 1;
- buf[0] = 0x81;
- buf[1] = 0x00;
- for(i = 0; i < sizeof(buf); i++) {
- buf[0] |= 1<<i;
- *bp++ = n & 0xff;
- n >>= 8;
- if(n == 0)
- break;
- }
-
- n = d->len;
- if(n != 0x10000) {
- buf[0] |= 0x1<<4;
- for(i = 0; i < sizeof(buf)-4 && n > 0; i++){
- buf[0] |= 1<<(i + 4);
- *bp++ = n & 0xff;
- n >>= 8;
- }
- }
- append(&p, &len, &sz, buf, bp - buf);
- }else{
- n = 0;
- while(n != d->len){
- buf[0] = (d->len - n < 127) ? d->len - n : 127;
- append(&p, &len, &sz, buf, 1);
- append(&p, &len, &sz, o->data + d->off + n, buf[0]);
- n += buf[0];
- }
- }
- }
- *pp = p;
- return len;
-}
-
-static int
-writepack(int fd, Objmeta *meta, int nmeta, Hash *h)
-{
- int i, j, n, x, res, pcnt, len, ret;
- DigestState *st;
- Biobuf *bfd;
- Objmeta *m;
- Object *o;
- char *p, buf[16];
-
- st = nil;
- ret = -1;
- pcnt = 0;
- if((fd = dup(fd, -1)) == -1)
- return -1;
- if((bfd = Bfdopen(fd, OWRITE)) == nil)
- return -1;
- if(hwrite(bfd, "PACK", 4, &st) == -1)
- return -1;
- PUTBE32(buf, 2);
- if(hwrite(bfd, buf, 4, &st) == -1)
- return -1;
- PUTBE32(buf, nmeta);
- if(hwrite(bfd, buf, 4, &st) == -1)
- return -1;
- qsort(meta, nmeta, sizeof(Objmeta), timecmp);
- fprint(2, "deltifying %d objects: 0%%", nmeta);
- for(i = 0; i < nmeta; i++){
- m = &meta[i];
- x = (i*100) / nmeta;
- if(x > pcnt){
- pcnt = x;
- if(pcnt%10 == 0)
- fprint(2, "\b\b\b\b%3d%%", pcnt);
- }
- if((o = readobject(m->hash)) == nil)
- return -1;
- if(m->delta == nil){
- len = o->size;
- buf[0] = o->type << 4;
- buf[0] |= len & 0xf;
- len >>= 4;
- for(j = 1; len != 0; j++){
- assert(j < sizeof(buf));
- buf[j-1] |= 0x80;
- buf[j] = len & 0x7f;
- len >>= 7;
- }
- hwrite(bfd, buf, j, &st);
- if(hcompress(bfd, o->data, o->size, &st) == -1)
- goto error;
- }else{
- n = encodedelta(m, o, m->base, &p);
- len = n;
- buf[0] = GRdelta << 4;
- buf[0] |= len & 0xf;
- len >>= 4;
- for(j = 1; len != 0; j++){
- assert(j < sizeof(buf));
- buf[j-1] |= 0x80;
- buf[j] = len & 0x7f;
- len >>= 7;
- }
- hwrite(bfd, buf, j, &st);
- hwrite(bfd, m->base->hash.h, sizeof(m->base->hash.h), &st);
- res = hcompress(bfd, p, n, &st);
- free(p);
- if(res == -1)
- goto error;
- }
- unref(o);
- }
- fprint(2, "\b\b\b\b100%%\n");
- sha1(nil, 0, h->h, st);
- if(Bwrite(bfd, h->h, sizeof(h->h)) == -1)
- goto error;
- ret = 0;
-error:
- if(Bterm(bfd) == -1)
- return -1;
- return ret;
-}
-
-int
cleanup(Hash h)
{
char newpfx[42], dpath[256], fpath[256];
@@ -472,12 +38,19 @@
}
void
+usage(void)
+{
+ fprint(2, "usage: %s [-d]\n", argv0);
+ exits("usage");
+}
+
+void
main(int argc, char **argv)
{
- int fd, nmeta, nrefs;
- Objmeta *meta;
+ char path[128], **names;
+ int fd, nobj, nrefs;
Hash *refs, h;
- char path[128];
+ Object **obj;
Dir rn;
ARGBEGIN{
@@ -490,14 +63,13 @@
gitinit();
refs = nil;
- if((nrefs = listrefs(&refs)) == -1)
+ if((nrefs = listrefs(&refs, &names)) == -1)
sysfatal("load refs: %r");
- if((nmeta = readmeta(&meta, refs, nrefs, nil, 0)) == -1)
- sysfatal("read object metadata: %r");
- pickdeltas(meta, nmeta);
+ if(findtwixt(refs, nrefs, nil, 0, &obj, &nobj) == -1)
+ sysfatal("load twixt: %r");
if((fd = create(TMPPATH("pack.tmp"), OWRITE, 0644)) == -1)
sysfatal("open %s: %r", TMPPATH("pack.tmp"));
- if(writepack(fd, meta, nmeta, &h) == -1)
+ if(writepack(fd, obj, nobj, &h) == -1)
sysfatal("writepack: %r");
if(indexpack(TMPPATH("pack.tmp"), TMPPATH("idx.tmp"), h) == -1)
sysfatal("indexpack: %r");
--- a/send.c
+++ b/send.c
@@ -156,7 +156,7 @@
}
int
-writepack(Conn *c, Update *upd, int nupd)
+writepackdata(Conn *c, Update *upd, int nupd)
{
Objset send, skip;
Object *o, *p;
@@ -372,7 +372,7 @@
if(send){
if(chattygit)
fprint(2, "sending pack...\n");
- if(writepack(c, upd, nupd) == -1)
+ if(writepackdata(c, upd, nupd) == -1)
return -1;
if(readphase(c) == -1)
--- /dev/null
+++ b/serve.c
@@ -1,0 +1,201 @@
+#include <u.h>
+#include <libc.h>
+#include <pool.h>
+
+#include "git.h"
+
+char *pathpfx = "";
+int allowwrite;
+
+int
+fmtpkt(Conn *c, char *fmt, ...)
+{
+ char pkt[Pktmax];
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = vsnprint(pkt, sizeof(pkt), fmt, ap);
+ n = writepkt(c, pkt, n);
+ va_end(ap);
+ return n;
+}
+
+int
+showrefs(Conn *c)
+{
+ int i, ret, nrefs;
+ Hash head, *refs;
+ char **names;
+
+ ret = -1;
+ nrefs = 0;
+ if(resolveref(&head, "HEAD") != -1) {
+ if(fmtpkt(c, "%H HEAD", head) == -1)
+ goto error;
+ }
+ if((nrefs = listrefs(&refs, &names)) == -1)
+ sysfatal("listrefs: %r");
+ for(i = 0; i < nrefs; i++)
+ if(fmtpkt(c, "%H refs/%s\n", refs[i], names[i]) == -1)
+ goto error;
+ if(flushpkt(c) == -1)
+ goto error;
+ ret = 0;
+error:
+ for(i = 0; i < nrefs; i++)
+ free(names[i]);
+ free(names);
+ free(refs);
+ return ret;
+}
+
+int
+negotiate(Conn *c, Hash **head, int *nhead, Hash **tail, int *ntail)
+{
+ char pkt[Pktmax];
+ int n, acked;
+ Object *o;
+
+ if(showrefs(c) == -1)
+ return -1;
+
+ *head = nil;
+ *tail = nil;
+ *nhead = 0;
+ *ntail = 0;
+ acked = 0;
+ while(1){
+ if((n = readpkt(c, pkt, sizeof(pkt))) == -1)
+ goto error;
+ if(n == 0)
+ break;
+ if(strncmp(pkt, "want ", 5) == 0){
+ *head = erealloc(*head, (*nhead + 1)*sizeof(Hash));
+ if(hparse(&(*head)[*nhead], &pkt[5]) == -1){
+ fmtpkt(c, "ERR: garbled want\n");
+ goto error;
+ }
+ *nhead += 1;
+ }
+
+ if(strncmp(pkt, "have ", 5) == 0){
+ *tail = erealloc(*tail, (*ntail + 1)*sizeof(Hash));
+ if(hparse(&(*tail)[*ntail], &pkt[5]) == -1){
+ fmtpkt(c, "ERR: garbled have\n");
+ goto error;
+ }
+ if((o = readobject((*tail)[*ntail])) == nil)
+ continue;
+ if(!acked)
+ if(fmtpkt(c, "ACK %H\r\n", o->hash) == -1)
+ goto error;
+ unref(o);
+ acked = 1;
+ *ntail += 1;
+ }
+ }
+ if(!acked)
+ fmtpkt(c, "NAK\n");
+ return 0;
+error:
+ free(*head);
+ free(*tail);
+ return -1;
+}
+
+int
+servpack(Conn *c)
+{
+ Hash *head, *tail, h;
+ Object **obj;
+ int nhead, ntail, nobj;
+
+ if(negotiate(c, &head, &nhead, &tail, &ntail) == -1)
+ sysfatal("negotiate: %r");
+ dprint(1, "finding twixt\n");
+ if(findtwixt(head, nhead, tail, ntail, &obj, &nobj) == -1)
+ sysfatal("twixt: %r");
+ dprint(1, "writing pack\n");
+ if(writepack(c->wfd, obj, nobj, &h) == -1)
+ sysfatal("send: %r");
+ return 0;
+}
+
+int
+recvpack(Conn *c)
+{
+ USED(c);
+ sysfatal("recvpack: noimpl");
+ return -1;
+}
+
+char*
+parsecmd(char *buf, char *cmd, int ncmd)
+{
+ int i;
+ char *p;
+
+ for(p = buf, i = 0; *p && i < ncmd - 1; i++, p++){
+ if(*p == ' ' || *p == '\t'){
+ cmd[i] = 0;
+ break;
+ }
+ cmd[i] = *p;
+ }
+ while(*p == ' ' || *p == '\t')
+ p++;
+ return p;
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-dw]\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char *p, cmd[32], buf[512], path[512];
+ Conn c;
+
+ ARGBEGIN{
+ case 'd':
+ debug++;
+ chattygit++;
+ break;
+ case 'r':
+ pathpfx = EARGF(usage());
+ if(*pathpfx != '/')
+ sysfatal("path prefix must begin with '/'");
+ break;
+ case 'w':
+ allowwrite++;
+ break;
+ default:
+ usage();
+ break;
+ }ARGEND;
+
+ gitinit();
+ initconn(&c, 0, 1);
+
+ if(readpkt(&c, buf, sizeof(buf)) == -1)
+ sysfatal("readpkt: %r");
+ p = parsecmd(buf, cmd, sizeof(cmd));
+ if(snprint(path, sizeof(path), "%s/%s", pathpfx, p) == sizeof(path))
+ sysfatal("%s: path too long\n", p);
+ if(chdir(path) == -1)
+ sysfatal("cd %s: %r", p);
+ if(access(".git", AREAD) == -1)
+ sysfatal("no git repository");
+ if(strcmp(cmd, "git-fetch-pack") == 0 && allowwrite)
+ recvpack(&c);
+ else if(strcmp(cmd, "git-upload-pack") == 0)
+ servpack(&c);
+ else
+ sysfatal("unsupported command '%s'", cmd);
+ exits(nil);
+}
--- a/util.c
+++ b/util.c
@@ -244,16 +244,15 @@
return s;
}
-int
-dprint(char *fmt, ...)
+void
+dprint(int v, char *fmt, ...)
{
va_list ap;
int n;
- if(!debug)
- return 0;
+ if(v >= debug)
+ return;
va_start(ap, fmt);
n = vfprint(2, fmt, ap);
va_end(ap);
- return n;
}