shithub: riscv

Download patch

ref: 83fe095033418ea6b95204c41e22d96ec133243f
parent: 837c596bdf90e7a723864b65a3bac84155ee66f6
author: Ori Bernstein <ori@eigenstate.org>
date: Sat Dec 14 12:11:01 EST 2024

git/get: support side-band and multi-ack

--- a/sys/src/cmd/git/get.c
+++ b/sys/src/cmd/git/get.c
@@ -166,16 +166,59 @@
 	}
 }
 
+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 @@
 			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,10 +270,13 @@
 	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;
@@ -237,8 +283,9 @@
 		}
 		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 @@
 		enqueueparent(&haveq, o);
 		osadd(&hadobj, o);
 		unref(o);
+		nsent++;
 	}
 	/*
 	 * While we could short circuit this and check if upstream has
@@ -274,7 +322,7 @@
 		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 @@
 		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 @@
 	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");
--- a/sys/src/cmd/git/git.h
+++ b/sys/src/cmd/git/git.h
@@ -4,7 +4,6 @@
 #include <flate.h>
 #include <regexp.h>
 
-typedef struct Capset	Capset;
 typedef struct Conn	Conn;
 typedef struct Hash	Hash;
 typedef struct Delta	Delta;
@@ -82,19 +81,19 @@
 	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	readphase(Conn *);
 int	writephase(Conn *);
 void	closeconn(Conn *);
-void	parsecaps(char *, Capset *);
+void	parsecaps(char *, Conn *);
 
 /* queues */
 void	qinit(Objq*);
--- a/sys/src/cmd/git/proto.c
+++ b/sys/src/cmd/git/proto.c
@@ -27,27 +27,28 @@
 }
 
 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 @@
 		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 @@
 localrepo(char *uri, char *path, int npath)
 {
 	int fd;
-
 	snprint(path, npath, "%s/.git/../", uri);
 	fd = open(path, OREAD);
 	if(fd < 0)
--- a/sys/src/cmd/git/send.c
+++ b/sys/src/cmd/git/send.c
@@ -91,11 +91,9 @@
 	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 @@
 		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 @@
 		 * 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 @@
 
 	if(writepack(c->wfd, ours, nours, theirs, ntheirs, &h) == -1)
 		return -1;
-	if(!cs.report)
+	if(!c->report)
 		return 0;
 
 	if(readphase(c) == -1)
--