Merge branch 'jk/perf-no-dups'
[git] / t / t7001-mv.sh
1 #!/bin/sh
2
3 test_description='git mv in subdirs'
4 . ./test-lib.sh
5
6 test_expect_success \
7     'prepare reference tree' \
8     'mkdir path0 path1 &&
9      cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
10      git add path0/COPYING &&
11      git commit -m add -a'
12
13 test_expect_success \
14     'moving the file out of subdirectory' \
15     'cd path0 && git mv COPYING ../path1/COPYING'
16
17 # in path0 currently
18 test_expect_success \
19     'commiting the change' \
20     'cd .. && git commit -m move-out -a'
21
22 test_expect_success \
23     'checking the commit' \
24     'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
25     grep "^R100..*path0/COPYING..*path1/COPYING" actual'
26
27 test_expect_success \
28     'moving the file back into subdirectory' \
29     'cd path0 && git mv ../path1/COPYING COPYING'
30
31 # in path0 currently
32 test_expect_success \
33     'commiting the change' \
34     'cd .. && git commit -m move-in -a'
35
36 test_expect_success \
37     'checking the commit' \
38     'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
39     grep "^R100..*path1/COPYING..*path0/COPYING" actual'
40
41 test_expect_success \
42     'mv --dry-run does not move file' \
43     'git mv -n path0/COPYING MOVED &&
44      test -f path0/COPYING &&
45      test ! -f MOVED'
46
47 test_expect_success \
48     'checking -k on non-existing file' \
49     'git mv -k idontexist path0'
50
51 test_expect_success \
52     'checking -k on untracked file' \
53     'touch untracked1 &&
54      git mv -k untracked1 path0 &&
55      test -f untracked1 &&
56      test ! -f path0/untracked1'
57
58 test_expect_success \
59     'checking -k on multiple untracked files' \
60     'touch untracked2 &&
61      git mv -k untracked1 untracked2 path0 &&
62      test -f untracked1 &&
63      test -f untracked2 &&
64      test ! -f path0/untracked1 &&
65      test ! -f path0/untracked2'
66
67 test_expect_success \
68     'checking -f on untracked file with existing target' \
69     'touch path0/untracked1 &&
70      test_must_fail git mv -f untracked1 path0 &&
71      test ! -f .git/index.lock &&
72      test -f untracked1 &&
73      test -f path0/untracked1'
74
75 # clean up the mess in case bad things happen
76 rm -f idontexist untracked1 untracked2 \
77      path0/idontexist path0/untracked1 path0/untracked2 \
78      .git/index.lock
79 rmdir path1
80
81 test_expect_success \
82     'moving to absent target with trailing slash' \
83     'test_must_fail git mv path0/COPYING no-such-dir/ &&
84      test_must_fail git mv path0/COPYING no-such-dir// &&
85      git mv path0/ no-such-dir/ &&
86      test_path_is_dir no-such-dir'
87
88 test_expect_success \
89     'clean up' \
90     'git reset --hard'
91
92 test_expect_success \
93     'moving to existing untracked target with trailing slash' \
94     'mkdir path1 &&
95      git mv path0/ path1/ &&
96      test_path_is_dir path1/path0/'
97
98 test_expect_success \
99     'moving to existing tracked target with trailing slash' \
100     'mkdir path2 &&
101      >path2/file && git add path2/file &&
102      git mv path1/path0/ path2/ &&
103      test_path_is_dir path2/path0/'
104
105 test_expect_success \
106     'clean up' \
107     'git reset --hard'
108
109 test_expect_success \
110     'adding another file' \
111     'cp "$TEST_DIRECTORY"/../README.md path0/README &&
112      git add path0/README &&
113      git commit -m add2 -a'
114
115 test_expect_success \
116     'moving whole subdirectory' \
117     'git mv path0 path2'
118
119 test_expect_success \
120     'commiting the change' \
121     'git commit -m dir-move -a'
122
123 test_expect_success \
124     'checking the commit' \
125     'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
126      grep "^R100..*path0/COPYING..*path2/COPYING" actual &&
127      grep "^R100..*path0/README..*path2/README" actual'
128
129 test_expect_success \
130     'succeed when source is a prefix of destination' \
131     'git mv path2/COPYING path2/COPYING-renamed'
132
133 test_expect_success \
134     'moving whole subdirectory into subdirectory' \
135     'git mv path2 path1'
136
137 test_expect_success \
138     'commiting the change' \
139     'git commit -m dir-move -a'
140
141 test_expect_success \
142     'checking the commit' \
143     'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
144      grep "^R100..*path2/COPYING..*path1/path2/COPYING" actual &&
145      grep "^R100..*path2/README..*path1/path2/README" actual'
146
147 test_expect_success \
148     'do not move directory over existing directory' \
149     'mkdir path0 && mkdir path0/path2 && test_must_fail git mv path2 path0'
150
151 test_expect_success \
152     'move into "."' \
153     'git mv path1/path2/ .'
154
155 test_expect_success "Michael Cassar's test case" '
156         rm -fr .git papers partA &&
157         git init &&
158         mkdir -p papers/unsorted papers/all-papers partA &&
159         echo a > papers/unsorted/Thesis.pdf &&
160         echo b > partA/outline.txt &&
161         echo c > papers/unsorted/_another &&
162         git add papers partA &&
163         T1=$(git write-tree) &&
164
165         git mv papers/unsorted/Thesis.pdf papers/all-papers/moo-blah.pdf &&
166
167         T=$(git write-tree) &&
168         git ls-tree -r $T | verbose grep partA/outline.txt
169 '
170
171 rm -fr papers partA path?
172
173 test_expect_success "Sergey Vlasov's test case" '
174         rm -fr .git &&
175         git init &&
176         mkdir ab &&
177         date >ab.c &&
178         date >ab/d &&
179         git add ab.c ab &&
180         git commit -m 'initial' &&
181         git mv ab a
182 '
183
184 test_expect_success 'absolute pathname' '(
185
186         rm -fr mine &&
187         mkdir mine &&
188         cd mine &&
189         test_create_repo one &&
190         cd one &&
191         mkdir sub &&
192         >sub/file &&
193         git add sub/file &&
194
195         git mv sub "$(pwd)/in" &&
196         ! test -d sub &&
197         test -d in &&
198         git ls-files --error-unmatch in/file
199
200
201 )'
202
203 test_expect_success 'absolute pathname outside should fail' '(
204
205         rm -fr mine &&
206         mkdir mine &&
207         cd mine &&
208         out=$(pwd) &&
209         test_create_repo one &&
210         cd one &&
211         mkdir sub &&
212         >sub/file &&
213         git add sub/file &&
214
215         test_must_fail git mv sub "$out/out" &&
216         test -d sub &&
217         ! test -d ../in &&
218         git ls-files --error-unmatch sub/file
219
220 )'
221
222 test_expect_success 'git mv to move multiple sources into a directory' '
223         rm -fr .git && git init &&
224         mkdir dir other &&
225         >dir/a.txt &&
226         >dir/b.txt &&
227         git add dir/?.txt &&
228         git mv dir/a.txt dir/b.txt other &&
229         git ls-files >actual &&
230         { echo other/a.txt; echo other/b.txt; } >expect &&
231         test_cmp expect actual
232 '
233
234 test_expect_success 'git mv should not change sha1 of moved cache entry' '
235
236         rm -fr .git &&
237         git init &&
238         echo 1 >dirty &&
239         git add dirty &&
240         entry="$(git ls-files --stage dirty | cut -f 1)" &&
241         git mv dirty dirty2 &&
242         [ "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" ] &&
243         echo 2 >dirty2 &&
244         git mv dirty2 dirty &&
245         [ "$entry" = "$(git ls-files --stage dirty | cut -f 1)" ]
246
247 '
248
249 rm -f dirty dirty2
250
251 test_expect_success 'git mv should overwrite symlink to a file' '
252
253         rm -fr .git &&
254         git init &&
255         echo 1 >moved &&
256         test_ln_s_add moved symlink &&
257         git add moved &&
258         test_must_fail git mv moved symlink &&
259         git mv -f moved symlink &&
260         ! test -e moved &&
261         test -f symlink &&
262         test "$(cat symlink)" = 1 &&
263         git update-index --refresh &&
264         git diff-files --quiet
265
266 '
267
268 rm -f moved symlink
269
270 test_expect_success 'git mv should overwrite file with a symlink' '
271
272         rm -fr .git &&
273         git init &&
274         echo 1 >moved &&
275         test_ln_s_add moved symlink &&
276         git add moved &&
277         test_must_fail git mv symlink moved &&
278         git mv -f symlink moved &&
279         ! test -e symlink &&
280         git update-index --refresh &&
281         git diff-files --quiet
282
283 '
284
285 test_expect_success SYMLINKS 'check moved symlink' '
286
287         test -h moved
288 '
289
290 rm -f moved symlink
291
292 test_expect_success 'setup submodule' '
293         git commit -m initial &&
294         git reset --hard &&
295         git submodule add ./. sub &&
296         echo content >file &&
297         git add file &&
298         git commit -m "added sub and file" &&
299         mkdir -p deep/directory/hierarchy &&
300         git submodule add ./. deep/directory/hierarchy/sub &&
301         git commit -m "added another submodule" &&
302         git branch submodule
303 '
304
305 test_expect_success 'git mv cannot move a submodule in a file' '
306         test_must_fail git mv sub file
307 '
308
309 test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
310         entry="$(git ls-files --stage sub | cut -f 1)" &&
311         git rm .gitmodules &&
312         (
313                 cd sub &&
314                 rm -f .git &&
315                 cp -R -P -p ../.git/modules/sub .git &&
316                 GIT_WORK_TREE=. git config --unset core.worktree
317         ) &&
318         mkdir mod &&
319         git mv sub mod/sub &&
320         ! test -e sub &&
321         [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
322         (
323                 cd mod/sub &&
324                 git status
325         ) &&
326         git update-index --refresh &&
327         git diff-files --quiet
328 '
329
330 test_expect_success 'git mv moves a submodule with a .git directory and .gitmodules' '
331         rm -rf mod &&
332         git reset --hard &&
333         git submodule update &&
334         entry="$(git ls-files --stage sub | cut -f 1)" &&
335         (
336                 cd sub &&
337                 rm -f .git &&
338                 cp -R -P -p ../.git/modules/sub .git &&
339                 GIT_WORK_TREE=. git config --unset core.worktree
340         ) &&
341         mkdir mod &&
342         git mv sub mod/sub &&
343         ! test -e sub &&
344         [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
345         (
346                 cd mod/sub &&
347                 git status
348         ) &&
349         echo mod/sub >expected &&
350         git config -f .gitmodules submodule.sub.path >actual &&
351         test_cmp expected actual &&
352         git update-index --refresh &&
353         git diff-files --quiet
354 '
355
356 test_expect_success 'git mv moves a submodule with gitfile' '
357         rm -rf mod &&
358         git reset --hard &&
359         git submodule update &&
360         entry="$(git ls-files --stage sub | cut -f 1)" &&
361         mkdir mod &&
362         (
363                 cd mod &&
364                 git mv ../sub/ .
365         ) &&
366         ! test -e sub &&
367         [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
368         (
369                 cd mod/sub &&
370                 git status
371         ) &&
372         echo mod/sub >expected &&
373         git config -f .gitmodules submodule.sub.path >actual &&
374         test_cmp expected actual &&
375         git update-index --refresh &&
376         git diff-files --quiet
377 '
378
379 test_expect_success 'mv does not complain when no .gitmodules file is found' '
380         rm -rf mod &&
381         git reset --hard &&
382         git submodule update &&
383         git rm .gitmodules &&
384         entry="$(git ls-files --stage sub | cut -f 1)" &&
385         mkdir mod &&
386         git mv sub mod/sub 2>actual.err &&
387         test_must_be_empty actual.err &&
388         ! test -e sub &&
389         [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
390         (
391                 cd mod/sub &&
392                 git status
393         ) &&
394         git update-index --refresh &&
395         git diff-files --quiet
396 '
397
398 test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '
399         rm -rf mod &&
400         git reset --hard &&
401         git submodule update &&
402         git config -f .gitmodules foo.bar true &&
403         entry="$(git ls-files --stage sub | cut -f 1)" &&
404         mkdir mod &&
405         test_must_fail git mv sub mod/sub 2>actual.err &&
406         test -s actual.err &&
407         test -e sub &&
408         git diff-files --quiet -- sub &&
409         git add .gitmodules &&
410         git mv sub mod/sub 2>actual.err &&
411         test_must_be_empty actual.err &&
412         ! test -e sub &&
413         [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
414         (
415                 cd mod/sub &&
416                 git status
417         ) &&
418         git update-index --refresh &&
419         git diff-files --quiet
420 '
421
422 test_expect_success 'mv issues a warning when section is not found in .gitmodules' '
423         rm -rf mod &&
424         git reset --hard &&
425         git submodule update &&
426         git config -f .gitmodules --remove-section submodule.sub &&
427         git add .gitmodules &&
428         entry="$(git ls-files --stage sub | cut -f 1)" &&
429         echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
430         mkdir mod &&
431         git mv sub mod/sub 2>actual.err &&
432         test_i18ncmp expect.err actual.err &&
433         ! test -e sub &&
434         [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
435         (
436                 cd mod/sub &&
437                 git status
438         ) &&
439         git update-index --refresh &&
440         git diff-files --quiet
441 '
442
443 test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
444         rm -rf mod &&
445         git reset --hard &&
446         git submodule update &&
447         mkdir mod &&
448         git mv -n sub mod/sub 2>actual.err &&
449         test -f sub/.git &&
450         git diff-index --exit-code HEAD &&
451         git update-index --refresh &&
452         git diff-files --quiet -- sub .gitmodules
453 '
454
455 test_expect_success 'checking out a commit before submodule moved needs manual updates' '
456         git mv sub sub2 &&
457         git commit -m "moved sub to sub2" &&
458         git checkout -q HEAD^ 2>actual &&
459         test_i18ngrep "^warning: unable to rmdir '\''sub2'\'':" actual &&
460         git status -s sub2 >actual &&
461         echo "?? sub2/" >expected &&
462         test_cmp expected actual &&
463         ! test -f sub/.git &&
464         test -f sub2/.git &&
465         git submodule update &&
466         test -f sub/.git &&
467         rm -rf sub2 &&
468         git diff-index --exit-code HEAD &&
469         git update-index --refresh &&
470         git diff-files --quiet -- sub .gitmodules &&
471         git status -s sub2 >actual &&
472         test_must_be_empty actual
473 '
474
475 test_expect_success 'mv -k does not accidentally destroy submodules' '
476         git checkout submodule &&
477         mkdir dummy dest &&
478         git mv -k dummy sub dest &&
479         git status --porcelain >actual &&
480         grep "^R  sub -> dest/sub" actual &&
481         git reset --hard &&
482         git checkout .
483 '
484
485 test_expect_success 'moving a submodule in nested directories' '
486         (
487                 cd deep &&
488                 git mv directory ../ &&
489                 # git status would fail if the update of linking git dir to
490                 # work dir of the submodule failed.
491                 git status &&
492                 git config -f ../.gitmodules submodule.deep/directory/hierarchy/sub.path >../actual &&
493                 echo "directory/hierarchy/sub" >../expect
494         ) &&
495         test_cmp expect actual
496 '
497
498 test_expect_success 'moving nested submodules' '
499         git commit -am "cleanup commit" &&
500         mkdir sub_nested_nested &&
501         (cd sub_nested_nested &&
502                 touch nested_level2 &&
503                 git init &&
504                 git add . &&
505                 git commit -m "nested level 2"
506         ) &&
507         mkdir sub_nested &&
508         (cd sub_nested &&
509                 touch nested_level1 &&
510                 git init &&
511                 git add . &&
512                 git commit -m "nested level 1" &&
513                 git submodule add ../sub_nested_nested &&
514                 git commit -m "add nested level 2"
515         ) &&
516         git submodule add ./sub_nested nested_move &&
517         git commit -m "add nested_move" &&
518         git submodule update --init --recursive &&
519         git mv nested_move sub_nested_moved &&
520         git status
521 '
522
523 test_done