Commit Diff


commit - fa3cef63799016195e8a917f39c82815522692aa
commit + 369fd7e5fa99b95f7d7aa812b5260584b86a3778
blob - df57239f50ac1caa3457f59d6df054cab52a05bc
blob + 2a2d317c015385526ce0f22c09ed3240dafad6a5
--- lib/worktree.c
+++ lib/worktree.c
@@ -4375,15 +4375,28 @@ revert_file(void *arg, unsigned char status, unsigned 
 
 		if (a->patch_cb && (status == GOT_STATUS_MODIFY ||
 		    status == GOT_STATUS_CONFLICT)) {
+			int is_bad_symlink = 0;
 			err = create_patched_content(&path_content, 1, &id,
 			    ondisk_path, dirfd, de_name, ie->path, a->repo,
 			    a->patch_cb, a->patch_arg);
 			if (err || path_content == NULL)
 				break;
-			if (rename(path_content, ondisk_path) == -1) {
-				err = got_error_from_errno3("rename",
-				    path_content, ondisk_path);
-				goto done;
+			if (te && S_ISLNK(te->mode)) {
+				if (unlink(path_content) == -1) {
+					err = got_error_from_errno2("unlink",
+					    path_content);
+					break;
+				}
+				err = install_symlink(&is_bad_symlink,
+				    a->worktree, ondisk_path, ie->path,
+				    blob, 0, 1, 0, a->repo,
+				    a->progress_cb, a->progress_arg);
+			} else {
+				if (rename(path_content, ondisk_path) == -1) {
+					err = got_error_from_errno3("rename",
+					    path_content, ondisk_path);
+					goto done;
+				}
 			}
 		} else {
 			int is_bad_symlink = 0;
blob - 72a6ec979f66c13f3dd2fe3b41d969024ebf7283
blob + 84419db8223bceeb8d7e6ec0d1d43f75ebff9b5a
--- regress/cmdline/revert.sh
+++ regress/cmdline/revert.sh
@@ -1223,6 +1223,260 @@ EOF
 	test_done "$testroot" "$ret"
 }
 
+function test_revert_patch_symlink {
+	local testroot=`test_init revert_patch_symlink`
+
+	(cd $testroot/repo && ln -s alpha alpha.link)
+	(cd $testroot/repo && ln -s epsilon epsilon.link)
+	(cd $testroot/repo && ln -s /etc/passwd passwd.link)
+	(cd $testroot/repo && ln -s ../beta epsilon/beta.link)
+	(cd $testroot/repo && ln -s nonexistent nonexistent.link)
+	(cd $testroot/repo && ln -sf epsilon/zeta zeta.link)
+	(cd $testroot/repo && ln -sf epsilon/zeta zeta2.link)
+	(cd $testroot/repo && git add .)
+	git_commit $testroot/repo -m "add symlinks"
+	local commit_id1=`git_show_head $testroot/repo`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+
+	# symlink to file A now points to file B
+	(cd $testroot/wt && ln -sf gamma/delta alpha.link)
+	# symlink to a directory A now points to file B
+	(cd $testroot/wt && ln -sfh beta epsilon.link)
+	# "bad" symlink now contains a different target path
+	echo "foo" > $testroot/wt/passwd.link
+	# relative symlink to directory A now points to relative directory B
+	(cd $testroot/wt && ln -sfh ../gamma epsilon/beta.link)
+	# an unversioned symlink
+	(cd $testroot/wt && ln -sf .got/foo dotgotfoo.link)
+	# symlink to file A now points to non-existent file B
+	(cd $testroot/wt && ln -sf nonexistent2 nonexistent.link)
+	# removed symlink
+	(cd $testroot/wt && got rm zeta.link > /dev/null)
+	(cd $testroot/wt && got rm zeta2.link > /dev/null)
+	# added symlink
+	(cd $testroot/wt && ln -sf beta new.link)
+	(cd $testroot/wt && got add new.link > /dev/null)
+	(cd $testroot/wt && ln -sf beta zeta3.link)
+	(cd $testroot/wt && got add zeta3.link > /dev/null)
+
+	printf "y\nn\ny\nn\ny\ny\nn\ny\ny\n" > $testroot/patchscript
+	(cd $testroot/wt && got revert -F $testroot/patchscript -p -R . \
+		> $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 +1 @@
+-alpha
+\ No newline at end of file
++gamma/delta
+\ No newline at end of file
+-----------------------------------------------
+M  alpha.link (change 1 of 1)
+revert this change? [y/n/q] y
+R  alpha.link
+-----------------------------------------------
+@@ -1 +1 @@
+-../beta
+\ No newline at end of file
++../gamma
+\ No newline at end of file
+-----------------------------------------------
+M  epsilon/beta.link (change 1 of 1)
+revert this change? [y/n/q] n
+-----------------------------------------------
+@@ -1 +1 @@
+-epsilon
+\ No newline at end of file
++beta
+\ No newline at end of file
+-----------------------------------------------
+M  epsilon.link (change 1 of 1)
+revert this change? [y/n/q] y
+R  epsilon.link
+A  new.link
+revert this addition? [y/n] n
+-----------------------------------------------
+@@ -1 +1 @@
+-nonexistent
+\ No newline at end of file
++nonexistent2
+\ No newline at end of file
+-----------------------------------------------
+M  nonexistent.link (change 1 of 1)
+revert this change? [y/n/q] y
+R  nonexistent.link
+-----------------------------------------------
+@@ -1 +1 @@
+-/etc/passwd
+\ No newline at end of file
++foo
+-----------------------------------------------
+M  passwd.link (change 1 of 1)
+revert this change? [y/n/q] y
+R  passwd.link
+D  zeta.link
+revert this deletion? [y/n] n
+D  zeta2.link
+revert this deletion? [y/n] y
+R  zeta2.link
+A  zeta3.link
+revert this addition? [y/n] y
+R  zeta3.link
+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
+
+	if ! [ -h $testroot/wt/alpha.link ]; then
+		echo "alpha.link is not a symlink"
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	readlink $testroot/wt/alpha.link > $testroot/stdout
+	echo "alpha" > $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
+
+	if ! [ -h $testroot/wt/epsilon.link ]; then
+		echo "epsilon.link is not a symlink"
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	readlink $testroot/wt/epsilon.link > $testroot/stdout
+	echo "epsilon" > $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
+
+	if [ -h $testroot/wt/passwd.link ]; then
+		echo "passwd.link should not be a symlink" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	echo -n "/etc/passwd" > $testroot/content.expected
+	cp $testroot/wt/passwd.link $testroot/content
+
+	cmp -s $testroot/content.expected $testroot/content
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/content.expected $testroot/content
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	readlink $testroot/wt/epsilon/beta.link > $testroot/stdout
+	echo "../gamma" > $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
+
+	readlink $testroot/wt/nonexistent.link > $testroot/stdout
+	echo "nonexistent" > $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"
+	fi
+
+	if [ ! -h $testroot/wt/dotgotfoo.link ]; then
+		echo "dotgotfoo.link is not a symlink " >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+	readlink $testroot/wt/dotgotfoo.link > $testroot/stdout
+	echo ".got/foo" > $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"
+	fi
+
+
+	if [ -e $testroot/wt/zeta.link ]; then
+		echo -n "zeta.link should not exist on disk" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	if [ ! -h $testroot/wt/zeta2.link ]; then
+		echo -n "zeta2.link is not a symlink" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	readlink $testroot/wt/zeta2.link > $testroot/stdout
+	echo "epsilon/zeta" > $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"
+	fi
+
+	if [ ! -h $testroot/wt/zeta3.link ]; then
+		echo -n "zeta3.link is not a symlink" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	readlink $testroot/wt/zeta2.link > $testroot/stdout
+	echo "epsilon/zeta" > $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"
+	fi
+
+	if [ ! -h $testroot/wt/new.link ]; then
+		echo -n "new.link is not a symlink" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	(cd $testroot/wt && got status > $testroot/stdout)
+	echo "?  dotgotfoo.link" > $testroot/stdout.expected
+	echo "M  epsilon/beta.link" >> $testroot/stdout.expected
+	echo "A  new.link" >> $testroot/stdout.expected
+	echo "D  zeta.link" >> $testroot/stdout.expected
+	echo "?  zeta3.link" >> $testroot/stdout.expected
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		return 1
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_revert_basic
 run_test test_revert_rm
 run_test test_revert_add
@@ -1238,3 +1492,4 @@ run_test test_revert_patch_one_change
 run_test test_revert_added_subtree
 run_test test_revert_deleted_subtree
 run_test test_revert_symlink
+run_test test_revert_patch_symlink