code: plan9front

Download patch

ref: 30794bba756772ada85b7642beddb42ffc416203
parent: 8f109ae113215166746b150a5a09b96050e62e52
author: Ori Bernstein <ori@eigenstate.org>
date: Tue Aug 2 23:13:00 EDT 2022

patch: apply patches atomically

When applying a patch, we used to write out the patch,
rename the file, and then move to the next one. This
meant that if a patch failed to apply cleanly, we would
leave the patch half-applied.

This sucked, so we now apply the patch as a whole unit,
or not at all.

--- a/sys/src/cmd/patch.c
+++ b/sys/src/cmd/patch.c
@@ -6,6 +6,7 @@
 typedef struct Patch Patch;
 typedef struct Hunk Hunk;
 typedef struct Fbuf Fbuf;
+typedef struct Fchg Fchg;
 
 struct Patch {
 	char	*name;
@@ -39,10 +40,18 @@
 	int	len;
 };
 
+struct Fchg {
+	char	*tmp;
+	char	*old;
+	char	*new;
+};
+
 int	strip;
 int	reverse;
 void	(*addnew)(Hunk*, char*);
 void	(*addold)(Hunk*, char*);
+Fchg	*changed;
+int	nchanged;
 
 char*
 readline(Biobuf *f, int *lnum)
@@ -398,15 +407,49 @@
 		sysfatal("open %s: %r", tmp);
 	if(write(fd, o, len) != len)
 		sysfatal("write %s: %r", tmp);
-	if(strcmp(old, new) == 0 && remove(old) == -1)
-		sysfatal("remove %s: %r", old);
-	if(rename(fd, new) == -1)
-		sysfatal("create %s: %r", new);
-	if(close(fd) == -1)
-		sysfatal("close %s: %r", tmp);
-	free(tmp);
+	if((changed = realloc(changed, (nchanged+1)*sizeof(Fchg))) == nil)
+		sysfatal("realloc: %r");
+	if((changed[nchanged].new = strdup(new)) == nil)
+		sysfatal("strdup: %r");
+	if((changed[nchanged].old = strdup(old)) == nil)
+		sysfatal("strdup: %r");
+	changed[nchanged].tmp = tmp;
+	nchanged++;
+	close(fd);
 }
 
+void
+finish(int ok)
+{
+	Fchg *c;
+	int i, fd;
+
+	for(i = 0; i < nchanged; i++){
+		c = &changed[i];
+		if(!ok){
+			if(remove(c->tmp) == -1)
+				fprint(2, "remove %s: %r\n", c->tmp);
+		}else{
+			if((fd = open(c->tmp, ORDWR)) == -1)
+				sysfatal("open %s: %r", c->tmp);
+			if(strcmp(c->old, c->new) == 0 && remove(c->old) == -1)
+				sysfatal("remove %s: %r", c->old);
+			if(rename(fd, c->new) == -1)
+				sysfatal("create %s: %r", c->new);
+			if(close(fd) == -1)
+				sysfatal("close %s: %r", c->tmp);
+			if(strcmp(c->new, "/dev/null") == 0)
+				print("%s\n", c->old);
+			else
+				print("%s\n", c->new);
+		}
+		free(c->tmp);
+		free(c->old);
+		free(c->new);
+	}
+	free(changed);
+}
+
 int
 slurp(Fbuf *f, char *path)
 {
@@ -530,10 +573,6 @@
 		if(i+1 == p->nhunk || strcmp(curfile, p->hunk[i+1].newpath) != 0){
 			o = append(o, &osz, e, f.buf + f.len);
 			blat(h->oldpath, h->newpath, o, osz);
-			if(strcmp(h->newpath, "/dev/null") == 0)
-				print("%s\n", h->oldpath);
-			else
-				print("%s\n", h->newpath);
 			osz = 0;
 		}
 	}
@@ -571,7 +610,7 @@
 {
 	Biobuf *f;
 	Patch *p;
-	int i;
+	int i, ok;
 
 	ARGBEGIN{
 	case 'p':
@@ -592,13 +631,16 @@
 		addnew = addnewfn;
 		addold = addoldfn;
 	}
+	ok = 1;
 	if(argc == 0){
 		if((f = Bfdopen(0, OREAD)) == nil)
 			sysfatal("open stdin: %r");
 		if((p = parse(f, "stdin")) == nil)
 			sysfatal("parse patch: %r");
-		if(apply(p, "stdin") == -1)
-			sysfatal("apply stdin: %r");
+		if(apply(p, "stdin") == -1){
+			fprint(2, "apply stdin: %r\n");
+			ok = 0;
+		}
 		freepatch(p);
 		Bterm(f);
 	}else{
@@ -607,11 +649,14 @@
 				sysfatal("open %s: %r", argv[i]);
 			if((p = parse(f, argv[i])) == nil)
 				sysfatal("parse patch: %r");
-			if(apply(p, argv[i]) == -1)
-				sysfatal("apply %s: %r", argv[i]);
+			if(apply(p, argv[i]) == -1){
+				fprint(2, "apply %s: %r\n", argv[i]);
+				ok = 0;
+			}
 			freepatch(p);
 			Bterm(f);
 		}
 	}
+	finish(ok);
 	exits(nil);
 }