. ./test-lib.sh
+# set_state <path> <worktree-content> <index-content>
+#
+# Prepare the content for path in worktree and the index as specified.
set_state () {
echo "$3" > "$1" &&
git add "$1" &&
echo "$2" > "$1"
}
+# save_state <path>
+#
+# Save index/worktree content of <path> in the files _worktree_<path>
+# and _index_<path>
save_state () {
noslash="$(echo "$1" | tr / _)" &&
cat "$1" > _worktree_"$noslash" &&
git show :"$1" > _index_"$noslash"
}
+# set_and_save_state <path> <worktree-content> <index-content>
set_and_save_state () {
set_state "$@" &&
save_state "$1"
}
+# verify_state <path> <expected-worktree-content> <expected-index-content>
verify_state () {
test "$(cat "$1")" = "$2" &&
test "$(git show :"$1")" = "$3"
}
+# verify_saved_state <path>
+#
+# Call verify_state with expected contents from the last save_state
verify_saved_state () {
noslash="$(echo "$1" | tr / _)" &&
verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
--- /dev/null
+#!/bin/sh
+
+test_description='restore basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit first &&
+ echo first-and-a-half >>first.t &&
+ git add first.t &&
+ test_commit second &&
+ echo one >one &&
+ echo two >two &&
+ echo untracked >untracked &&
+ echo ignored >ignored &&
+ echo /ignored >.gitignore &&
+ git add one two .gitignore &&
+ git update-ref refs/heads/one master
+'
+
+test_expect_success 'restore without pathspec is not ok' '
+ test_must_fail git restore &&
+ test_must_fail git restore --source=first
+'
+
+test_expect_success 'restore a file, ignoring branch of same name' '
+ cat one >expected &&
+ echo dirty >>one &&
+ git restore one &&
+ test_cmp expected one
+'
+
+test_expect_success 'restore a file on worktree from another ref' '
+ test_when_finished git reset --hard &&
+ git cat-file blob first:./first.t >expected &&
+ git restore --source=first first.t &&
+ test_cmp expected first.t &&
+ git cat-file blob HEAD:./first.t >expected &&
+ git show :first.t >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'restore a file in the index from another ref' '
+ test_when_finished git reset --hard &&
+ git cat-file blob first:./first.t >expected &&
+ git restore --source=first --staged first.t &&
+ git show :first.t >actual &&
+ test_cmp expected actual &&
+ git cat-file blob HEAD:./first.t >expected &&
+ test_cmp expected first.t
+'
+
+test_expect_success 'restore a file in both the index and worktree from another ref' '
+ test_when_finished git reset --hard &&
+ git cat-file blob first:./first.t >expected &&
+ git restore --source=first --staged --worktree first.t &&
+ git show :first.t >actual &&
+ test_cmp expected actual &&
+ test_cmp expected first.t
+'
+
+test_expect_success 'restore --staged uses HEAD as source' '
+ test_when_finished git reset --hard &&
+ git cat-file blob :./first.t >expected &&
+ echo index-dirty >>first.t &&
+ git add first.t &&
+ git restore --staged first.t &&
+ git cat-file blob :./first.t >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '
+ git init unmerged &&
+ (
+ cd unmerged &&
+ echo one >unmerged &&
+ echo one >common &&
+ git add unmerged common &&
+ git commit -m common &&
+ git switch -c first &&
+ echo first >unmerged &&
+ git commit -am first &&
+ git switch -c second master &&
+ echo second >unmerged &&
+ git commit -am second &&
+ test_must_fail git merge first &&
+
+ echo dirty >>common &&
+ test_must_fail git restore . &&
+
+ git restore --ignore-unmerged --quiet . >output 2>&1 &&
+ git diff common >diff-output &&
+ : >empty &&
+ test_cmp empty output &&
+ test_cmp empty diff-output
+ )
+'
+
+test_done
--- /dev/null
+#!/bin/sh
+
+test_description='git restore --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success PERL 'setup' '
+ mkdir dir &&
+ echo parent >dir/foo &&
+ echo dummy >bar &&
+ git add bar dir/foo &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+test_expect_success PERL 'restore -p without pathspec is fine' '
+ echo q >cmd &&
+ git restore -p <cmd
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success PERL 'saying "n" does nothing' '
+ set_and_save_state dir/foo work head &&
+ test_write_lines n n | git restore -p &&
+ verify_saved_state bar &&
+ verify_saved_state dir/foo
+'
+
+test_expect_success PERL 'git restore -p' '
+ set_and_save_state dir/foo work head &&
+ test_write_lines n y | git restore -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success PERL 'git restore -p with staged changes' '
+ set_state dir/foo work index &&
+ test_write_lines n y | git restore -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo index index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD' '
+ set_state dir/foo work index &&
+ # the third n is to get out in case it mistakenly does not apply
+ test_write_lines n y n | git restore -p --source=HEAD &&
+ verify_saved_state bar &&
+ verify_state dir/foo head index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD^' '
+ set_state dir/foo work index &&
+ # the third n is to get out in case it mistakenly does not apply
+ test_write_lines n y n | git restore -p --source=HEAD^ &&
+ verify_saved_state bar &&
+ verify_state dir/foo parent index
+'
+
+test_expect_success PERL 'git restore -p handles deletion' '
+ set_state dir/foo work index &&
+ rm dir/foo &&
+ test_write_lines n y | git restore -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo index index
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success PERL 'path limiting works: dir' '
+ set_state dir/foo work head &&
+ test_write_lines y n | git restore -p dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: -- dir' '
+ set_state dir/foo work head &&
+ test_write_lines y n | git restore -p -- dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
+ set_state dir/foo work head &&
+ # the third n is to get out in case it mistakenly does not apply
+ test_write_lines y n n | git restore -p --source=HEAD^ -- dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo parent head
+'
+
+test_expect_success PERL 'path limiting works: foo inside dir' '
+ set_state dir/foo work head &&
+ # the third n is to get out in case it mistakenly does not apply
+ test_write_lines y n n | (cd dir && git restore -p foo) &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success PERL 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+test_done