code: plan9front

Download patch

ref: d86a7ed412555192e2000a9a34b3372f380ec3d0
parent: 3839ad4d3c9b26b8a5d564d1d8f09490799e4cd7
author: Ori Bernstein <ori@eigenstate.org>
date: Tue Aug 16 19:50:28 EDT 2022

git/rebase: allow rebase without a clean tree

Here, patch grows a dry run option (-n), which allows
git/import to bail out early if it would patch files
that are dirty in tree.

Once git/import refuses to patch dirty files, git/rebase
can skip checking if the whole tree is clean, which is
both convenient and much faster on large trees.

--- a/sys/man/1/patch
+++ b/sys/man/1/patch
@@ -28,6 +28,9 @@
 Reverse direction of the patch. Additions become removals,
 and the new and old file names are swapped.
 .TP
+.B -n
+Print the files that would be modified by this patch, but do not apply it.
+.TP
 .BI -p \ nstrip
 Remove the prefix containing
 .I nstrip
--- a/sys/src/cmd/git/import
+++ b/sys/src/cmd/git/import
@@ -76,8 +76,16 @@
 
 	# force re-reading env
 	rc -c '
-		echo applying $msg | sed 1q
 		date=`{seconds $date}
+		files=`$nl{patch -np1 < $diffpath}
+		if(! git/walk -q $files){
+			>[1=2] {
+				echo patch would clobber files:
+				git/walk $files
+				exit clobber
+			}
+		}
+		echo applying $msg | sed 1q
 		if(! files=`$nl{patch -p1 < $diffpath})
 			die ''patch failed''
 		for(f in $files){
@@ -111,6 +119,6 @@
 	if(test -d $p && test -f $p/header && test -f $p/body)
 		{{cat $p/header; echo; cat $p/body} | apply} || die $status
 	if not
-		apply < $p || die $status
+		apply < $p
 }
 exit ''
--- a/sys/src/cmd/git/rebase
+++ b/sys/src/cmd/git/rebase
@@ -7,8 +7,6 @@
 eval `''{aux/getflags $*} || exec aux/usage
 
 tmp=_rebase.working
-if(! git/walk -q)
-	die dirty working tree
 if(~ $#abort 1){
 	if(! test -f .git/rebase.todo)
 		die no rebase to abort
--- a/sys/src/cmd/patch.c
+++ b/sys/src/cmd/patch.c
@@ -52,6 +52,7 @@
 void	(*addold)(Hunk*, char*);
 Fchg	*changed;
 int	nchanged;
+int	dryrun;
 
 char*
 readline(Biobuf *f, int *lnum)
@@ -392,21 +393,22 @@
 	char *tmp;
 	int fd;
 
-	if(strcmp(new, "/dev/null") == 0){
-		if(len != 0)
-			sysfatal("diff modifies removed file");
-		if(remove(old) == -1)
-			sysfatal("removeold %s: %r", old);
+	if(strcmp(new, "/dev/null") == 0 && len != 0){
+		sysfatal("diff modifies removed file");
 		return;
 	}
-	if(mkpath(new) == -1)
-		sysfatal("mkpath %s: %r", new);
-	if((tmp = smprint("%s.tmp%d", new, getpid())) == nil)
-		sysfatal("smprint: %r");
-	if((fd = create(tmp, OWRITE, 0666)) == -1)
-		sysfatal("open %s: %r", tmp);
-	if(write(fd, o, len) != len)
-		sysfatal("write %s: %r", tmp);
+	tmp = nil;
+	if(!dryrun){
+		if(mkpath(new) == -1)
+			sysfatal("mkpath %s: %r", new);
+		if((tmp = smprint("%s.tmp%d", new, getpid())) == nil)
+			sysfatal("smprint: %r");
+		if((fd = create(tmp, OWRITE, 0666)) == -1)
+			sysfatal("open %s: %r", tmp);
+		if(write(fd, o, len) != len)
+			sysfatal("write %s: %r", tmp);
+		close(fd);
+	}
 	if((changed = realloc(changed, (nchanged+1)*sizeof(Fchg))) == nil)
 		sysfatal("realloc: %r");
 	if((changed[nchanged].new = strdup(new)) == nil)
@@ -415,7 +417,6 @@
 		sysfatal("strdup: %r");
 	changed[nchanged].tmp = tmp;
 	nchanged++;
-	close(fd);
 }
 
 void
@@ -429,7 +430,14 @@
 		if(!ok){
 			if(remove(c->tmp) == -1)
 				fprint(2, "remove %s: %r\n", c->tmp);
-		}else{
+			goto Free;
+		}
+		if(!dryrun){
+			if(strcmp(c->new, "/dev/null") == 0){
+				if(remove(c->old) == -1)
+					sysfatal("remove %s: %r", c->old);
+				goto Print;
+			}
 			if((fd = open(c->tmp, ORDWR)) == -1)
 				sysfatal("open %s: %r", c->tmp);
 			if(strcmp(c->old, c->new) == 0 && remove(c->old) == -1)
@@ -438,11 +446,13 @@
 				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);
 		}
+Print:
+		if(strcmp(c->new, "/dev/null") == 0)
+			print("%s\n", c->old);
+		else
+			print("%s\n", c->new);
+Free:
 		free(c->tmp);
 		free(c->old);
 		free(c->new);
@@ -560,16 +570,18 @@
 	for(i = 0; i < p->nhunk; i++){
 		h = &p->hunk[i];
 		if(curfile == nil || strcmp(curfile, h->newpath) != 0){
-			if(slurp(&f, h->oldpath) == -1)
+			if(!dryrun && slurp(&f, h->oldpath) == -1)
 				sysfatal("slurp %s: %r", h->oldpath);
 			curfile = h->newpath;
 			e = f.buf;
 		}
-		s = e;
-		e = search(&f, h, fname);
-		o = append(o, &osz, s, e);
-		o = append(o, &osz, h->new, h->new + h->newlen);
-		e += h->oldlen;
+		if(!dryrun){
+			s = e;
+			e = search(&f, h, fname);
+			o = append(o, &osz, s, e);
+			o = append(o, &osz, h->new, h->new + h->newlen);
+			e += h->oldlen;
+		}
 		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);
@@ -601,7 +613,7 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-R] [-p nstrip] [patch...]\n", argv0);
+	fprint(2, "usage: %s [-nR] [-p nstrip] [patch...]\n", argv0);
 	exits("usage");
 }
 
@@ -615,6 +627,9 @@
 	ARGBEGIN{
 	case 'p':
 		strip = atoi(EARGF(usage()));
+		break;
+	case 'n':
+		dryrun++;
 		break;
 	case 'R':
 		reverse++;