1
+#!/bin/sh
2
+
3
+test_description='Test handling of the current working directory becoming empty'
4
+
5
+. ./test-lib.sh
6
+
7
+test_expect_success setup '
8
+ test_commit init &&
9
+
10
+ git branch fd_conflict &&
11
+
12
+ mkdir -p foo/bar &&
13
+ test_commit foo/bar/baz &&
14
+
15
+ git revert HEAD &&
16
+ git tag reverted &&
17
+
18
+ git checkout fd_conflict &&
19
+ mkdir dirORfile &&
20
+ test_commit dirORfile/foo &&
21
+
22
+ git rm -r dirORfile &&
23
+ echo not-a-directory >dirORfile &&
24
+ git add dirORfile &&
25
+ git commit -m dirORfile &&
26
+
27
+ git switch -c df_conflict HEAD~1 &&
28
+ test_commit random_file &&
29
+
30
+ git switch -c undo_fd_conflict fd_conflict &&
31
+ git revert HEAD
32
+'
33
+
34
+test_incidental_dir_removal () {
35
+ works=$1 &&
36
+ shift &&
37
+
38
+ test_when_finished "git reset --hard" &&
39
+
40
+ git checkout foo/bar/baz^{commit} &&
41
+ test_path_is_dir foo/bar &&
42
+
43
+ (
44
+ cd foo &&
45
+ "$@" &&
46
+
47
+ # Although we want pwd & git status to pass, test for existing
48
+ # rather than desired behavior.
49
+ if test "$works" = "success"
50
+ then
51
+ test-tool getcwd &&
52
+ git status --porcelain
53
+ else
54
+ ! test-tool getcwd &&
55
+ test_might_fail git status --porcelain
56
+ fi
57
+ ) &&
58
+ test_path_is_missing foo/bar/baz &&
59
+ test_path_is_missing foo/bar &&
60
+
61
+ # Although we want dir to be present, test for existing rather
62
+ # than desired behavior.
63
+ if test "$works" = "success"
64
+ then
65
+ test_path_is_dir foo
66
+ else
67
+ test_path_is_missing foo
68
+ fi
69
+}
70
+
71
+test_required_dir_removal () {
72
+ works=$1 &&
73
+ shift &&
74
+
75
+ git checkout df_conflict^{commit} &&
76
+ test_when_finished "git clean -fdx" &&
77
+
78
+ (
79
+ cd dirORfile &&
80
+
81
+ # We'd like for the command to fail (much as it would if there
82
+ # was an untracked file there), and for the index and worktree
83
+ # to be left clean with pwd and git status working afterwards.
84
+ # But test for existing rather than desired behavior.
85
+ if test "$works" = "success"
86
+ then
87
+ test_must_fail "$@" 2>../error &&
88
+ grep "Refusing to remove.*current working directory" ../error &&
89
+
90
+ git diff --exit-code HEAD &&
91
+
92
+ test-tool getcwd &&
93
+ git status --porcelain
94
+ else
95
+ "$@" &&
96
+ ! test-tool getcwd &&
97
+ test_might_fail git status --porcelain
98
+ fi
99
+ ) &&
100
+
101
+ # Although we want dirORfile to be present, test for existing rather
102
+ # than desired behavior.
103
+ if test "$works" = "success"
104
+ then
105
+ test_path_is_dir dirORfile
106
+ else
107
+ test_path_is_file dirORfile
108
+ fi
109
+}
110
+
111
+test_expect_success 'checkout does not clean cwd incidentally' '
112
+ test_incidental_dir_removal failure git checkout init
113
+'
114
+
115
+test_expect_success 'checkout fails if cwd needs to be removed' '
116
+ test_required_dir_removal failure git checkout fd_conflict
117
+'
118
+
119
+test_expect_success 'reset --hard does not clean cwd incidentally' '
120
+ test_incidental_dir_removal failure git reset --hard init
121
+'
122
+
123
+test_expect_success 'reset --hard fails if cwd needs to be removed' '
124
+ test_required_dir_removal failure git reset --hard fd_conflict
125
+'
126
+
127
+test_expect_success 'merge does not clean cwd incidentally' '
128
+ test_incidental_dir_removal failure git merge reverted
129
+'
130
+
131
+# This file uses some simple merges where
132
+# Base: 'dirORfile/' exists
133
+# Side1: random other file changed
134
+# Side2: 'dirORfile/' removed, 'dirORfile' added
135
+# this should resolve cleanly, but merge-recursive throws merge conflicts
136
+# because it's dumb. Add a special test for checking merge-recursive (and
137
+# merge-ort), then after this just hard require ort for all remaining tests.
138
+#
139
+test_expect_success 'merge fails if cwd needs to be removed; recursive friendly' '
140
+ git checkout foo/bar/baz &&
141
+ test_when_finished "git clean -fdx" &&
142
+
143
+ mkdir dirORfile &&
144
+ (
145
+ cd dirORfile &&
146
+
147
+ # We would rather this failed, but we test for existing
148
+ # rather than desired behavior
149
+ git merge fd_conflict 2>../error
150
+ ) &&
151
+
152
+ ## Here is the behavior we would rather have:
153
+ #test_path_is_dir dirORfile &&
154
+ #grep "Refusing to remove the current working directory" error
155
+ ## But instead we test for existing behavior
156
+ test_path_is_file dirORfile &&
157
+ test_must_be_empty error
158
+'
159
+
160
+GIT_TEST_MERGE_ALGORITHM=ort
161
+
162
+test_expect_success 'merge fails if cwd needs to be removed' '
163
+ test_required_dir_removal failure git merge fd_conflict
164
+'
165
+
166
+test_expect_success 'cherry-pick does not clean cwd incidentally' '
167
+ test_incidental_dir_removal failure git cherry-pick reverted
168
+'
169
+
170
+test_expect_success 'cherry-pick fails if cwd needs to be removed' '
171
+ test_required_dir_removal failure git cherry-pick fd_conflict
172
+'
173
+
174
+test_expect_success 'rebase does not clean cwd incidentally' '
175
+ test_incidental_dir_removal failure git rebase reverted
176
+'
177
+
178
+test_expect_success 'rebase fails if cwd needs to be removed' '
179
+ test_required_dir_removal failure git rebase fd_conflict
180
+'
181
+
182
+test_expect_success 'revert does not clean cwd incidentally' '
183
+ test_incidental_dir_removal failure git revert HEAD
184
+'
185
+
186
+test_expect_success 'revert fails if cwd needs to be removed' '
187
+ test_required_dir_removal failure git revert undo_fd_conflict
188
+'
189
+
190
+test_expect_success 'rm does not clean cwd incidentally' '
191
+ test_incidental_dir_removal failure git rm bar/baz.t
192
+'
193
+
194
+test_expect_success 'apply does not remove cwd incidentally' '
195
+ git diff HEAD HEAD~1 >patch &&
196
+ test_incidental_dir_removal failure git apply ../patch
197
+'
198
+
199
+test_incidental_untracked_dir_removal () {
200
+ works=$1 &&
201
+ shift &&
202
+
203
+ test_when_finished "git reset --hard" &&
204
+
205
+ git checkout foo/bar/baz^{commit} &&
206
+ mkdir -p untracked &&
207
+ mkdir empty
208
+ >untracked/random &&
209
+
210
+ (
211
+ cd untracked &&
212
+ "$@" &&
213
+
214
+ # Although we want pwd & git status to pass, test for existing
215
+ # rather than desired behavior.
216
+ if test "$works" = "success"
217
+ then
218
+ test-tool getcwd &&
219
+ git status --porcelain
220
+ else
221
+ ! test-tool getcwd &&
222
+ test_might_fail git status --porcelain
223
+ fi
224
+ ) &&
225
+ test_path_is_missing empty &&
226
+ test_path_is_missing untracked/random &&
227
+
228
+ # Although we want dir to be present, test for existing rather
229
+ # than desired behavior.
230
+ if test "$works" = "success"
231
+ then
232
+ test_path_is_dir untracked
233
+ else
234
+ test_path_is_missing untracked
235
+ fi
236
+}
237
+
238
+test_expect_success 'clean does not remove cwd incidentally' '
239
+ test_incidental_untracked_dir_removal failure \
240
+ git -C .. clean -fd -e warnings . >warnings
241
+'
242
+
243
+test_expect_success 'stash does not remove cwd incidentally' '
244
+ test_incidental_untracked_dir_removal failure \
245
+ git stash --include-untracked
246
+'
247
+
248
+test_expect_success '`rm -rf dir` only removes a subset of dir' '
249
+ test_when_finished "rm -rf a/" &&
250
+
251
+ mkdir -p a/b/c &&
252
+ >a/b/c/untracked &&
253
+ >a/b/c/tracked &&
254
+ git add a/b/c/tracked &&
255
+
256
+ (
257
+ cd a/b &&
258
+ git rm -rf ../b
259
+ ) &&
260
+
261
+ test_path_is_dir a/b &&
262
+ test_path_is_missing a/b/c/tracked &&
263
+ test_path_is_file a/b/c/untracked
264
+'
265
+
266
+test_expect_success '`rm -rf dir` even with only tracked files will remove something else' '
267
+ test_when_finished "rm -rf a/" &&
268
+
269
+ mkdir -p a/b/c &&
270
+ >a/b/c/tracked &&
271
+ git add a/b/c/tracked &&
272
+
273
+ (
274
+ cd a/b &&
275
+ git rm -rf ../b
276
+ ) &&
277
+
278
+ test_path_is_missing a/b/c/tracked &&
279
+ ## We would prefer if a/b was still present, though empty, since it
280
+ ## was the current working directory
281
+ #test_path_is_dir a/b
282
+ ## But the current behavior is that it not only deletes the directory
283
+ ## a/b as requested, but also goes and deletes a
284
+ test_path_is_missing a
285
+'
286
+
287
+test_expect_success 'git version continues working from a deleted dir' '
288
+ mkdir tmp &&
289
+ (
290
+ cd tmp &&
291
+ rm -rf ../tmp &&
292
+ git version
293
+ )
294
+'
295
+
296
+test_submodule_removal () {
297
+ path_status=$1 &&
298
+ shift &&
299
+
300
+ test_status=
301
+ test "$path_status" = dir && test_status=test_must_fail
302
+
303
+ # Actually, while path_status=dir && test_status=test_must_fail
304
+ # reflect our desired behavior, current behavior is:
305
+ path_status=missing
306
+ test_status=
307
+
308
+ test_when_finished "git reset --hard HEAD~1" &&
309
+ test_when_finished "rm -rf .git/modules/my_submodule" &&
310
+
311
+ git checkout foo/bar/baz &&
312
+
313
+ git init my_submodule &&
314
+ touch my_submodule/file &&
315
+ git -C my_submodule add file &&
316
+ git -C my_submodule commit -m "initial commit" &&
317
+ git submodule add ./my_submodule &&
318
+ git commit -m "Add the submodule" &&
319
+
320
+ (
321
+ cd my_submodule &&
322
+ $test_status "$@"
323
+ ) &&
324
+
325
+ test_path_is_${path_status} my_submodule
326
+}
327
+
328
+test_expect_success 'rm -r with -C leaves submodule if cwd inside' '
329
+ test_submodule_removal dir git -C .. rm -r my_submodule/
330
+'
331
+
332
+test_expect_success 'rm -r leaves submodule if cwd inside' '
333
+ test_submodule_removal dir \
334
+ git --git-dir=../.git --work-tree=.. rm -r ../my_submodule/
335
+'
336
+
337
+test_expect_success 'rm -rf removes submodule even if cwd inside' '
338
+ test_submodule_removal missing \
339
+ git --git-dir=../.git --work-tree=.. rm -rf ../my_submodule/
340
+'
341
+
342
+test_done