Skip to content

Commit

Permalink
stash: Don't fail if work dir contains file named 'HEAD'
Browse files Browse the repository at this point in the history
When performing a plain "git stash" (without --patch), git-diff would fail
with "fatal: ambiguous argument 'HEAD': both revision and filename". The
output was piped into git-update-index, masking the failed exit status.
The output is now sent to a temporary file (which is cleaned up by
existing code), and the exit status is checked. The "HEAD" arg to the
git-diff invocation has been disambiguated too, of course.

In patch mode, "git stash -p" would fail harmlessly, leaving the working
dir untouched. Interactive adding is fine, but the resulting tree was
diffed with an ambiguous 'HEAD' argument.

Use >foo (no space) when redirecting output.

In t3904, checks and operations on each file are in the order they'll
appear when interactively staging.

In t3905, fix a bug in "stash save --include-untracked -q is quiet": The
redirected stdout file was considered untracked, and so was removed from
the working directory. Use test path helper functions where appropriate.

Signed-off-by: Jonathon Mah <me@JonathonMah.com>
Acked-by: Thomas Rast <trast@student.ethz.ch>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
jmah authored and gitster committed Jan 1, 2012
1 parent 17b4e93 commit 44df2e2
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 31 deletions.
7 changes: 4 additions & 3 deletions git-stash.sh
Expand Up @@ -115,7 +115,8 @@ create_stash () {
git read-tree --index-output="$TMPindex" -m $i_tree &&
GIT_INDEX_FILE="$TMPindex" &&
export GIT_INDEX_FILE &&
git diff --name-only -z HEAD | git update-index -z --add --remove --stdin &&
git diff --name-only -z HEAD -- >"$TMP-stagenames" &&
git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
git write-tree &&
rm -f "$TMPindex"
) ) ||
Expand All @@ -134,7 +135,7 @@ create_stash () {
w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
die "$(gettext "Cannot save the current worktree state")"

git diff-tree -p HEAD $w_tree > "$TMP-patch" &&
git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
test -s "$TMP-patch" ||
die "$(gettext "No changes selected")"

Expand Down Expand Up @@ -491,7 +492,7 @@ drop_stash () {
die "$(eval_gettext "\${REV}: Could not drop stash entry")"

# clear_stash if we just dropped the last stash entry
git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
git rev-parse --verify "$ref_stash@{0}" >/dev/null 2>&1 || clear_stash
}

apply_to_branch () {
Expand Down
24 changes: 24 additions & 0 deletions t/t3903-stash.sh
Expand Up @@ -601,4 +601,28 @@ test_expect_success 'stash apply shows status same as git status (relative to cu
test_cmp expect actual
'

cat > expect << EOF
diff --git a/HEAD b/HEAD
new file mode 100644
index 0000000..fe0cbee
--- /dev/null
+++ b/HEAD
@@ -0,0 +1 @@
+file-not-a-ref
EOF

test_expect_success 'stash where working directory contains "HEAD" file' '
git stash clear &&
git reset --hard &&
echo file-not-a-ref > HEAD &&
git add HEAD &&
test_tick &&
git stash &&
git diff-files --quiet &&
git diff-index --cached --quiet HEAD &&
test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" &&
git diff stash^..stash > output &&
test_cmp output expect
'

test_done
47 changes: 29 additions & 18 deletions t/t3904-stash-patch.sh
Expand Up @@ -7,7 +7,8 @@ test_expect_success PERL 'setup' '
mkdir dir &&
echo parent > dir/foo &&
echo dummy > bar &&
git add bar dir/foo &&
echo committed > HEAD &&
git add bar dir/foo HEAD &&
git commit -m initial &&
test_tick &&
test_commit second dir/foo head &&
Expand All @@ -17,47 +18,57 @@ test_expect_success PERL 'setup' '
save_head
'

# note: bar sorts before dir, so the first 'n' is always to skip 'bar'
# note: order of files with unstaged changes: HEAD bar dir/foo

test_expect_success PERL 'saying "n" does nothing' '
set_state HEAD HEADfile_work HEADfile_index &&
set_state dir/foo work index &&
(echo n; echo n) | test_must_fail git stash save -p &&
verify_state dir/foo work index &&
verify_saved_state bar
(echo n; echo n; echo n) | test_must_fail git stash save -p &&
verify_state HEAD HEADfile_work HEADfile_index &&
verify_saved_state bar &&
verify_state dir/foo work index
'

test_expect_success PERL 'git stash -p' '
(echo n; echo y) | git stash save -p &&
verify_state dir/foo head index &&
(echo y; echo n; echo y) | git stash save -p &&
verify_state HEAD committed HEADfile_index &&
verify_saved_state bar &&
verify_state dir/foo head index &&
git reset --hard &&
git stash apply &&
verify_state dir/foo work head &&
verify_state bar dummy dummy
verify_state HEAD HEADfile_work committed &&
verify_state bar dummy dummy &&
verify_state dir/foo work head
'

test_expect_success PERL 'git stash -p --no-keep-index' '
set_state dir/foo work index &&
set_state HEAD HEADfile_work HEADfile_index &&
set_state bar bar_work bar_index &&
(echo n; echo y) | git stash save -p --no-keep-index &&
verify_state dir/foo head head &&
set_state dir/foo work index &&
(echo y; echo n; echo y) | git stash save -p --no-keep-index &&
verify_state HEAD committed committed &&
verify_state bar bar_work dummy &&
verify_state dir/foo head head &&
git reset --hard &&
git stash apply --index &&
verify_state dir/foo work index &&
verify_state bar dummy bar_index
verify_state HEAD HEADfile_work HEADfile_index &&
verify_state bar dummy bar_index &&
verify_state dir/foo work index
'

test_expect_success PERL 'git stash --no-keep-index -p' '
set_state dir/foo work index &&
set_state HEAD HEADfile_work HEADfile_index &&
set_state bar bar_work bar_index &&
(echo n; echo y) | git stash save --no-keep-index -p &&
set_state dir/foo work index &&
(echo y; echo n; echo y) | git stash save --no-keep-index -p &&
verify_state HEAD committed committed &&
verify_state dir/foo head head &&
verify_state bar bar_work dummy &&
git reset --hard &&
git stash apply --index &&
verify_state dir/foo work index &&
verify_state bar dummy bar_index
verify_state HEAD HEADfile_work HEADfile_index &&
verify_state bar dummy bar_index &&
verify_state dir/foo work index
'

test_expect_success PERL 'none of this moved HEAD' '
Expand Down
33 changes: 23 additions & 10 deletions t/t3905-stash-include-untracked.sh
Expand Up @@ -17,6 +17,7 @@ test_expect_success 'stash save --include-untracked some dirty working directory
echo 3 > file &&
test_tick &&
echo 1 > file2 &&
echo 1 > HEAD &&
mkdir untracked &&
echo untracked >untracked/untracked &&
git stash --include-untracked &&
Expand All @@ -35,6 +36,13 @@ test_expect_success 'stash save --include-untracked cleaned the untracked files'
'

cat > expect.diff <<EOF
diff --git a/HEAD b/HEAD
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/HEAD
@@ -0,0 +1 @@
+1
diff --git a/file2 b/file2
new file mode 100644
index 0000000..d00491f
Expand All @@ -51,14 +59,16 @@ index 0000000..5a72eb2
+untracked
EOF
cat > expect.lstree <<EOF
HEAD
file2
untracked
EOF

test_expect_success 'stash save --include-untracked stashed the untracked files' '
test "!" -f file2 &&
test ! -e untracked &&
git diff HEAD stash^3 -- file2 untracked >actual &&
test_path_is_missing file2 &&
test_path_is_missing untracked &&
test_path_is_missing HEAD &&
git diff HEAD stash^3 -- HEAD file2 untracked >actual &&
test_cmp expect.diff actual &&
git ls-tree --name-only stash^3: >actual &&
test_cmp expect.lstree actual
Expand All @@ -75,6 +85,7 @@ git clean --force --quiet

cat > expect <<EOF
M file
?? HEAD
?? actual
?? expect
?? file2
Expand Down Expand Up @@ -116,10 +127,12 @@ test_expect_success 'stash save --include-untracked dirty index got stashed' '

git reset > /dev/null

# Must direct output somewhere where it won't be considered an untracked file
test_expect_success 'stash save --include-untracked -q is quiet' '
echo 1 > file5 &&
git stash save --include-untracked --quiet > output.out 2>&1 &&
test ! -s output.out
git stash save --include-untracked --quiet > .git/stash-output.out 2>&1 &&
test_line_count = 0 .git/stash-output.out &&
rm -f .git/stash-output.out
'

test_expect_success 'stash save --include-untracked removed files' '
Expand All @@ -133,7 +146,7 @@ rm -f expect

test_expect_success 'stash save --include-untracked removed files got stashed' '
git stash pop &&
test ! -f file
test_path_is_missing file
'

cat > .gitignore <<EOF
Expand All @@ -155,14 +168,14 @@ test_expect_success 'stash save --include-untracked respects .gitignore' '
test_expect_success 'stash save -u can stash with only untracked files different' '
echo 4 > file4 &&
git stash -u &&
test "!" -f file4
test_path_is_missing file4
'

test_expect_success 'stash save --all does not respect .gitignore' '
git stash -a &&
test "!" -f ignored &&
test "!" -e ignored.d &&
test "!" -f .gitignore
test_path_is_missing ignored &&
test_path_is_missing ignored.d &&
test_path_is_missing .gitignore
'

test_expect_success 'stash save --all is stash poppable' '
Expand Down

0 comments on commit 44df2e2

Please sign in to comment.