Commit f1e81a0517df837593350dd932e813563bdc01b8

Stefan Sperling 2019-08-10T16:57:20

fix bug where 'revert -p' would delete all lines following a reverted change

diff --git a/lib/worktree.c b/lib/worktree.c
index d648267..96f3f30 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -2893,7 +2893,7 @@ copy_change(FILE *f1, FILE *f2, int *line_cur1, int *line_cur2,
 		(*line_cur2)++;
 	}
 	/* Skip over old file's replaced lines. */
-	while (!feof(f1) && *line_cur1 <= end_new) {
+	while (!feof(f1) && *line_cur1 <= end_old) {
 		if (rejectfile)
 			err = copy_one_line(f1, NULL, rejectfile);
 		else
@@ -2902,12 +2902,33 @@ copy_change(FILE *f1, FILE *f2, int *line_cur1, int *line_cur2,
 			return err;
 		(*line_cur1)++;
 	}
-	/* Copy old file's lines after patch. */
-	while (!feof(f1) && *line_cur1 <= end_old) {
-		err = copy_one_line(f1, outfile, rejectfile);
-		if (err)
-			return err;
-		(*line_cur1)++;
+
+	return NULL;
+}
+
+static const struct got_error *
+copy_remaining_content(FILE *f1, FILE *f2, int *line_cur1, int *line_cur2,
+    FILE *outfile, FILE *rejectfile)
+{
+	const struct got_error *err;
+
+	if (outfile) {
+		/* Copy old file's lines until EOF. */
+		while (!feof(f1)) {
+			err = copy_one_line(f1, outfile, NULL);
+			if (err)
+				return err;
+			(*line_cur1)++;
+		}
+	}
+	if (rejectfile) {
+		/* Copy new file's lines until EOF. */
+		while (!feof(f2)) {
+			err = copy_one_line(f2, NULL, rejectfile);
+			if (err)
+				return err;
+			(*line_cur2)++;
+		}
 	}
 
 	return NULL;
@@ -2968,24 +2989,6 @@ apply_or_reject_change(int *choice, struct got_diff_change *change, int n,
 		    end_old, start_new, end_new, rejectfile, outfile);
 		break;
 	case GOT_PATCH_CHOICE_QUIT:
-		if (outfile) {
-			/* Copy old file's lines until EOF. */
-			while (!feof(f1)) {
-				err = copy_one_line(f1, outfile, NULL);
-				if (err)
-					goto done;
-				(*line_cur1)++;
-			}
-		}
-		if (rejectfile) {
-			/* Copy new file's lines until EOF. */
-			while (!feof(f2)) {
-				err = copy_one_line(f2, NULL, rejectfile);
-				if (err)
-					goto done;
-				(*line_cur2)++;
-			}
-		}
 		break;
 	default:
 		err = got_error(GOT_ERR_PATCH_CHOICE);
@@ -3086,6 +3089,10 @@ create_patched_content(char **path_outfile, int reverse_patch,
 		else if (choice == GOT_PATCH_CHOICE_QUIT)
 			break;
 	}
+	if (have_content)
+		err = copy_remaining_content(f1, f2, &line_cur1, &line_cur2,
+		    reverse_patch ? NULL : outfile,
+		    reverse_patch ? outfile : NULL);
 done:
 	free(id_str);
 	if (blob)
@@ -5882,6 +5889,9 @@ create_unstaged_content(char **path_unstaged_content,
 		if (choice == GOT_PATCH_CHOICE_QUIT)
 			break;
 	}
+	if (have_content || have_rejected_content)
+		err = copy_remaining_content(f1, f2, &line_cur1, &line_cur2,
+		    outfile, rejectfile);
 done:
 	free(label1);
 	if (blob)
diff --git a/regress/cmdline/revert.sh b/regress/cmdline/revert.sh
index e2eeccf..d66beb7 100755
--- a/regress/cmdline/revert.sh
+++ b/regress/cmdline/revert.sh
@@ -794,6 +794,74 @@ function test_revert_patch_removed {
 	test_done "$testroot" "$ret"
 }
 
+function test_revert_patch_one_change {
+	local testroot=`test_init revert_patch_one_change`
+
+	jot 16 > $testroot/repo/numbers
+	(cd $testroot/repo && git add numbers)
+	git_commit $testroot/repo -m "added numbers file"
+	local commit_id=`git_show_head $testroot/repo`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	sed -i -e 's/^2$/a/' $testroot/wt/numbers
+
+	# revert change with -p
+	printf "y\n" > $testroot/patchscript
+	(cd $testroot/wt && got revert -F $testroot/patchscript -p \
+		numbers > $testroot/stdout)
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		echo "got revert command failed unexpectedly" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+	cat > $testroot/stdout.expected <<EOF
+-----------------------------------------------
+@@ -1,5 +1,5 @@
+ 1
+-2
++a
+ 3
+ 4
+ 5
+-----------------------------------------------
+M  numbers (change 1 of 1)
+revert this change? [y/n/q] y
+EOF
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	(cd $testroot/wt && got status > $testroot/stdout)
+	echo -n > $testroot/stdout.expected
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	(cd $testroot/wt && got diff > $testroot/stdout)
+	echo -n > $testroot/stdout.expected
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_revert_basic
 run_test test_revert_rm
 run_test test_revert_add
@@ -804,3 +872,4 @@ run_test test_revert_directory
 run_test test_revert_patch
 run_test test_revert_patch_added
 run_test test_revert_patch_removed
+run_test test_revert_patch_one_change