shithub: front

Download patch

ref: 8c43c1ae1adf0409ddd8e268474600cb6341b2c3
parent: 5db1e72fff8404826e77d1e295f860505932c5ff
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Jul 27 13:37:47 EDT 2025

git: send more known refs, in hopes of smaller fetches

we used to only say we had the branches we were pulling,
but that meant that when pulling a new branch, we would
say we had nothing, leading to a lot of duplicate data
getting sent.

This change the 20 most recent local branches as refs
we know about, in the hopes that the remote can compute
a smaller delta.

The long term fix would be to write the request in
parallel with reading the response, which would allow
us to provide a great deal more information without
blocking, but this will do the job for now.

--- a/sys/src/cmd/git/get.c
+++ b/sys/src/cmd/git/get.c
@@ -5,6 +5,8 @@
 
 char *fetchbranch;
 char *upstream = "origin";
+Hash heads[64];
+int nheads;
 int listonly;
 
 /*
@@ -302,7 +304,7 @@
 			continue;
 		for(j = 0; j < i; j++)
 			if(hasheq(&want[i], &want[j]))
-				goto Next;
+				continue;
 		if((o = readobject(want[i])) != nil){
 			unref(o);
 			continue;
@@ -311,7 +313,6 @@
 			sysfatal("could not send want for %H", want[i]);
 		caps[0] = 0;
 		req = 1;
-Next:		continue;
 	}
 	flushpkt(c);
 
@@ -337,6 +338,22 @@
 		nsent++;
 	}
 	/*
+	 * The other branches we have probably make sense to send,
+	 * since often we'll be pulling a new branch with objects
+	 * that we already have; it's not entirely clear what we
+	 * want to do here.
+	 */
+	for(i = 0; i < nheads; i++){
+		if((o = readobject(heads[i])) == nil)
+			sysfatal("missing exected object: %H", have[i]);
+		if(fmtpkt(c, "have %H", o->hash) == -1)
+			sysfatal("write: %r");
+		enqueueparent(&haveq, o);
+		osadd(&hadobj, o);
+		unref(o);
+		nsent++;
+	}
+	/*
 	 * While we could short circuit this and check if upstream has
 	 * acked our objects, for the first 256 haves, this is simple
 	 * enough.
@@ -467,7 +484,14 @@
 	case 'u':	upstream=EARGF(usage());	break;
 	case 'd':	chattygit++;			break;
 	case 'l':	listonly++;			break;
-	default:	usage();			break;
+	case 'h':
+		if(nheads < nelem(heads))
+			if(hparse(&heads[nheads], EARGF(usage())) == 0)
+				nheads++;
+		break;
+	default:
+		usage();
+		break;
 	}ARGEND;
 
 	gitinit();
--- a/sys/src/cmd/git/pull
+++ b/sys/src/cmd/git/pull
@@ -9,7 +9,19 @@
 	dflag=()
 	if(! ~ $#debug 0)
 		dflag='-d'
-	{git/get $dflag -u $upstream $url >[2=3] || die $status} | awk '
+	heads=`$nl{
+		# safe to split in awk, branch names may not
+		# have spaces or newlines.
+		git/query `$nl{
+			cd .git/refs && \
+			walk -f -emp heads remotes | \
+			sort -rn | \
+			awk '{print $2}'
+		}
+	}
+	if(! ~ $#heads 0)
+		heads='-h'^$heads
+	{git/get $dflag -u $upstream $heads $url >[2=3] || die $status} | awk '
 	/^remote refs\/(heads|tags)\//{
 		ref=$2
 		hash=$3
--