shithub: front

Download patch

ref: 798352644e5cc146971a086058dfeec805a5453b
parent: 0e7690a4f8f056fde7074d9483795123a98fc6ea
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Sep 29 13:28:48 EDT 2024

git/save: and try again...

--- a/sys/src/cmd/git/save.c
+++ b/sys/src/cmd/git/save.c
@@ -191,26 +191,13 @@
 	return 0; 
 }
 
-int
-pathelt(char *buf, int nbuf, char *p, int *isdir)
-{
-	char *b;
-
-	b = buf;
-	if(*p == '/')
-		p++;
-	while(*p && *p != '/' && b != buf + nbuf)
-		*b++ = *p++;
-	*b = '\0';
-	*isdir = (*p == '/');
-	return b - buf;
-}
-
 Dirent*
 dirent(Dirent **ent, int *nent, char *name)
 {
 	Dirent *d;
 
+	assert(strchr(name, '/') == nil);
+
 	for(d = *ent; d != *ent + *nent; d++)
 		if(d->name && strcmp(d->name, name) == 0)
 			return d;
@@ -225,37 +212,64 @@
 int
 treeify(Object *t, char **path, char **epath, int off, Hash *h)
 {
-	int r, n, ne, nsub, nent, isdir;
-	char **p, **ep;
-	char elt[256];
-	Object **sub;
+	int nent, ne, slash;
+	char *s, **p, **ep;
 	Dirent *e, *ent;
+	Object *o;
 	Dir *d;
 
-	r = -1;
-	nsub = 0;
 	nent = t->tree->nent;
 	ent = eamalloc(nent, sizeof(*ent));
-	sub = eamalloc((epath - path), sizeof(Object*));
 	memcpy(ent, t->tree->ent, nent*sizeof(*ent));
 	for(p = path; p != epath; p = ep){
-		ne = pathelt(elt, sizeof(elt), *p + off, &isdir);
-		for(ep = p; ep != epath; ep++){
-			if(strncmp(elt, *ep + off, ne) != 0)
+		s = *p;
+
+		/*
+		 * paths have been normalized already,
+		 * no leading or double-slashes allowed.
+		 */
+		assert(s[off] != '\0' && s[off] != '/');
+
+		/* get next path element length (from off until '/' or nul) */
+		for(ne = 1; s[off+ne] != '\0' && s[off+ne] != '/'; ne++)
+			;
+
+		/* truncate at '/' or nul */
+		slash = s[off + ne];
+		s[off + ne] = '\0';
+
+		/* skip over children (having s as prefix) */
+		for(ep = p + 1; ep != epath; ep++){
+			if(strncmp(s, *ep, off + ne) != 0)
 				break;
 			if((*ep)[off+ne] != '\0' && (*ep)[off+ne] != '/')
 				break;
 		}
-		e = dirent(&ent, &nent, elt);
+
+		e = dirent(&ent, &nent, s + off);
+
+		d = dirstat(s);
+		if(d == nil){
+			/* delete */
+			e->name = nil;
+
+			s[off + ne] = slash;
+			continue;
+		}
+
 		if(e->islink)
-			sysfatal("symlinks may not be modified: %s", *path);
+			sysfatal("symlinks may not be modified: %s", s);
 		if(e->ismod)
-			sysfatal("submodules may not be modified: %s", *path);
-		if(isdir){
+			sysfatal("submodules may not be modified: %s", s);
+
+		s[off + ne] = slash;
+
+		if(slash && (d->mode & DMDIR) != 0){
+			free(d);
 			e->mode = DMDIR | 0755;
-			sub[nsub] = readobject(e->h);
-			if(sub[nsub] == nil || sub[nsub]->type != GTree)
-				sub[nsub] = emptydir();
+			o = readobject(e->h);
+			if(o == nil || o->type != GTree)
+				o = emptydir();
 			/*
 			 * if after processing deletions, a tree is empty,
 			 * mark it for removal from the parent.
@@ -264,29 +278,21 @@
 			 * but this is fine -- and ensures that an empty
 			 * repository will continue to work.
 			 */
-			n = treeify(sub[nsub], p, ep, off + ne + 1, &e->h);
-			if(n == 0)
+			if(treeify(o, p, ep, off + ne + 1, &e->h) == 0)
 				e->name = nil;
-			else if(n == -1)
-				goto err;
 		}else{
-			d = dirstat(*p);
-			if(d != nil && tracked(*p))
-				blobify(d, *p, &e->mode, &e->h);
+			if((d->mode & DMDIR) == 0 && tracked(s))
+				blobify(d, s, &e->mode, &e->h);
 			else
 				e->name = nil;
 			free(d);
 		}
 	}
-	if(nent == 0){
-		werrstr("%.*s: empty directory", off, *path);
-		goto err;
-	}
-
-	r = writetree(ent, nent, h);
-err:
-	free(sub);
-	return r;		
+	if(nent == 0)
+		sysfatal("%.*s: refusing to update empty directory", off, *path);
+	nent = writetree(ent, nent, h);
+	free(ent);
+	return nent;		
 }
 
 
--