t2500: add various tests for nuking untracked files

Noting that unpack_trees treats reset=1 & update=1 as license to nuke untracked files, I looked for code paths that use this combination and tried to generate testcases which demonstrated unintentional loss of untracked files and directories. I found several. I also include testcases for `git reset --{hard,merge,keep}`. A hard reset is perhaps the most direct test of unpack_tree's reset=1 behavior, but we cannot make `git reset --hard` preserve untracked files without some migration work. Also, the two commands `checkout --force` (because of the --force) and `read-tree --reset` (because it's plumbing and we need to keep it backward compatible) were left out as we expect those to continue removing untracked files and directories. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Sep 24, 2021 at 06:37 UTC 446cc5544a3cb8dbd0ddac5bc5b160a687eb6471
1 file changed +244
t/t2500-untracked-overwriting.sh new
+244
@@ -0,0 +1,244 @@
1 +#!/bin/sh
2 +
3 +test_description='Test handling of overwriting untracked files'
4 +
5 +. ./test-lib.sh
6 +
7 +test_setup_reset () {
8 + git init reset_$1 &&
9 + (
10 + cd reset_$1 &&
11 + test_commit init &&
12 +
13 + git branch stable &&
14 + git branch work &&
15 +
16 + git checkout work &&
17 + test_commit foo &&
18 +
19 + git checkout stable
20 + )
21 +}
22 +
23 +test_expect_success 'reset --hard will nuke untracked files/dirs' '
24 + test_setup_reset hard &&
25 + (
26 + cd reset_hard &&
27 + git ls-tree -r stable &&
28 + git log --all --name-status --oneline &&
29 + git ls-tree -r work &&
30 +
31 + mkdir foo.t &&
32 + echo precious >foo.t/file &&
33 + echo foo >expect &&
34 +
35 + git reset --hard work &&
36 +
37 + # check that untracked directory foo.t/ was nuked
38 + test_path_is_file foo.t &&
39 + test_cmp expect foo.t
40 + )
41 +'
42 +
43 +test_expect_success 'reset --merge will preserve untracked files/dirs' '
44 + test_setup_reset merge &&
45 + (
46 + cd reset_merge &&
47 +
48 + mkdir foo.t &&
49 + echo precious >foo.t/file &&
50 + cp foo.t/file expect &&
51 +
52 + test_must_fail git reset --merge work 2>error &&
53 + test_cmp expect foo.t/file &&
54 + grep "Updating .foo.t. would lose untracked files" error
55 + )
56 +'
57 +
58 +test_expect_success 'reset --keep will preserve untracked files/dirs' '
59 + test_setup_reset keep &&
60 + (
61 + cd reset_keep &&
62 +
63 + mkdir foo.t &&
64 + echo precious >foo.t/file &&
65 + cp foo.t/file expect &&
66 +
67 + test_must_fail git reset --merge work 2>error &&
68 + test_cmp expect foo.t/file &&
69 + grep "Updating.*foo.t.*would lose untracked files" error
70 + )
71 +'
72 +
73 +test_setup_checkout_m () {
74 + git init checkout &&
75 + (
76 + cd checkout &&
77 + test_commit init &&
78 +
79 + test_write_lines file has some >filler &&
80 + git add filler &&
81 + git commit -m filler &&
82 +
83 + git branch stable &&
84 +
85 + git switch -c work &&
86 + echo stuff >notes.txt &&
87 + test_write_lines file has some words >filler &&
88 + git add notes.txt filler &&
89 + git commit -m filler &&
90 +
91 + git checkout stable
92 + )
93 +}
94 +
95 +test_expect_failure 'checkout -m does not nuke untracked file' '
96 + test_setup_checkout_m &&
97 + (
98 + cd checkout &&
99 +
100 + # Tweak filler
101 + test_write_lines this file has some >filler &&
102 + # Make an untracked file, save its contents in "expect"
103 + echo precious >notes.txt &&
104 + cp notes.txt expect &&
105 +
106 + test_must_fail git checkout -m work &&
107 + test_cmp expect notes.txt
108 + )
109 +'
110 +
111 +test_setup_sequencing () {
112 + git init sequencing_$1 &&
113 + (
114 + cd sequencing_$1 &&
115 + test_commit init &&
116 +
117 + test_write_lines this file has some words >filler &&
118 + git add filler &&
119 + git commit -m filler &&
120 +
121 + mkdir -p foo/bar &&
122 + test_commit foo/bar/baz &&
123 +
124 + git branch simple &&
125 + git branch fooey &&
126 +
127 + git checkout fooey &&
128 + git rm foo/bar/baz.t &&
129 + echo stuff >>filler &&
130 + git add -u &&
131 + git commit -m "changes" &&
132 +
133 + git checkout simple &&
134 + echo items >>filler &&
135 + echo newstuff >>newfile &&
136 + git add filler newfile &&
137 + git commit -m another
138 + )
139 +}
140 +
141 +test_expect_failure 'git rebase --abort and untracked files' '
142 + test_setup_sequencing rebase_abort_and_untracked &&
143 + (
144 + cd sequencing_rebase_abort_and_untracked &&
145 + git checkout fooey &&
146 + test_must_fail git rebase simple &&
147 +
148 + cat init.t &&
149 + git rm init.t &&
150 + echo precious >init.t &&
151 + cp init.t expect &&
152 + git status --porcelain &&
153 + test_must_fail git rebase --abort &&
154 + test_cmp expect init.t
155 + )
156 +'
157 +
158 +test_expect_failure 'git rebase fast forwarding and untracked files' '
159 + test_setup_sequencing rebase_fast_forward_and_untracked &&
160 + (
161 + cd sequencing_rebase_fast_forward_and_untracked &&
162 + git checkout init &&
163 + echo precious >filler &&
164 + cp filler expect &&
165 + test_must_fail git rebase init simple &&
166 + test_cmp expect filler
167 + )
168 +'
169 +
170 +test_expect_failure 'git rebase --autostash and untracked files' '
171 + test_setup_sequencing rebase_autostash_and_untracked &&
172 + (
173 + cd sequencing_rebase_autostash_and_untracked &&
174 + git checkout simple &&
175 + git rm filler &&
176 + mkdir filler &&
177 + echo precious >filler/file &&
178 + cp filler/file expect &&
179 + git rebase --autostash init &&
180 + test_path_is_file filler/file
181 + )
182 +'
183 +
184 +test_expect_failure 'git stash and untracked files' '
185 + test_setup_sequencing stash_and_untracked_files &&
186 + (
187 + cd sequencing_stash_and_untracked_files &&
188 + git checkout simple &&
189 + git rm filler &&
190 + mkdir filler &&
191 + echo precious >filler/file &&
192 + cp filler/file expect &&
193 + git status --porcelain &&
194 + git stash push &&
195 + git status --porcelain &&
196 + test_path_is_file filler/file
197 + )
198 +'
199 +
200 +test_expect_failure 'git am --abort and untracked dir vs. unmerged file' '
201 + test_setup_sequencing am_abort_and_untracked &&
202 + (
203 + cd sequencing_am_abort_and_untracked &&
204 + git format-patch -1 --stdout fooey >changes.mbox &&
205 + test_must_fail git am --3way changes.mbox &&
206 +
207 + # Delete the conflicted file; we will stage and commit it later
208 + rm filler &&
209 +
210 + # Put an unrelated untracked directory there
211 + mkdir filler &&
212 + echo foo >filler/file1 &&
213 + echo bar >filler/file2 &&
214 +
215 + test_must_fail git am --abort 2>errors &&
216 + test_path_is_dir filler &&
217 + grep "Updating .filler. would lose untracked files in it" errors
218 + )
219 +'
220 +
221 +test_expect_failure 'git am --skip and untracked dir vs deleted file' '
222 + test_setup_sequencing am_skip_and_untracked &&
223 + (
224 + cd sequencing_am_skip_and_untracked &&
225 + git checkout fooey &&
226 + git format-patch -1 --stdout simple >changes.mbox &&
227 + test_must_fail git am --3way changes.mbox &&
228 +
229 + # Delete newfile
230 + rm newfile &&
231 +
232 + # Put an unrelated untracked directory there
233 + mkdir newfile &&
234 + echo foo >newfile/file1 &&
235 + echo bar >newfile/file2 &&
236 +
237 + # Change our mind about resolutions, just skip this patch
238 + test_must_fail git am --skip 2>errors &&
239 + test_path_is_dir newfile &&
240 + grep "Updating .newfile. would lose untracked files in it" errors
241 + )
242 +'
243 +
244 +test_done