git: 9front

Download patch

ref: 3bd380117136b17d8f14f3da254ff491f257bfd5
parent: a0dc0a9b1b81655ba2e9fa87f74f07a4fcea825d
author: Ori Bernstein <ori@eigenstate.org>
date: Sat Oct 18 13:40:15 EDT 2025

git/query: support expanding truncated hashes to full hashes

--- a/sys/src/cmd/git/git.h
+++ b/sys/src/cmd/git/git.h
@@ -281,6 +281,7 @@
 int	findtwixt(Hash *, int, Hash *, int, Object ***, int *);
 Object	*readobject(Hash);
 Object	*clearedobject(Hash, int);
+int	expandprefix(Hash*, Hash, int);
 void	parseobject(Object *);
 int	indexpack(char *, char *, Hash);
 int	writepack(int, Hash*, int, Hash*, int, Hash*);
@@ -321,6 +322,7 @@
 int	showprogress(int, int);
 u64int	murmurhash2(void*, usize);
 Qid	parseqid(char*);
+int	charval(int);
 
 /* packing */
 void	dtinit(Dtab *, Object*);
--- a/sys/src/cmd/git/pack.c
+++ b/sys/src/cmd/git/pack.c
@@ -677,8 +677,22 @@
 	return -1;
 }
 
+int
+hashcmp(uchar *a, uchar *b, uint nbit)
+{
+	int x, y, i, r;
+
+	i = nbit/8;
+	r = memcmp(a, b, i);
+	if(r != 0 || nbit % 8 == 0)
+		return r;
+	x = (a[i+1] & 0xff00>>nbit) & 0xff;
+	y = (b[i+1] & 0xff00>>nbit) & 0xff;
+	return x - y;
+}
+
 vlong
-searchindex(char *idx, int nidx, Hash h)
+searchindex(char *idx, int nidx, Hash h, int npfx, Hash *hret)
 {
 	int lo, hi, hidx, i, r, nent;
 	vlong o, oo;
@@ -687,6 +701,8 @@
 	o = 8;
 	if(nidx < 8 + 256*4)
 		return -1;
+	if(npfx < 8)
+		return -1;
 	/*
 	 * Read the fanout table. The fanout table
 	 * contains 256 entries, corresponsding to
@@ -713,12 +729,13 @@
 	 * entry may exist in, search them
 	 */
 	r = -1;
+	s = nil;
 	hidx = -1;
 	o = 8 + 256*4;
 	while(lo < hi){
 		hidx = (hi + lo)/2;
 		s = idx + o + hidx*sizeof(h.h);
-		r = memcmp(h.h, s, sizeof(h.h));
+		r = hashcmp(h.h, s, npfx);
 		if(r < 0)
 			hi = hidx;
 		else if(r > 0)
@@ -728,6 +745,8 @@
 	}
 	if(r != 0)
 		goto notfound;
+	if(hret != nil)
+		memcpy(hret, s, sizeof(Hash));
 
 	/*
 	 * We found the entry. If it's 32 bits, then we
@@ -1032,7 +1051,7 @@
 	retried = 0;
 retry:
 	for(i = 0; i < npackf; i++){
-		o = searchindex(packf[i].idx, packf[i].nidx, h);
+		o = searchindex(packf[i].idx, packf[i].nidx, h, SHA1dlen*8, nil);
 		if(o != -1){
 			if((f = openpack(&packf[i])) == nil)
 				goto error;
@@ -1070,6 +1089,38 @@
 error:
 	free(new);
 	return nil;
+}
+
+int
+expandprefix(Hash *rh, Hash h, int npfx)
+{
+	int i, fd, ndir;
+	char buf[128];
+	Dir *d;
+
+	refreshpacks();
+	if(npfx < 8 || npfx % 4 != 0)
+		return -1;
+	for(i = 0; i < npackf; i++)
+		if(searchindex(packf[i].idx, packf[i].nidx, h, npfx, rh) != -1)
+			return 0;
+	sprint(buf, ".git/objects/%x", h.h[0]);
+	if((fd = open(buf, OREAD)) == -1)
+		return -1;
+	ndir = dirreadall(fd, &d);
+	close(fd);
+	if(ndir == -1)
+		return -1;
+	for(i = 0; i < ndir; i++){
+		snprint(buf, sizeof(buf), "%x%s", h.h[0], d[i].name);
+		if(hparse(rh, buf) == 0 && hashcmp(h.h, rh->h, npfx) == 0){
+			free(d);
+			return 0;
+		}
+
+	}
+	free(d);
+	return -1;
 }
 
 /*
--- a/sys/src/cmd/git/ref.c
+++ b/sys/src/cmd/git/ref.c
@@ -350,6 +350,24 @@
 	return 0;
 }
 
+static int
+matchpfx(Hash *h, char *ref)
+{
+	int i, c;
+	Hash pfx;
+	char *p;
+
+	memset(&pfx, 0, sizeof(Hash));
+	for(i = 0, p = ref; *p; p++, i++){
+		if((c = charval(*p)) == -1)
+			return -1;
+		pfx.h[i/2] |= c;
+		if((i & 1) == 0)
+			pfx.h[i/2] <<= 4;
+	}
+	return expandprefix(h, pfx, i*4);
+}
+
 int
 readref(Hash *h, char *ref)
 {
@@ -357,7 +375,6 @@
 	char buf[256], s[256], **pfx;
 	int r, f, n;
 
-	/* TODO: support hash prefixes */
 	if((r = hparse(h, ref)) != -1)
 		return r;
 	if(strcmp(ref, "HEAD") == 0){
@@ -383,6 +400,8 @@
 		close(f);
 		goto found;
 	}
+	if((r = matchpfx(h, ref)) != -1)
+		return r;
 	return -1;
 
 found:
--- a/sys/src/cmd/git/util.c
+++ b/sys/src/cmd/git/util.c
@@ -85,8 +85,8 @@
 	return memcmp(a->h, b->h, sizeof(a->h)) == 0;
 }
 
-static int
-charval(int c, int *err)
+int
+charval(int c)
 {
 	if(c >= '0' && c <= '9')
 		return c - '0';
@@ -94,7 +94,7 @@
 		return c - 'a' + 10;
 	if(c >= 'A' && c <= 'F')
 		return c - 'A' + 10;
-	*err = 1;
+	werrstr("invalid hex char");
 	return -1;
 }
 
@@ -259,18 +259,14 @@
 int
 hparse(Hash *h, char *b)
 {
-	int i, err;
+	int i, c0, c1;
 
-	err = 0;
-	for(i = 0; i < sizeof(h->h); i++){
-		err = 0;
-		h->h[i] = 0;
-		h->h[i] |= ((charval(b[2*i], &err) & 0xf) << 4);
-		h->h[i] |= ((charval(b[2*i+1], &err)& 0xf) << 0);
-		if(err){
-			werrstr("invalid hash");
+	for(i = 0; i < nelem(h->h); i++){
+		if((c0 = charval(b[2*i+0])) == -1)
 			return -1;
-		}
+		if((c1 = charval(b[2*i+1])) == -1)
+			return -1;
+		h->h[i] = (c0 << 4) | c1;
 	}
 	return 0;
 }
--