summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Junio C Hamano <gitster@pobox.com>2013-07-11 13:05:52 -0700
committerLibravatar Junio C Hamano <gitster@pobox.com>2013-07-11 13:05:52 -0700
commitd26792ad69c0d2ea0b197656ae9a562e2c1f900e (patch)
tree06b8527f68bc9705bc6fe979f9c3f75ab2bb5e05
parentMerge branch 'jc/maint-diff-core-safecrlf' (diff)
parentgit stash: avoid data loss when "git stash save" kills a directory (diff)
downloadtgif-d26792ad69c0d2ea0b197656ae9a562e2c1f900e.tar.xz
Merge branch 'pb/stash-refuse-to-kill'
"git stash save" is not just about "saving" the local changes, but also is to restore the working tree state to that of HEAD. If you changed a non-directory into a directory in the local change, you may have untracked files in that directory, which have to be killed while doing so, unless you run it with --include-untracked. Teach the command to detect and error out before spreading the damage. This needed a small fix to "ls-files --killed". * pb/stash-refuse-to-kill: git stash: avoid data loss when "git stash save" kills a directory treat_directory(): do not declare submodules to be untracked
-rw-r--r--Documentation/git-stash.txt12
-rw-r--r--dir.c4
-rwxr-xr-xgit-stash.sh12
-rwxr-xr-xt/t3010-ls-files-killed-modified.sh23
-rwxr-xr-xt/t3903-stash.sh18
5 files changed, 61 insertions, 8 deletions
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index db7e803038..7c8b648fbe 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -14,7 +14,8 @@ SYNOPSIS
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
'git stash' branch <branchname> [<stash>]
'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
- [-u|--include-untracked] [-a|--all] [<message>]]
+ [-u|--include-untracked] [-a|--all] [-f|--force]
+ [<message>]]
'git stash' clear
'git stash' create [<message>]
'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
@@ -44,7 +45,7 @@ is also possible).
OPTIONS
-------
-save [-p|--patch] [--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
+save [-p|--patch] [--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-f|--force] [<message>]::
Save your local modifications to a new 'stash', and run `git reset
--hard` to revert them. The <message> part is optional and gives
@@ -71,6 +72,13 @@ linkgit:git-add[1] to learn how to operate the `--patch` mode.
+
The `--patch` option implies `--keep-index`. You can use
`--no-keep-index` to override this.
++
+In some cases, saving a stash could mean irretrievably removing some
+data - if a directory with untracked files replaces a tracked file of
+the same name, the new untracked files are not saved (except in case
+of `--include-untracked`) but the original tracked file shall be restored.
+By default, `stash save` will abort in such a case; `--force` will allow
+it to remove the untracked files.
list [<options>]::
diff --git a/dir.c b/dir.c
index 897c87403e..0480419544 100644
--- a/dir.c
+++ b/dir.c
@@ -1036,9 +1036,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
return path_recurse;
case index_gitdir:
- if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
- return path_none;
- return path_untracked;
+ return path_none;
case index_nonexistent:
if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
diff --git a/git-stash.sh b/git-stash.sh
index 1e541a2125..85c9e2c081 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -195,6 +195,7 @@ save_stash () {
keep_index=
patch_mode=
untracked=
+ force=
while test $# != 0
do
case "$1" in
@@ -215,6 +216,9 @@ save_stash () {
-u|--include-untracked)
untracked=untracked
;;
+ -f|--force)
+ force=t
+ ;;
-a|--all)
untracked=all
;;
@@ -258,6 +262,14 @@ save_stash () {
say "$(gettext "No local changes to save")"
exit 0
fi
+ if test -z "$untracked$force" &&
+ test -n "$(git ls-files --killed | head -n 1)"
+ then
+ say "$(gettext "The following untracked files would NOT be saved but need to be removed by stash save:")"
+ test -n "$GIT_QUIET" || git ls-files --killed | sed 's/^/\t/'
+ say "$(gettext "Aborting. Consider using either the --force or --include-untracked option.")" >&2
+ exit 1
+ fi
test -f "$GIT_DIR/logs/$ref_stash" ||
clear_stash || die "$(gettext "Cannot initialize stash")"
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index 262e617445..f611d799b6 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -11,6 +11,8 @@ This test prepares the following in the cache:
path1 - a symlink
path2/file2 - a file in a directory
path3/file3 - a file in a directory
+ submod1/ - a submodule
+ submod2/ - another submodule
and the following on the filesystem:
@@ -21,9 +23,11 @@ and the following on the filesystem:
path4 - a file
path5 - a symlink
path6/file6 - a file in a directory
+ submod1/ - a submodule (modified from the cache)
+ submod2/ - a submodule (matches the cache)
-git ls-files -k should report that existing filesystem
-objects except path4, path5 and path6/file6 to be killed.
+git ls-files -k should report that existing filesystem objects
+path0/*, path1/*, path2 and path3 to be killed.
Also for modification test, the cache and working tree have:
@@ -33,7 +37,7 @@ Also for modification test, the cache and working tree have:
path10 - a non-empty file, cache dirtied.
We should report path0, path1, path2/file2, path3/file3, path7 and path8
-modified without reporting path9 and path10.
+modified without reporting path9 and path10. submod1 is also modified.
'
. ./test-lib.sh
@@ -48,6 +52,18 @@ test_expect_success 'git update-index --add to add various paths.' '
: >path9 &&
date >path10 &&
git update-index --add -- path0 path?/file? path7 path8 path9 path10 &&
+ for i in 1 2
+ do
+ git init submod$i &&
+ (
+ cd submod$i && git commit --allow-empty -m "empty $i"
+ ) || break
+ done &&
+ git update-index --add submod[12]
+ (
+ cd submod1 &&
+ git commit --allow-empty -m "empty 1 (updated)"
+ ) &&
rm -fr path? # leave path10 alone
'
@@ -94,6 +110,7 @@ test_expect_success 'validate git ls-files -m output.' '
path3/file3
path7
path8
+ submod1
EOF
test_cmp .expected .output
'
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index debda7a678..5d22f17ca2 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -673,4 +673,22 @@ test_expect_success 'store updates stash ref and reflog' '
grep quux bazzy
'
+test_expect_success 'stash a change to turn a non-directory to a directory' '
+ git reset --hard &&
+ >testfile &&
+ git add testfile &&
+ git commit -m "add testfile as a regular file" &&
+ rm testfile &&
+ mkdir testfile &&
+ >testfile/file &&
+ test_must_fail git stash save "recover regular file" &&
+ test -f testfile/file
+'
+
+test_expect_success 'stash a change to turn a non-directory to a directory (forced)' '
+ git stash save --force "recover regular file (forced)" &&
+ ! test -f testfile/file &&
+ test -f testfile
+'
+
test_done