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++)