shithub: git9

Download patch

ref: 48b5ef5d7e0f59e0a9e8c4d00676cfb66725407d
parent: cca3d6b95aa2e8ebf0feda59468eb61020bb38a8
author: Ori Bernstein <ori@eigenstate.org>
date: Fri Feb 12 11:00:12 EST 2021

git/query: allow tree diffing against empty commits

This allows git/export of the first commit in a
repository's history

--- a/query.c
+++ b/query.c
@@ -57,67 +57,91 @@
 }
 
 void
-difftrees(Hash ah, Hash bh)
+difftrees(Object *a, Object *b)
 {
 	Dirent *ap, *bp, *ae, *be;
-	Object *a, *b;
 	int c;
 
-
-	if((a = readobject(ah)) == nil)
-		sysfatal("bad hash %H", ah);
-	if((b = readobject(bh)) == nil)
-		sysfatal("bad hash %H", bh);
-	if(a->type != b->type)
-		return;
-	switch(a->type){
-	case GCommit:
-		difftrees(a->commit->tree, b->commit->tree);
-		break;
-	case GTree:
+	ap = ae = nil;
+	bp = be = nil;
+	if(a != nil){
+		if(a->type != GTree)
+			return;
 		ap = a->tree->ent;
 		ae = ap + a->tree->nent;
+	}
+	if(b != nil){
+		if(b->type != GTree)
+			return;
 		bp = b->tree->ent;
 		be = bp + b->tree->nent;
-		while(ap != ae && bp != be){
-			c = strcmp(ap->name, bp->name);
-			if(c == 0){
-				if(ap->mode == bp->mode && hasheq(&ap->h, &bp->h))
-					goto next;
+	}
+	while(ap != ae && bp != be){
+		c = strcmp(ap->name, bp->name);
+		if(c == 0){
+			if(ap->mode == bp->mode && hasheq(&ap->h, &bp->h))
+				goto next;
 
-				if(ap->mode != bp->mode)
-					print("! %P%s\n", ap->name);
-				else if(!(ap->mode & DMDIR) || !(bp->mode & DMDIR))
-					print("@ %P%s\n", ap->name);
-				if((ap->mode & DMDIR) && (bp->mode & DMDIR)){
-					if(npath >= nelem(path))
-						sysfatal("path too deep");
-					path[npath++] = ap->name;
-					difftrees(ap->h, bp->h);
-					npath--;
-				}
-next:
-				ap++;
-				bp++;
-			}else if(c < 0) {
-				show(ap, '-');
-				ap++;
-			}else if(c > 0){
-				show(bp, '+');
-				bp++;
+			if(ap->mode != bp->mode)
+				print("! %P%s\n", ap->name);
+			else if(!(ap->mode & DMDIR) || !(bp->mode & DMDIR))
+				print("@ %P%s\n", ap->name);
+			if((ap->mode & DMDIR) && (bp->mode & DMDIR)){
+				if(npath >= nelem(path))
+					sysfatal("path too deep");
+				path[npath++] = ap->name;
+				if((a = readobject(ap->h)) == nil)
+					sysfatal("bad hash %H", ap->h);
+				if((b = readobject(bp->h)) == nil)
+					sysfatal("bad hash %H", bp->h);
+				difftrees(a, b);
+				unref(a);
+				unref(b);
+				npath--;
 			}
-		}
-		for(; ap != ae; ap++)
+next:
+			ap++;
+			bp++;
+		}else if(c < 0) {
 			show(ap, '-');
-		for(; bp != be; bp++)
+			ap++;
+		}else if(c > 0){
 			show(bp, '+');
-		break;
+			bp++;
+		}
 	}
-	unref(a);
-	unref(b);
+	for(; ap != ae; ap++)
+		show(ap, '-');
+	for(; bp != be; bp++)
+		show(bp, '+');
 }
 
+void
+diffcommits(Hash ah, Hash bh)
+{
+	Object *a, *b, *at, *bt;
 
+	at = nil;
+	bt = nil;
+	if(!hasheq(&ah, &Zhash) && (a = readobject(ah)) != nil){
+		if(a->type != GCommit)
+			sysfatal("not commit: %H", ah);
+		if((at = readobject(a->commit->tree)) == nil)
+			sysfatal("bad hash %H", a->commit->tree);
+		unref(a);
+	}
+	if(!hasheq(&bh, &Zhash) && (b = readobject(bh)) != nil){
+		if(b->type != GCommit)
+			sysfatal("not commit: %H", ah);
+		if((bt = readobject(b->commit->tree)) == nil)
+			sysfatal("bad hash %H", b->commit->tree);
+		unref(b);
+	}
+	difftrees(at, bt);
+	unref(at);
+	unref(bt);
+}
+
 void
 usage(void)
 {
@@ -161,7 +185,7 @@
 	if(changes){
 		if(n != 2)
 			sysfatal("diff: need 2 commits, got %d", n);
-		difftrees(h[0], h[1]);
+		diffcommits(h[0], h[1]);
 	}else{
 		p = (fullpath ? "/mnt/git/object/" : "");
 		for(j = 0; j < n; j++)