From: ori@eigenstate.org
To: 9front@9front.org
Subject: [9front] git/get: keep sending what we have until we get an ack
Date: Sun, 29 May 2022 11:57:12 -0400 [thread overview]
Message-ID: <DEF9D76D97221A216DBD06FC7698F944@eigenstate.org> (raw)
Git9 is sloppy about telling git what commits we have.
We would list the commits at the tip of the branch, but not
walk down it, which means we would request too much data if
our local branches were ahead of the remote.
This patch changes that, sending the tips *and* the first
256 commits after them, so that git can produce a better
pack for us, with fewer redundant commits.
Note, I only do one round of negotiation, because I wasn't
able to get multiple rounds to work with the https transport.
diff 01a6de812c2fd38c9a28036300e3c2c7dc8441f5 uncommitted
--- a/sys/src/cmd/git/get.c
+++ b/sys/src/cmd/git/get.c
@@ -177,17 +177,35 @@
exits(buf);
}
+void
+enqueueparent(Objq *q, Object *o)
+{
+ Object *p;
+ int i;
+
+ if(o->type != GCommit)
+ return;
+ for(i = 0; i < o->commit->nparent; i++){
+ if((p = readobject(o->commit->parent[i])) == nil)
+ continue;
+ qput(q, p, 0);
+ unref(p);
+ }
+}
+
int
fetchpack(Conn *c)
{
char buf[Pktmax], *sp[3], *ep;
- char *packtmp, *idxtmp, **ref;
+ char *packtmp, *idxtmp, **ref, *caps;
Hash h, *have, *want;
- int nref, refsz, first;
- int i, n, l, req, pfd;
+ int nref, refsz, first, nsent;
+ int i, l, n, req, pfd;
vlong packsz;
- Objset hadobj;
+ Objset hadobj, wantobj;
Object *o;
+ Objq haveq;
+ Qelt e;
nref = 0;
refsz = 16;
@@ -234,37 +252,69 @@
if(writephase(c) == -1)
sysfatal("write: %r");
req = 0;
+ caps = " multi_ack";
+ osinit(&wantobj);
for(i = 0; i < nref; i++){
- if(hasheq(&have[i], &want[i]))
+ if(hasheq(&have[i], &want[i]) || oshas(&wantobj, want[i]))
continue;
if((o = readobject(want[i])) != nil){
unref(o);
continue;
}
- n = snprint(buf, sizeof(buf), "want %H\n", want[i]);
- if(writepkt(c, buf, n) == -1)
+ if(fmtpkt(c, "want %H%s\n", want[i], caps) == -1)
sysfatal("could not send want for %H", want[i]);
+ osadd(&wantobj, o);
+ caps = "";
req = 1;
}
+ osclear(&wantobj);
flushpkt(c);
+
+ nsent = 0;
+ qinit(&haveq);
osinit(&hadobj);
+ /*
+ * We know we have these objects, and we want to make sure that
+ * they end up at the front of the queue. Send the 'have lines'
+ * first, and then enqueue their parents for a second round of
+ * sends.
+ */
for(i = 0; i < nref; i++){
if(hasheq(&have[i], &Zhash) || oshas(&hadobj, have[i]))
continue;
if((o = readobject(have[i])) == nil)
sysfatal("missing object we should have: %H", have[i]);
+ if(fmtpkt(c, "have %H", o->hash) == -1)
+ sysfatal("write: %r");
+ enqueueparent(&haveq, o);
osadd(&hadobj, o);
- unref(o);
- n = snprint(buf, sizeof(buf), "have %H\n", have[i]);
- if(writepkt(c, buf, n + 1) == -1)
- sysfatal("could not send have for %H", have[i]);
+ unref(o);
}
+ /*
+ * While we could short circuit this and check if upstream has
+ * acked our objects, for the first 256 haves, this is simple
+ * enough.
+ *
+ * Also, doing multiple rounds of reference discovery breaks
+ * when using smart http.
+ */
+ while(req && qpop(&haveq, &e) && nsent < 256){
+ if(oshas(&hadobj, e.o->hash))
+ continue;
+ if((o = readobject(e.o->hash)) == nil)
+ sysfatal("missing object we should have: %H", have[i]);
+ if(fmtpkt(c, "have %H", o->hash) == -1)
+ sysfatal("write: %r");
+ enqueueparent(&haveq, o);
+ osadd(&hadobj, o);
+ unref(o);
+ nsent++;
+ }
osclear(&hadobj);
+ qclear(&haveq);
if(!req)
flushpkt(c);
-
- n = snprint(buf, sizeof(buf), "done\n");
- if(writepkt(c, buf, n) == -1)
+ if(fmtpkt(c, "done\n") == -1)
sysfatal("write: %r");
if(!req)
goto showrefs;
@@ -298,9 +348,9 @@
if(strncmp(buf, "PACK", 4) == 0)
break;
l = strtol(buf, &ep, 16);
- if(l == 0 || ep != buf + 4)
+ if(ep != buf + 4)
sysfatal("fetch packfile: junk pktline");
- if(readn(c->rfd, buf, l) != l)
+ if(readn(c->rfd, buf, l-4) != l-4)
sysfatal("fetch packfile: short read");
}
if(write(pfd, "PACK", 4) != 4)
--- a/sys/src/cmd/git/git.h
+++ b/sys/src/cmd/git/git.h
@@ -313,6 +313,7 @@
/* proto handling */
int readpkt(Conn*, char*, int);
int writepkt(Conn*, char*, int);
+int fmtpkt(Conn*, char*, ...);
int flushpkt(Conn*);
void initconn(Conn*, int, int);
int gitconnect(Conn *, char *, char *);
--- a/sys/src/cmd/git/proto.c
+++ b/sys/src/cmd/git/proto.c
@@ -94,6 +94,20 @@
}
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
flushpkt(Conn *c)
{
dprint(1, "<=w= 0000\n");
--- a/sys/src/cmd/git/serve.c
+++ b/sys/src/cmd/git/serve.c
@@ -9,20 +9,6 @@
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;
next reply other threads:[~2022-05-29 15:58 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-05-29 15:57 ori [this message]
2022-06-04 2:03 ` ori
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DEF9D76D97221A216DBD06FC7698F944@eigenstate.org \
--to=ori@eigenstate.org \
--cc=9front@9front.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).