Raw
1 #!/bin/sh
2
3 test_description='git mv in subdirs'
4
5 . ./test-lib.sh
6 . "$TEST_DIRECTORY"/lib-diff-data.sh
7
8 index_at_path () {
9 git ls-files --format='%(objectmode) %(objectname) %(stage)' "$@"
10 }
11
12 test_expect_success 'mv -f refreshes updated index entry' '
13 echo test >bar &&
14 git add bar &&
15 git commit -m test &&
16
17 echo foo >foo &&
18 git add foo &&
19
20 # Wait one second to ensure ctime of rename will differ from original
21 # file creation ctime.
22 sleep 1 &&
23 git mv -f foo bar &&
24 git reset --merge HEAD &&
25
26 # Verify the index has been reset
27 git diff-files >out &&
28 test_must_be_empty out
29 '
30
31 test_expect_success 'prepare reference tree' '
32 mkdir path0 path1 &&
33 COPYING_test_data >path0/COPYING &&
34 git add path0/COPYING &&
35 git commit -m add -a
36 '
37
38 test_expect_success 'moving the file out of subdirectory' '
39 git -C path0 mv COPYING ../path1/COPYING
40 '
41
42 # in path0 currently
43 test_expect_success 'commiting the change' '
44 git commit -m move-out -a
45 '
46
47 test_expect_success 'checking the commit' '
48 git diff-tree -r -M --name-status HEAD^ HEAD >actual &&
49 test_grep "^R100..*path0/COPYING..*path1/COPYING" actual
50 '
51
52 test_expect_success 'moving the file back into subdirectory' '
53 git -C path0 mv ../path1/COPYING COPYING
54 '
55
56 # in path0 currently
57 test_expect_success 'commiting the change' '
58 git commit -m move-in -a
59 '
60
61 test_expect_success 'checking the commit' '
62 git diff-tree -r -M --name-status HEAD^ HEAD >actual &&
63 test_grep "^R100..*path1/COPYING..*path0/COPYING" actual
64 '
65
66 test_expect_success 'mv --dry-run does not move file' '
67 git mv -n path0/COPYING MOVED &&
68 test_path_is_file path0/COPYING &&
69 test_path_is_missing MOVED
70 '
71
72 test_expect_success 'checking -k on non-existing file' '
73 git mv -k idontexist path0
74 '
75
76 test_expect_success 'checking -k on untracked file' '
77 >untracked1 &&
78 git mv -k untracked1 path0 &&
79 test_path_is_file untracked1 &&
80 test_path_is_missing path0/untracked1
81 '
82
83 test_expect_success 'checking -k on multiple untracked files' '
84 >untracked2 &&
85 git mv -k untracked1 untracked2 path0 &&
86 test_path_is_file untracked1 &&
87 test_path_is_file untracked2 &&
88 test_path_is_missing path0/untracked1 &&
89 test_path_is_missing path0/untracked2
90 '
91
92 test_expect_success 'checking -f on untracked file with existing target' '
93 >path0/untracked1 &&
94 test_must_fail git mv -f untracked1 path0 &&
95 test_path_is_missing .git/index.lock &&
96 test_path_is_file untracked1 &&
97 test_path_is_file path0/untracked1
98 '
99
100 # clean up the mess in case bad things happen
101 rm -f idontexist untracked1 untracked2 \
102 path0/idontexist path0/untracked1 path0/untracked2 \
103 .git/index.lock
104 rmdir path1
105
106 test_expect_success 'moving to absent target with trailing slash' '
107 test_must_fail git mv path0/COPYING no-such-dir/ &&
108 test_must_fail git mv path0/COPYING no-such-dir// &&
109 git mv path0/ no-such-dir/ &&
110 test_path_is_dir no-such-dir
111 '
112
113 test_expect_success 'clean up' '
114 git reset --hard
115 '
116
117 test_expect_success 'moving file to directory without trailing slash' '
118 git reset --hard HEAD &&
119 rm -rf file.txt target && mkdir target &&
120 echo content > file.txt &&
121 git add file.txt &&
122 git mv file.txt target &&
123 test_path_is_file target/file.txt
124 '
125
126 test_expect_success 'moving file to a bare filename in the cwd' '
127 git reset --hard &&
128 rm -rf from dest.txt &&
129 mkdir from &&
130 echo content >from/file &&
131 git add from/file &&
132 git mv from/file dest.txt &&
133 test_path_is_file dest.txt
134 '
135
136 test_expect_success 'moving to a non-existent directory' '
137 git reset --hard &&
138 rm -rf from && mkdir from &&
139 echo content >from/file &&
140 git add from/file &&
141 test_must_fail git mv from/file no-such-dir/file 2>actual &&
142 test_grep "destination directory does not exist" actual
143 '
144
145 test_expect_success 'moving to a destination with a file as a leading path component' '
146 git reset --hard &&
147 rm -rf from && mkdir from &&
148 echo contents >from/file &&
149 echo blocker >not-dir &&
150 git add from/file &&
151 test_must_fail git mv from/file not-dir/file 2>actual &&
152 test_grep "destination is not a directory" actual
153 '
154
155 test_expect_success SYMLINKS 'moving to a destination beyond a symlink' '
156 git reset --hard &&
157 rm -rf from regular-dir link-to-dir &&
158 mkdir from regular-dir &&
159 echo contents >from/file &&
160 ln -s regular-dir link-to-dir &&
161 git add from/file &&
162 test_must_fail git mv from/file link-to-dir/file 2>actual &&
163 test_grep "destination is beyond a symbolic link" actual
164 '
165
166 test_expect_success SYMLINKS 'moving to a destination with a symlink as an intermediate component' '
167 git reset --hard &&
168 rm -rf from && mkdir -p from/real/inner &&
169 echo contents >from/file &&
170 ln -s real from/link &&
171 git add from/file from/link &&
172 test_must_fail git mv from/file from/link/inner/dst 2>actual &&
173 test_grep "destination is beyond a symbolic link" actual
174 '
175
176 test_expect_success SYMLINKS 'refuses to overwrite a symlink at the destination' '
177 git reset --hard &&
178 rm -rf from && mkdir from &&
179 echo contents >from/file &&
180 ln -s target from/link &&
181 git add from/file from/link &&
182 test_must_fail git mv from/file from/link 2>actual &&
183 test_grep "destination exists" actual
184 '
185
186 test_expect_success SYMLINKS 'mv through a symlinked leading path does not touch the index' '
187 git reset --hard &&
188 rm -rf from && mkdir from &&
189 echo contents >from/src &&
190 ln -s . from/link &&
191 git add from/src from/link &&
192 git commit -m "setup symlink case" &&
193 git ls-files --stage >expect.index &&
194 test_must_fail git mv from/src from/link/real/dst 2>actual &&
195 test_grep "destination is beyond a symbolic link" actual &&
196 git ls-files --stage >actual.index &&
197 test_cmp expect.index actual.index
198 '
199
200 test_expect_success SYMLINKS 'mv -f does not follow a symlinked leading path' '
201 git reset --hard &&
202 rm -rf from && mkdir from &&
203 echo contents >from/src &&
204 ln -s file from/link &&
205 git add from/src from/link &&
206 test_must_fail git mv -f from/src from/link/dst 2>actual &&
207 test_grep "destination is beyond a symbolic link" actual
208 '
209
210 test_expect_success 'mv --dry-run detects non-existent destination parent directory' '
211 git reset --hard &&
212 rm -rf from && mkdir from &&
213 echo contents >from/file &&
214 git add from/file &&
215 test_must_fail git mv -n from/file no-such-dir/file 2>actual &&
216 test_grep "destination directory does not exist" actual
217 '
218
219 test_expect_success 'moving to existing untracked target with trailing slash' '
220 mkdir path1 &&
221 git mv path0/ path1/ &&
222 test_path_is_dir path1/path0/
223 '
224
225 test_expect_success 'moving to existing tracked target with trailing slash' '
226 mkdir path2 &&
227 >path2/file && git add path2/file &&
228 git mv path1/path0/ path2/ &&
229 test_path_is_dir path2/path0/
230 '
231
232 test_expect_success 'clean up' '
233 git reset --hard
234 '
235
236 test_expect_success 'adding another file' '
237 COPYING_test_data | tr A-Za-z N-ZA-Mn-za-m >path0/README &&
238 git add path0/README &&
239 git commit -m add2 -a
240 '
241
242 test_expect_success 'moving whole subdirectory' '
243 git mv path0 path2
244 '
245
246 test_expect_success 'commiting the change' '
247 git commit -m dir-move -a
248 '
249
250 test_expect_success 'checking the commit' '
251 git diff-tree -r -M --name-status HEAD^ HEAD >actual &&
252 test_grep "^R100..*path0/COPYING..*path2/COPYING" actual &&
253 test_grep "^R100..*path0/README..*path2/README" actual
254 '
255
256 test_expect_success 'succeed when source is a prefix of destination' '
257 git mv path2/COPYING path2/COPYING-renamed
258 '
259
260 test_expect_success 'moving whole subdirectory into subdirectory' '
261 git mv path2 path1
262 '
263
264 test_expect_success 'commiting the change' '
265 git commit -m dir-move -a
266 '
267
268 test_expect_success 'checking the commit' '
269 git diff-tree -r -M --name-status HEAD^ HEAD >actual &&
270 test_grep "^R100..*path2/COPYING..*path1/path2/COPYING" actual &&
271 test_grep "^R100..*path2/README..*path1/path2/README" actual
272 '
273
274 test_expect_success 'do not move directory over existing directory' '
275 mkdir path0 &&
276 mkdir path0/path2 &&
277 test_must_fail git mv path2 path0
278 '
279
280 test_expect_success 'rename directory to non-existing directory' '
281 mkdir dir-a &&
282 >dir-a/f &&
283 git add dir-a &&
284 git mv dir-a non-existing-dir
285 '
286
287 test_expect_success 'move into "."' '
288 git mv path1/path2/ .
289 '
290
291 test_expect_success "Michael Cassar's test case" '
292 rm -fr .git papers partA &&
293 git init &&
294 mkdir -p papers/unsorted papers/all-papers partA &&
295 echo a >papers/unsorted/Thesis.pdf &&
296 echo b >partA/outline.txt &&
297 echo c >papers/unsorted/_another &&
298 git add papers partA &&
299 T1=$(git write-tree) &&
300
301 git mv papers/unsorted/Thesis.pdf papers/all-papers/moo-blah.pdf &&
302
303 T=$(git write-tree) &&
304 git ls-tree -r $T >out &&
305 test_grep partA/outline.txt out
306 '
307
308 rm -fr papers partA path?
309
310 test_expect_success "Sergey Vlasov's test case" '
311 rm -fr .git &&
312 git init &&
313 mkdir ab &&
314 date >ab.c &&
315 date >ab/d &&
316 git add ab.c ab &&
317 git commit -m "initial" &&
318 git mv ab a
319 '
320
321 test_expect_success 'absolute pathname' '
322 (
323 rm -fr mine &&
324 mkdir mine &&
325 cd mine &&
326 test_create_repo one &&
327 cd one &&
328 mkdir sub &&
329 >sub/file &&
330 git add sub/file &&
331
332 git mv sub "$(pwd)/in" &&
333 test_path_is_missing sub &&
334 test_path_is_dir in &&
335 git ls-files --error-unmatch in/file
336 )
337 '
338
339 test_expect_success 'absolute pathname outside should fail' '
340 (
341 rm -fr mine &&
342 mkdir mine &&
343 cd mine &&
344 out=$(pwd) &&
345 test_create_repo one &&
346 cd one &&
347 mkdir sub &&
348 >sub/file &&
349 git add sub/file &&
350
351 test_must_fail git mv sub "$out/out" &&
352 test_path_is_dir sub &&
353 test_path_is_missing ../in &&
354 git ls-files --error-unmatch sub/file
355 )
356 '
357
358 test_expect_success 'git mv to move multiple sources into a directory' '
359 rm -fr .git && git init &&
360 mkdir dir other &&
361 >dir/a.txt &&
362 >dir/b.txt &&
363 git add dir/?.txt &&
364 git mv dir/a.txt dir/b.txt other &&
365 git ls-files >actual &&
366 cat >expect <<-\EOF &&
367 other/a.txt
368 other/b.txt
369 EOF
370 test_cmp expect actual
371 '
372
373 test_expect_success 'git mv should not change sha1 of moved cache entry' '
374 rm -fr .git &&
375 git init &&
376 echo 1 >dirty &&
377 git add dirty &&
378 entry="$(index_at_path dirty)" &&
379 git mv dirty dirty2 &&
380 test "$entry" = "$(index_at_path dirty2)" &&
381 echo 2 >dirty2 &&
382 git mv dirty2 dirty &&
383 test "$entry" = "$(index_at_path dirty)"
384 '
385
386 rm -f dirty dirty2
387
388 # NB: This test is about the error message
389 # as well as the failure.
390 test_expect_success 'git mv error on conflicted file' '
391 rm -fr .git &&
392 git init &&
393 >conflict &&
394 test_when_finished "rm -f conflict" &&
395 cfhash=$(git hash-object -w conflict) &&
396 q_to_tab <<-EOF | git update-index --index-info &&
397 0 $cfhash 0Qconflict
398 100644 $cfhash 1Qconflict
399 EOF
400
401 test_must_fail git mv conflict newname 2>actual &&
402 test_grep "conflicted" actual
403 '
404
405 test_expect_success 'git mv should overwrite symlink to a file' '
406 rm -fr .git &&
407 git init &&
408 echo 1 >moved &&
409 test_ln_s_add moved symlink &&
410 git add moved &&
411 test_must_fail git mv moved symlink &&
412 git mv -f moved symlink &&
413 test_path_is_missing moved &&
414 test_path_is_file symlink &&
415 test "$(cat symlink)" = 1 &&
416 git update-index --refresh &&
417 git diff-files --quiet
418 '
419
420 rm -f moved symlink
421
422 test_expect_success 'git mv should overwrite file with a symlink' '
423 rm -fr .git &&
424 git init &&
425 echo 1 >moved &&
426 test_ln_s_add moved symlink &&
427 git add moved &&
428 test_must_fail git mv symlink moved &&
429 git mv -f symlink moved &&
430 test_path_is_missing symlink &&
431 git update-index --refresh &&
432 git diff-files --quiet
433 '
434
435 test_expect_success SYMLINKS 'check moved symlink' '
436 test_path_is_symlink moved
437 '
438
439 rm -f moved symlink
440
441 test_expect_success 'setup submodule' '
442 test_config_global protocol.file.allow always &&
443 git commit -m initial &&
444 git reset --hard &&
445 git submodule add ./. sub &&
446 echo content >file &&
447 git add file &&
448 git commit -m "added sub and file" &&
449 mkdir -p deep/directory/hierarchy &&
450 git submodule add ./. deep/directory/hierarchy/sub &&
451 git commit -m "added another submodule" &&
452 git branch submodule
453 '
454
455 test_expect_success 'git mv cannot move a submodule in a file' '
456 test_must_fail git mv sub file
457 '
458
459 test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
460 entry="$(index_at_path sub)" &&
461 git rm .gitmodules &&
462 (
463 cd sub &&
464 rm -f .git &&
465 cp -R -P -p ../.git/modules/sub .git &&
466 GIT_WORK_TREE=. git config --unset core.worktree
467 ) &&
468 mkdir mod &&
469 git mv sub mod/sub &&
470 test_path_is_missing sub &&
471 test "$entry" = "$(index_at_path mod/sub)" &&
472 git -C mod/sub status &&
473 git update-index --refresh &&
474 git diff-files --quiet
475 '
476
477 test_expect_success 'git mv moves a submodule with a .git directory and .gitmodules' '
478 rm -rf mod &&
479 git reset --hard &&
480 git submodule update &&
481 entry="$(index_at_path sub)" &&
482 (
483 cd sub &&
484 rm -f .git &&
485 cp -R -P -p ../.git/modules/sub .git &&
486 GIT_WORK_TREE=. git config --unset core.worktree
487 ) &&
488 mkdir mod &&
489 git mv sub mod/sub &&
490 test_path_is_missing sub &&
491 test "$entry" = "$(index_at_path mod/sub)" &&
492 git -C mod/sub status &&
493 echo mod/sub >expected &&
494 git config -f .gitmodules submodule.sub.path >actual &&
495 test_cmp expected actual &&
496 git update-index --refresh &&
497 git diff-files --quiet
498 '
499
500 test_expect_success 'git mv moves a submodule with gitfile' '
501 rm -rf mod &&
502 git reset --hard &&
503 git submodule update &&
504 entry="$(index_at_path sub)" &&
505 mkdir mod &&
506 git -C mod mv ../sub/ . &&
507 test_path_is_missing sub &&
508 test "$entry" = "$(index_at_path mod/sub)" &&
509 git -C mod/sub status &&
510 echo mod/sub >expected &&
511 git config -f .gitmodules submodule.sub.path >actual &&
512 test_cmp expected actual &&
513 git update-index --refresh &&
514 git diff-files --quiet
515 '
516
517 test_expect_success 'mv does not complain when no .gitmodules file is found' '
518 rm -rf mod &&
519 git reset --hard &&
520 git submodule update &&
521 git rm .gitmodules &&
522 entry="$(index_at_path sub)" &&
523 mkdir mod &&
524 git mv sub mod/sub 2>actual.err &&
525 test_must_be_empty actual.err &&
526 test_path_is_missing sub &&
527 test "$entry" = "$(index_at_path mod/sub)" &&
528 git -C mod/sub status &&
529 git update-index --refresh &&
530 git diff-files --quiet
531 '
532
533 test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '
534 rm -rf mod &&
535 git reset --hard &&
536 git submodule update &&
537 git config -f .gitmodules foo.bar true &&
538 entry="$(index_at_path sub)" &&
539 mkdir mod &&
540 test_must_fail git mv sub mod/sub 2>actual.err &&
541 test_file_not_empty actual.err &&
542 test_path_exists sub &&
543 git diff-files --quiet -- sub &&
544 git add .gitmodules &&
545 git mv sub mod/sub 2>actual.err &&
546 test_must_be_empty actual.err &&
547 test_path_is_missing sub &&
548 test "$entry" = "$(index_at_path mod/sub)" &&
549 git -C mod/sub status &&
550 git update-index --refresh &&
551 git diff-files --quiet
552 '
553
554 test_expect_success 'mv issues a warning when section is not found in .gitmodules' '
555 rm -rf mod &&
556 git reset --hard &&
557 git submodule update &&
558 git config -f .gitmodules --remove-section submodule.sub &&
559 git add .gitmodules &&
560 entry="$(index_at_path sub)" &&
561 echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
562 mkdir mod &&
563 git mv sub mod/sub 2>actual.err &&
564 test_cmp expect.err actual.err &&
565 test_path_is_missing sub &&
566 test "$entry" = "$(index_at_path mod/sub)" &&
567 git -C mod/sub status &&
568 git update-index --refresh &&
569 git diff-files --quiet
570 '
571
572 test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
573 rm -rf mod &&
574 git reset --hard &&
575 git submodule update &&
576 mkdir mod &&
577 git mv -n sub mod/sub 2>actual.err &&
578 test_path_is_file sub/.git &&
579 git diff-index --exit-code HEAD &&
580 git update-index --refresh &&
581 git diff-files --quiet -- sub .gitmodules
582 '
583
584 test_expect_success 'checking out a commit before submodule moved needs manual updates' '
585 git mv sub sub2 &&
586 git commit -m "moved sub to sub2" &&
587 git checkout -q HEAD^ 2>actual &&
588 test_grep "^warning: unable to rmdir '\''sub2'\'':" actual &&
589 git status -s sub2 >actual &&
590 echo "?? sub2/" >expected &&
591 test_cmp expected actual &&
592 test_path_is_missing sub/.git &&
593 test_path_is_file sub2/.git &&
594 git submodule update &&
595 test_path_is_file sub/.git &&
596 rm -rf sub2 &&
597 git diff-index --exit-code HEAD &&
598 git update-index --refresh &&
599 git diff-files --quiet -- sub .gitmodules &&
600 git status -s sub2 >actual &&
601 test_must_be_empty actual
602 '
603
604 test_expect_success 'mv -k does not accidentally destroy submodules' '
605 git checkout submodule &&
606 mkdir dummy dest &&
607 git mv -k dummy sub dest &&
608 git status --porcelain >actual &&
609 test_grep "^R sub -> dest/sub" actual &&
610 git reset --hard &&
611 git checkout .
612 '
613
614 test_expect_success 'moving a submodule in nested directories' '
615 (
616 cd deep &&
617 git mv directory ../ &&
618 # git status would fail if the update of linking git dir to
619 # work dir of the submodule failed.
620 git status &&
621 git config -f ../.gitmodules submodule.deep/directory/hierarchy/sub.path >../actual &&
622 echo "directory/hierarchy/sub" >../expect
623 ) &&
624 test_cmp expect actual
625 '
626
627 test_expect_success 'moving nested submodules' '
628 test_config_global protocol.file.allow always &&
629 git commit -am "cleanup commit" &&
630 mkdir sub_nested_nested &&
631 (
632 cd sub_nested_nested &&
633 >nested_level2 &&
634 git init &&
635 git add . &&
636 git commit -m "nested level 2"
637 ) &&
638 mkdir sub_nested &&
639 (
640 cd sub_nested &&
641 >nested_level1 &&
642 git init &&
643 git add . &&
644 git commit -m "nested level 1" &&
645 git submodule add ../sub_nested_nested &&
646 git commit -m "add nested level 2"
647 ) &&
648 git submodule add ./sub_nested nested_move &&
649 git commit -m "add nested_move" &&
650 git submodule update --init --recursive &&
651 git mv nested_move sub_nested_moved &&
652 git status
653 '
654
655 test_expect_success 'moving file and its parent directory at the same time fails' '
656 test_when_finished git reset --hard HEAD &&
657 git reset --hard HEAD &&
658 mkdir -p a &&
659 mkdir -p b &&
660 >a/a.txt &&
661 git add a/a.txt &&
662 cat >expect <<-EOF &&
663 fatal: cannot move both ${SQ}a/a.txt${SQ} and its parent directory ${SQ}a${SQ}
664 EOF
665 test_must_fail git mv a/a.txt a b 2>err &&
666 test_cmp expect err
667 '
668
669 test_expect_success 'moving nested directory and its parent directory at the same time fails' '
670 test_when_finished git reset --hard HEAD &&
671 git reset --hard HEAD &&
672 mkdir -p a/b/c &&
673 >a/b/c/file.txt &&
674 git add a &&
675 mkdir target &&
676 cat >expect <<-EOF &&
677 fatal: cannot move both ${SQ}a/b/c${SQ} and its parent directory ${SQ}a${SQ}
678 EOF
679 test_must_fail git mv a/b/c a target 2>err &&
680 test_cmp expect err
681 '
682
683 test_done