Raw
1 #!/bin/sh
2 #
3 # Copyright (c) 2007 Carlos Rica
4 #
5
6 test_description='git reset
7
8 Documented tests for git reset'
9
10 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
11 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
12
13 . ./test-lib.sh
14
15 if test_have_prereq ICONV
16 then
17 commit_msg () {
18 # String "modify 2nd file (changed)" partly in German
19 # (translated with Google Translate),
20 # encoded in UTF-8, used as a commit log message below.
21 msg="modify 2nd file (ge\303\244ndert)\n"
22 if test -n "$1"
23 then
24 printf "$msg" | iconv -f utf-8 -t "$1"
25 else
26 printf "$msg"
27 fi
28 }
29
30 # Tested non-UTF-8 encoding
31 test_encoding="ISO8859-1"
32 else
33 commit_msg () {
34 echo "modify 2nd file (geandert)"
35 }
36
37 # Tested non-UTF-8 encoding
38 test_encoding="UTF-8"
39 fi
40
41 test_expect_success 'creating initial files and commits' '
42 test_tick &&
43 echo "1st file" >first &&
44 git add first &&
45 git commit -m "create 1st file" &&
46
47 echo "2nd file" >second &&
48 git add second &&
49 git commit -m "create 2nd file" &&
50
51 echo "2nd line 1st file" >>first &&
52 git commit -a -m "modify 1st file" &&
53 head5p2=$(git rev-parse --verify HEAD) &&
54 head5p2f=$(git rev-parse --short HEAD:first) &&
55
56 git rm first &&
57 git mv second secondfile &&
58 git commit -a -m "remove 1st and rename 2nd" &&
59 head5p1=$(git rev-parse --verify HEAD) &&
60 head5p1s=$(git rev-parse --short HEAD:secondfile) &&
61
62 echo "1st line 2nd file" >secondfile &&
63 echo "2nd line 2nd file" >>secondfile &&
64 # "git commit -m" would break MinGW, as Windows refuse to pass
65 # $test_encoding encoded parameter to git.
66 commit_msg $test_encoding | git -c "i18n.commitEncoding=$test_encoding" commit -a -F - &&
67 head5=$(git rev-parse --verify HEAD) &&
68 head5s=$(git rev-parse --short HEAD:secondfile) &&
69 head5sl=$(git rev-parse HEAD:secondfile)
70 '
71 # git log --pretty=oneline # to see those SHA1 involved
72
73 check_changes () {
74 test "$(git rev-parse HEAD)" = "$1" &&
75 git diff | test_cmp .diff_expect - &&
76 git diff --cached | test_cmp .cached_expect - &&
77 for FILE in *
78 do
79 echo $FILE':'
80 cat $FILE || return
81 done | test_cmp .cat_expect -
82 }
83
84 # no negated form for various type of resets
85 for opt in soft mixed hard merge keep
86 do
87 test_expect_success "no 'git reset --no-$opt'" '
88 test_when_finished "rm -f err" &&
89 test_must_fail git reset --no-$opt 2>err &&
90 test_grep "error: unknown option .no-$opt." err
91 '
92 done
93
94 test_expect_success 'reset --hard message' '
95 hex=$(git log -1 --format="%h") &&
96 git reset --hard >.actual &&
97 echo HEAD is now at $hex $(commit_msg) >.expected &&
98 test_cmp .expected .actual
99 '
100
101 test_expect_success 'reset --hard message (ISO8859-1 logoutputencoding)' '
102 hex=$(git log -1 --format="%h") &&
103 git -c "i18n.logOutputEncoding=$test_encoding" reset --hard >.actual &&
104 echo HEAD is now at $hex $(commit_msg $test_encoding) >.expected &&
105 test_cmp .expected .actual
106 '
107
108 test_expect_success 'giving a non existing revision should fail' '
109 >.diff_expect &&
110 >.cached_expect &&
111 cat >.cat_expect <<-\EOF &&
112 secondfile:
113 1st line 2nd file
114 2nd line 2nd file
115 EOF
116
117 test_must_fail git reset aaaaaa &&
118 test_must_fail git reset --mixed aaaaaa &&
119 test_must_fail git reset --soft aaaaaa &&
120 test_must_fail git reset --hard aaaaaa &&
121 check_changes $head5
122 '
123
124 test_expect_success 'reset --soft with unmerged index should fail' '
125 touch .git/MERGE_HEAD &&
126 echo "100644 $head5sl 1 un" |
127 git update-index --index-info &&
128 test_must_fail git reset --soft HEAD &&
129 rm .git/MERGE_HEAD &&
130 git rm --cached -- un
131 '
132
133 test_expect_success 'giving paths with options different than --mixed should fail' '
134 test_must_fail git reset --soft -- first &&
135 test_must_fail git reset --hard -- first &&
136 test_must_fail git reset --soft HEAD^ -- first &&
137 test_must_fail git reset --hard HEAD^ -- first &&
138 check_changes $head5
139 '
140
141 test_expect_success 'giving unrecognized options should fail' '
142 test_must_fail git reset --other &&
143 test_must_fail git reset -o &&
144 test_must_fail git reset --mixed --other &&
145 test_must_fail git reset --mixed -o &&
146 test_must_fail git reset --soft --other &&
147 test_must_fail git reset --soft -o &&
148 test_must_fail git reset --hard --other &&
149 test_must_fail git reset --hard -o &&
150 check_changes $head5
151 '
152
153 test_expect_success 'trying to do reset --soft with pending merge should fail' '
154 git branch branch1 &&
155 git branch branch2 &&
156
157 git checkout branch1 &&
158 echo "3rd line in branch1" >>secondfile &&
159 git commit -a -m "change in branch1" &&
160
161 git checkout branch2 &&
162 echo "3rd line in branch2" >>secondfile &&
163 git commit -a -m "change in branch2" &&
164
165 test_must_fail git merge branch1 &&
166 test_must_fail git reset --soft &&
167
168 printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
169 git commit -a -m "the change in branch2" &&
170
171 git checkout main &&
172 git branch -D branch1 branch2 &&
173 check_changes $head5
174 '
175
176 test_expect_success 'trying to do reset --soft with pending checkout merge should fail' '
177 git branch branch3 &&
178 git branch branch4 &&
179
180 git checkout branch3 &&
181 echo "3rd line in branch3" >>secondfile &&
182 git commit -a -m "line in branch3" &&
183
184 git checkout branch4 &&
185 echo "3rd line in branch4" >>secondfile &&
186
187 git checkout -m branch3 &&
188 test_must_fail git reset --soft &&
189
190 printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
191 git commit -a -m "the line in branch3" &&
192
193 git checkout main &&
194 git branch -D branch3 branch4 &&
195 check_changes $head5
196 '
197
198 test_expect_success 'resetting to HEAD with no changes should succeed and do nothing' '
199 git reset --hard &&
200 check_changes $head5 &&
201 git reset --hard HEAD &&
202 check_changes $head5 &&
203 git reset --soft &&
204 check_changes $head5 &&
205 git reset --soft HEAD &&
206 check_changes $head5 &&
207 git reset --mixed &&
208 check_changes $head5 &&
209 git reset --mixed HEAD &&
210 check_changes $head5 &&
211 git reset &&
212 check_changes $head5 &&
213 git reset HEAD &&
214 check_changes $head5
215 '
216
217 test_expect_success '--soft reset only should show changes in diff --cached' '
218 >.diff_expect &&
219 cat >.cached_expect <<-EOF &&
220 diff --git a/secondfile b/secondfile
221 index $head5p1s..$head5s 100644
222 --- a/secondfile
223 +++ b/secondfile
224 @@ -1 +1,2 @@
225 -2nd file
226 +1st line 2nd file
227 +2nd line 2nd file
228 EOF
229 cat >.cat_expect <<-\EOF &&
230 secondfile:
231 1st line 2nd file
232 2nd line 2nd file
233 EOF
234 git reset --soft HEAD^ &&
235 check_changes $head5p1 &&
236 test "$(git rev-parse ORIG_HEAD)" = \
237 $head5
238 '
239
240 test_expect_success 'changing files and redo the last commit should succeed' '
241 >.diff_expect &&
242 >.cached_expect &&
243 cat >.cat_expect <<-\EOF &&
244 secondfile:
245 1st line 2nd file
246 2nd line 2nd file
247 3rd line 2nd file
248 EOF
249 echo "3rd line 2nd file" >>secondfile &&
250 git commit -a -C ORIG_HEAD &&
251 head4=$(git rev-parse --verify HEAD) &&
252 check_changes $head4 &&
253 test "$(git rev-parse ORIG_HEAD)" = \
254 $head5
255 '
256
257 test_expect_success '--hard reset should change the files and undo commits permanently' '
258 >.diff_expect &&
259 >.cached_expect &&
260 cat >.cat_expect <<-\EOF &&
261 first:
262 1st file
263 2nd line 1st file
264 second:
265 2nd file
266 EOF
267 git reset --hard HEAD~2 &&
268 check_changes $head5p2 &&
269 test "$(git rev-parse ORIG_HEAD)" = \
270 $head4
271 '
272
273 test_expect_success 'redoing changes adding them without commit them should succeed' '
274 >.diff_expect &&
275 cat >.cached_expect <<-EOF &&
276 diff --git a/first b/first
277 deleted file mode 100644
278 index $head5p2f..0000000
279 --- a/first
280 +++ /dev/null
281 @@ -1,2 +0,0 @@
282 -1st file
283 -2nd line 1st file
284 diff --git a/second b/second
285 deleted file mode 100644
286 index $head5p1s..0000000
287 --- a/second
288 +++ /dev/null
289 @@ -1 +0,0 @@
290 -2nd file
291 diff --git a/secondfile b/secondfile
292 new file mode 100644
293 index 0000000..$head5s
294 --- /dev/null
295 +++ b/secondfile
296 @@ -0,0 +1,2 @@
297 +1st line 2nd file
298 +2nd line 2nd file
299 EOF
300 cat >.cat_expect <<-\EOF &&
301 secondfile:
302 1st line 2nd file
303 2nd line 2nd file
304 EOF
305 git rm first &&
306 git mv second secondfile &&
307
308 echo "1st line 2nd file" >secondfile &&
309 echo "2nd line 2nd file" >>secondfile &&
310 git add secondfile &&
311 check_changes $head5p2
312 '
313
314 test_expect_success '--mixed reset to HEAD should unadd the files' '
315 cat >.diff_expect <<-EOF &&
316 diff --git a/first b/first
317 deleted file mode 100644
318 index $head5p2f..0000000
319 --- a/first
320 +++ /dev/null
321 @@ -1,2 +0,0 @@
322 -1st file
323 -2nd line 1st file
324 diff --git a/second b/second
325 deleted file mode 100644
326 index $head5p1s..0000000
327 --- a/second
328 +++ /dev/null
329 @@ -1 +0,0 @@
330 -2nd file
331 EOF
332 >.cached_expect &&
333 cat >.cat_expect <<-\EOF &&
334 secondfile:
335 1st line 2nd file
336 2nd line 2nd file
337 EOF
338 git reset &&
339 check_changes $head5p2 &&
340 test "$(git rev-parse ORIG_HEAD)" = $head5p2
341 '
342
343 test_expect_success 'redoing the last two commits should succeed' '
344 >.diff_expect &&
345 >.cached_expect &&
346 cat >.cat_expect <<-\EOF &&
347 secondfile:
348 1st line 2nd file
349 2nd line 2nd file
350 EOF
351 git add secondfile &&
352 git reset --hard $head5p2 &&
353 git rm first &&
354 git mv second secondfile &&
355 git commit -a -m "remove 1st and rename 2nd" &&
356
357 echo "1st line 2nd file" >secondfile &&
358 echo "2nd line 2nd file" >>secondfile &&
359 # "git commit -m" would break MinGW, as Windows refuse to pass
360 # $test_encoding encoded parameter to git.
361 commit_msg $test_encoding | git -c "i18n.commitEncoding=$test_encoding" commit -a -F - &&
362 check_changes $head5
363 '
364
365 test_expect_success '--hard reset to HEAD should clear a failed merge' '
366 >.diff_expect &&
367 >.cached_expect &&
368 cat >.cat_expect <<-\EOF &&
369 secondfile:
370 1st line 2nd file
371 2nd line 2nd file
372 3rd line in branch2
373 EOF
374 git branch branch1 &&
375 git branch branch2 &&
376
377 git checkout branch1 &&
378 echo "3rd line in branch1" >>secondfile &&
379 git commit -a -m "change in branch1" &&
380
381 git checkout branch2 &&
382 echo "3rd line in branch2" >>secondfile &&
383 git commit -a -m "change in branch2" &&
384 head3=$(git rev-parse --verify HEAD) &&
385
386 test_must_fail git pull . branch1 &&
387 git reset --hard &&
388 check_changes $head3
389 '
390
391 test_expect_success '--hard reset to ORIG_HEAD should clear a fast-forward merge' '
392 >.diff_expect &&
393 >.cached_expect &&
394 cat >.cat_expect <<-\EOF &&
395 secondfile:
396 1st line 2nd file
397 2nd line 2nd file
398 EOF
399 git reset --hard HEAD^ &&
400 check_changes $head5 &&
401
402 git pull . branch1 &&
403 git reset --hard ORIG_HEAD &&
404 check_changes $head5 &&
405
406 git checkout main &&
407 git branch -D branch1 branch2 &&
408 check_changes $head5
409 '
410
411 test_expect_success 'test --mixed <paths>' '
412 echo 1 >file1 &&
413 echo 2 >file2 &&
414 git add file1 file2 &&
415 test_tick &&
416 git commit -m files &&
417 before1=$(git rev-parse --short HEAD:file1) &&
418 before2=$(git rev-parse --short HEAD:file2) &&
419 git rm file2 &&
420 echo 3 >file3 &&
421 echo 4 >file4 &&
422 echo 5 >file1 &&
423 after1=$(git rev-parse --short $(git hash-object file1)) &&
424 after4=$(git rev-parse --short $(git hash-object file4)) &&
425 git add file1 file3 file4 &&
426 git reset HEAD -- file1 file2 file3 &&
427 test_must_fail git diff --quiet &&
428 git diff >output &&
429
430 cat >expect <<-EOF &&
431 diff --git a/file1 b/file1
432 index $before1..$after1 100644
433 --- a/file1
434 +++ b/file1
435 @@ -1 +1 @@
436 -1
437 +5
438 diff --git a/file2 b/file2
439 deleted file mode 100644
440 index $before2..0000000
441 --- a/file2
442 +++ /dev/null
443 @@ -1 +0,0 @@
444 -2
445 EOF
446
447 test_cmp expect output &&
448 git diff --cached >output &&
449
450 cat >cached_expect <<-EOF &&
451 diff --git a/file4 b/file4
452 new file mode 100644
453 index 0000000..$after4
454 --- /dev/null
455 +++ b/file4
456 @@ -0,0 +1 @@
457 +4
458 EOF
459
460 test_cmp cached_expect output
461 '
462
463 test_expect_success 'test resetting the index at give paths' '
464 mkdir sub &&
465 >sub/file1 &&
466 >sub/file2 &&
467 git update-index --add sub/file1 sub/file2 &&
468 T=$(git write-tree) &&
469 git reset HEAD sub/file2 &&
470 test_must_fail git diff --quiet &&
471 U=$(git write-tree) &&
472 echo "$T" &&
473 echo "$U" &&
474 test_must_fail git diff-index --cached --exit-code "$T" &&
475 test "$T" != "$U"
476 '
477
478 test_expect_success 'resetting an unmodified path is a no-op' '
479 git reset --hard &&
480 git reset -- file1 &&
481 git diff-files --exit-code &&
482 git diff-index --cached --exit-code HEAD
483 '
484
485 test_reset_refreshes_index () {
486
487 # To test whether the index is refreshed in `git reset --mixed` with
488 # the given options, create a scenario where we clearly see different
489 # results depending on whether the refresh occurred or not.
490
491 # Step 0: start with a clean index
492 git reset --hard HEAD &&
493
494 # Step 1: remove file2, but only in the index (no change to worktree)
495 git rm --cached file2 &&
496
497 # Step 2: reset index & leave worktree unchanged from HEAD
498 git $1 reset $2 --mixed HEAD &&
499
500 # Step 3: verify whether the index is refreshed by checking whether
501 # file2 still has staged changes in the index differing from HEAD (if
502 # the refresh occurred, there should be no such changes)
503 git diff-files >output.log &&
504 test_must_be_empty output.log
505 }
506
507 test_expect_success '--mixed refreshes the index' '
508 # Verify default behavior (without --[no-]refresh or reset.refresh)
509 test_reset_refreshes_index &&
510
511 # With --quiet
512 test_reset_refreshes_index "" --quiet
513 '
514
515 test_expect_success '--mixed --[no-]refresh sets refresh behavior' '
516 # Verify that --[no-]refresh controls index refresh
517 test_reset_refreshes_index "" --refresh &&
518 ! test_reset_refreshes_index "" --no-refresh
519 '
520
521 test_expect_success '--mixed preserves skip-worktree' '
522 echo 123 >>file2 &&
523 git add file2 &&
524 git update-index --skip-worktree file2 &&
525 git reset --mixed HEAD >output &&
526 test_must_be_empty output &&
527
528 cat >expect <<-\EOF &&
529 Unstaged changes after reset:
530 M file2
531 EOF
532 git update-index --no-skip-worktree file2 &&
533 git add file2 &&
534 git reset --mixed HEAD >output &&
535 test_cmp expect output
536 '
537
538 test_expect_success 'resetting specific path that is unmerged' '
539 git rm --cached file2 &&
540 F1=$(git rev-parse HEAD:file1) &&
541 F2=$(git rev-parse HEAD:file2) &&
542 F3=$(git rev-parse HEAD:secondfile) &&
543 {
544 echo "100644 $F1 1 file2" &&
545 echo "100644 $F2 2 file2" &&
546 echo "100644 $F3 3 file2"
547 } | git update-index --index-info &&
548 git ls-files -u &&
549 git reset HEAD file2 &&
550 test_must_fail git diff --quiet &&
551 git diff-index --exit-code --cached HEAD
552 '
553
554 test_expect_success 'disambiguation (1)' '
555 git reset --hard &&
556 >secondfile &&
557 git add secondfile &&
558 git reset secondfile &&
559 test_must_fail git diff --quiet -- secondfile &&
560 test -z "$(git diff --cached --name-only)" &&
561 test -f secondfile &&
562 test_must_be_empty secondfile
563 '
564
565 test_expect_success 'disambiguation (2)' '
566 git reset --hard &&
567 >secondfile &&
568 git add secondfile &&
569 rm -f secondfile &&
570 test_must_fail git reset secondfile &&
571 test -n "$(git diff --cached --name-only -- secondfile)" &&
572 test ! -f secondfile
573 '
574
575 test_expect_success 'disambiguation (3)' '
576 git reset --hard &&
577 >secondfile &&
578 git add secondfile &&
579 rm -f secondfile &&
580 git reset HEAD secondfile &&
581 test_must_fail git diff --quiet &&
582 test -z "$(git diff --cached --name-only)" &&
583 test ! -f secondfile
584 '
585
586 test_expect_success 'disambiguation (4)' '
587 git reset --hard &&
588 >secondfile &&
589 git add secondfile &&
590 rm -f secondfile &&
591 git reset -- secondfile &&
592 test_must_fail git diff --quiet &&
593 test -z "$(git diff --cached --name-only)" &&
594 test ! -f secondfile
595 '
596
597 test_expect_success 'reset with paths accepts tree' '
598 # for simpler tests, drop last commit containing added files
599 git reset --hard HEAD^ &&
600 git reset HEAD^^{tree} -- . &&
601 git diff --cached HEAD^ --exit-code &&
602 git diff HEAD --exit-code
603 '
604
605 test_expect_success 'reset -N keeps removed files as intent-to-add' '
606 echo new-file >new-file &&
607 git add new-file &&
608 git reset -N HEAD &&
609
610 tree=$(git write-tree) &&
611 git ls-tree $tree new-file >actual &&
612 test_must_be_empty actual &&
613
614 git diff --name-only >actual &&
615 echo new-file >expect &&
616 test_cmp expect actual
617 '
618
619 test_expect_success 'reset --mixed sets up work tree' '
620 git init mixed_worktree &&
621 (
622 cd mixed_worktree &&
623 test_commit dummy
624 ) &&
625 git --git-dir=mixed_worktree/.git --work-tree=mixed_worktree reset >actual &&
626 test_must_be_empty actual
627 '
628
629 test_expect_success 'reset handles --end-of-options' '
630 git update-ref refs/heads/--foo HEAD^ &&
631 git log -1 --format=%s refs/heads/--foo >expect &&
632 git reset --hard --end-of-options --foo &&
633 git log -1 --format=%s HEAD >actual &&
634 test_cmp expect actual
635 '
636
637 test_done