diff --git a/sys/src/cmd/git/get.c b/sys/src/cmd/git/get.c index aa40f7990..5f1a79995 100644 --- a/sys/src/cmd/git/get.c +++ b/sys/src/cmd/git/get.c @@ -166,16 +166,59 @@ enqueueparent(Objq *q, Object *o) } } +void +fmtcaps(Conn *c, char *caps, int ncaps) +{ + char *p, *e; + + p = caps; + e = caps + ncaps; + *p = 0; + if(c->multiack) + p = seprint(p, e, " multi_ack"); + if(c->sideband64k) + p = seprint(p, e, " side-band-64k"); + else if(c->sideband) + p = seprint(p, e, " side-band"); + assert(p != e); +} + +int +sbread(Conn *c, char *buf, int nbuf, char **pbuf) +{ + int n; + + assert(nbuf >= Pktmax); + if(!c->sideband && !c->sideband64k){ + *pbuf = buf; + return readn(c->rfd, buf, nbuf); + }else{ + *pbuf = buf+1; + while(1){ + n = readpkt(c, buf, nbuf); + if(n <= 0) + return n; + else if(buf[0] == 1 && n > 1) + return n - 1; + else if(buf[0] == 3) + fprint(2, "error: %s\n", buf+1); + else if(buf[0] < 1 || buf[0] > 3) + fprint(2, "unknown sideband(%c:%d) data: %s\n", buf[0], buf[0], buf+1); + + } + } +} + int fetchpack(Conn *c) { - char buf[Pktmax], *sp[3], *ep; - char *packtmp, *idxtmp, **ref, *caps; + char spinner[] = {'|', '/', '-', '\\'}; + char buf[Pktmax], caps[512], *sp[3], *ep; + char *packtmp, *idxtmp, **ref, *rp; Hash h, *have, *want; int nref, refsz, first, nsent; - int i, l, n, req, pfd; + int i, j, l, n, spin, req, pfd; vlong packsz; - Capset cs; Objset hadobj; Object *o; Objq haveq; @@ -195,9 +238,9 @@ fetchpack(Conn *c) break; if(first && n > strlen(buf)){ - parsecaps(buf + strlen(buf) + 1, &cs); - if(cs.symfrom[0] != 0) - print("symref %s %s\n", cs.symfrom, cs.symto); + parsecaps(buf + strlen(buf) + 1, c); + if(c->symfrom[0] != 0) + print("symref %s %s\n", c->symfrom, c->symto); } first = 0; @@ -227,18 +270,22 @@ fetchpack(Conn *c) if(writephase(c) == -1) sysfatal("write: %r"); req = 0; - caps = " multi_ack"; + fmtcaps(c, caps, sizeof(caps)); for(i = 0; i < nref; i++){ if(hasheq(&have[i], &want[i])) continue; + for(j = 0; j < i; j++) + if(hasheq(&want[i], &want[j])) + goto Next; if((o = readobject(want[i])) != nil){ unref(o); continue; } if(fmtpkt(c, "want %H%s\n", want[i], caps) == -1) sysfatal("could not send want for %H", want[i]); - caps = ""; + caps[0] = 0; req = 1; +Next: continue; } flushpkt(c); @@ -261,6 +308,7 @@ fetchpack(Conn *c) enqueueparent(&haveq, o); osadd(&hadobj, o); unref(o); + nsent++; } /* * While we could short circuit this and check if upstream has @@ -274,7 +322,7 @@ fetchpack(Conn *c) if(oshas(&hadobj, e.o->hash)) continue; if((o = readobject(e.o->hash)) == nil) - sysfatal("missing object we should have: %H", have[i]); + sysfatal("missing object we should have: %H", e.o->hash); if(fmtpkt(c, "have %H", o->hash) == -1) sysfatal("write: %r"); enqueueparent(&haveq, o); @@ -292,9 +340,6 @@ fetchpack(Conn *c) goto showrefs; if(readphase(c) == -1) sysfatal("read: %r"); - if((n = readpkt(c, buf, sizeof(buf))) == -1) - sysfatal("read: %r"); - buf[n] = 0; if((packtmp = smprint(".git/objects/pack/fetch.%d.pack", getpid())) == nil) sysfatal("smprint: %r"); @@ -305,38 +350,56 @@ fetchpack(Conn *c) if((pfd = create(packtmp, ORDWR, 0664)) == -1) sysfatal("could not create %s: %r", packtmp); - fprint(2, "fetching...\n"); - /* - * Work around torvalds git bug: we get duplicate have lines - * somtimes, even though the protocol is supposed to start the - * pack file immediately. - * - * Skip ahead until we read 'PACK' off the wire - */ - while(1){ - if(readn(c->rfd, buf, 4) != 4) - sysfatal("fetch packfile: short read"); - buf[4] = 0; - if(strncmp(buf, "PACK", 4) == 0) - break; - l = strtol(buf, &ep, 16); - if(ep != buf + 4) - sysfatal("fetch packfile: junk pktline"); - if(readn(c->rfd, buf, l-4) != l-4) - sysfatal("fetch packfile: short read"); + fprint(2, "fetching... "); + packsz = 0; + if(c->multiack){ + for(i = 0; i < nsent; i++){ + if(readpkt(c, buf, sizeof(buf)) == -1) + sysfatal("read: %r"); + if(strncmp(buf, "NAK\n", 4) == 0) + break; + if(strncmp(buf, "ACK ", 4) != 0) + sysfatal("bad response: '%s'", buf); + } + } + if(readpkt(c, buf, sizeof(buf)) == -1) + sysfatal("read: %r"); + if(!c->sideband && !c->sideband64k && !c->multiack){ + /* + * Work around torvalds git bug: we get duplicate have lines + * somtimes, even though the protocol is supposed to start the + * pack file immediately. + * + * Skip ahead until we read 'PACK' off the wire + */ + while(1){ + if(readn(c->rfd, buf, 4) != 4) + sysfatal("fetch packfile: short read"); + if(strncmp(buf, "PACK", 4) == 0) + break; + buf[4] = 0; + l = strtol(buf, &ep, 16); + if(ep != buf + 4) + sysfatal("fetch packfile: junk pktline"); + if(readn(c->rfd, buf, l-4) != l-4) + sysfatal("fetch packfile: short read"); + } + if(write(pfd, "PACK", 4) != 4) + sysfatal("write pack header: %r"); + packsz = 4; } - if(write(pfd, "PACK", 4) != 4) - sysfatal("write pack header: %r"); - packsz = 4; + spin = 0; while(1){ - n = read(c->rfd, buf, sizeof buf); + n = sbread(c, buf, sizeof buf, &rp); if(n == 0) break; - if(n == -1 || write(pfd, buf, n) != n) + if(n == -1 || write(pfd, rp, n) != n) sysfatal("fetch packfile: %r"); + if(interactive && spin++ % 100 == 0) + fprint(2, "\b%c", spinner[spin/100 % nelem(spinner)]); packsz += n; } - + fprint(2, "\n"); closeconn(c); if(seek(pfd, 0, 0) == -1) fail(packtmp, idxtmp, "packfile seek: %r"); diff --git a/sys/src/cmd/git/git.h b/sys/src/cmd/git/git.h index 6bad7dff3..78299d848 100644 --- a/sys/src/cmd/git/git.h +++ b/sys/src/cmd/git/git.h @@ -4,7 +4,6 @@ #include #include -typedef struct Capset Capset; typedef struct Conn Conn; typedef struct Hash Hash; typedef struct Delta Delta; @@ -82,19 +81,19 @@ struct Hash { uchar h[20]; }; -struct Capset { - char symfrom[256]; - char symto[256]; - int sideband; - int sideband64k; - int report; -}; - struct Conn { int type; int rfd; int wfd; + /* capabilities */ + char symfrom[256]; + char symto[256]; + char multiack; + char sideband; + char sideband64k; + char report; + /* only used by http */ int cfd; char *url; /* note, first GET uses a different url */ @@ -338,7 +337,7 @@ int gitconnect(Conn *, char *, char *); int readphase(Conn *); int writephase(Conn *); void closeconn(Conn *); -void parsecaps(char *, Capset *); +void parsecaps(char *, Conn *); /* queues */ void qinit(Objq*); diff --git a/sys/src/cmd/git/proto.c b/sys/src/cmd/git/proto.c index 44b34bdd8..921b29c48 100644 --- a/sys/src/cmd/git/proto.c +++ b/sys/src/cmd/git/proto.c @@ -27,27 +27,28 @@ matchcap(char *s, char *cap, int full) } void -parsecaps(char *caps, Capset *cs) +parsecaps(char *caps, Conn *c) { - char *p, *n, *c, *t; + char *p, *n, *s, *t; - memset(cs, 0, sizeof(*cs)); for(p = caps; p != nil; p = n){ n = strchr(p, ' '); if(n != nil) *n++ = 0; if(matchcap(p, "report-status", 1) != nil) - cs->report = 1; + c->report = 1; + if(matchcap(p, "multi_ack", 1) != nil) + c->multiack = 1; else if(matchcap(p, "side-band", 1) != nil) - cs->sideband = 1; + c->sideband = 1; else if(matchcap(p, "side-band-64k", 1) != nil) - cs->sideband64k = 1; - else if((c = matchcap(p, "symref=", 0)) != nil){ - if((t = strchr(c, ':')) == nil) + c->sideband64k = 1; + else if((s = matchcap(p, "symref=", 0)) != nil){ + if((t = strchr(s, ':')) == nil) continue; *t++ = '\0'; - snprint(cs->symfrom, sizeof(cs->symfrom), c); - snprint(cs->symto, sizeof(cs->symto), t); + snprint(c->symfrom, sizeof(c->symfrom), s); + snprint(c->symto, sizeof(c->symto), t); } } } @@ -108,7 +109,7 @@ readpkt(Conn *c, char *buf, int nbuf) sysfatal("pktline: bad length '%s'", len); n -= 4; if(n >= nbuf) - sysfatal("pktline: undersize buffer"); + abort();//sysfatal("pktline: undersize buffer"); if(readn(c->rfd, buf, n) != n) return -1; if(n > 4 && strncmp(buf, "ERR ", 4) == 0){ @@ -468,7 +469,6 @@ static int localrepo(char *uri, char *path, int npath) { int fd; - snprint(path, npath, "%s/.git/../", uri); fd = open(path, OREAD); if(fd < 0) diff --git a/sys/src/cmd/git/send.c b/sys/src/cmd/git/send.c index 68ef97e56..b1b60b0e6 100644 --- a/sys/src/cmd/git/send.c +++ b/sys/src/cmd/git/send.c @@ -91,11 +91,9 @@ sendpack(Conn *c) Hash h, *theirs, *ours; Object *a, *b, *p, *o; char **refs; - Capset cs; Map *map, *m; first = 1; - memset(&cs, 0, sizeof(Capset)); nours = readours(&ours, &refs); theirs = nil; ntheirs = 0; @@ -113,7 +111,7 @@ sendpack(Conn *c) if(n == 0) break; if(first && n > strlen(buf)) - parsecaps(buf + strlen(buf) + 1, &cs); + parsecaps(buf + strlen(buf) + 1, c); first = 0; if(getfields(buf, sp, nelem(sp), 1, " \t\r\n") != 2) @@ -179,7 +177,7 @@ sendpack(Conn *c) * Github doesn't advertise any capabilities, so we can't check * for compatibility. We just need to add it blindly. */ - if(i == 0 && cs.report){ + if(i == 0 && c->report){ buf[n++] = '\0'; n += snprint(buf + n, sizeof(buf) - n, " report-status"); } @@ -195,7 +193,7 @@ sendpack(Conn *c) if(writepack(c->wfd, ours, nours, theirs, ntheirs, &h) == -1) return -1; - if(!cs.report) + if(!c->report) return 0; if(readphase(c) == -1)