3 # Copyright (c) 2008 Christian Couder
5 test_description
='Tests replace refs functionality'
7 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
=main
8 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
10 TEST_PASSES_SANITIZE_LEAK
=true
12 .
"$TEST_DIRECTORY/lib-gpg.sh"
14 add_and_commit_file
()
19 git add
$_file ||
return $?
20 test_tick ||
return $?
21 git commit
--quiet -m "$_file: $_msg"
24 commit_buffer_contains_parents
()
26 git cat-file commit
"$1" >payload
&&
27 sed -n -e '/^$/q' -e '/^parent /p' <payload
>actual
&&
31 echo "parent $_parent"
33 test_cmp expected actual
36 commit_peeling_shows_parents
()
43 _found
=$
(git rev-parse
--verify $_commit^
$_parent_number) ||
return 1
44 test "$_found" = "$_parent" ||
return 1
45 _parent_number
=$
(( $_parent_number + 1 ))
47 test_must_fail git rev-parse
--verify $_commit^
$_parent_number 2>err
&&
48 test_grep
"Needed a single revision" err
53 commit_buffer_contains_parents
"$@" &&
54 commit_peeling_shows_parents
"$@"
65 test_expect_success
'set up buggy branch' '
66 echo "line 1" >>hello &&
67 echo "line 2" >>hello &&
68 echo "line 3" >>hello &&
69 echo "line 4" >>hello &&
70 add_and_commit_file hello "4 lines" &&
71 HASH1=$(git rev-parse --verify HEAD) &&
72 echo "line BUG" >>hello &&
73 echo "line 6" >>hello &&
74 echo "line 7" >>hello &&
75 echo "line 8" >>hello &&
76 add_and_commit_file hello "4 more lines with a BUG" &&
77 HASH2=$(git rev-parse --verify HEAD) &&
78 echo "line 9" >>hello &&
79 echo "line 10" >>hello &&
80 add_and_commit_file hello "2 more lines" &&
81 HASH3=$(git rev-parse --verify HEAD) &&
82 echo "line 11" >>hello &&
83 add_and_commit_file hello "1 more line" &&
84 HASH4=$(git rev-parse --verify HEAD) &&
85 sed -e "s/BUG/5/" hello >hello.new &&
87 add_and_commit_file hello "BUG fixed" &&
88 HASH5=$(git rev-parse --verify HEAD) &&
89 echo "line 12" >>hello &&
90 echo "line 13" >>hello &&
91 add_and_commit_file hello "2 more lines" &&
92 HASH6=$(git rev-parse --verify HEAD) &&
93 echo "line 14" >>hello &&
94 echo "line 15" >>hello &&
95 echo "line 16" >>hello &&
96 add_and_commit_file hello "again 3 more lines" &&
97 HASH7=$(git rev-parse --verify HEAD)
100 test_expect_success
'replace the author' '
101 git cat-file commit $HASH2 >actual &&
102 test_grep "author A U Thor" actual &&
103 R=$(sed -e "s/A U/O/" actual | git hash-object -t commit --stdin -w) &&
104 git cat-file commit $R >actual &&
105 test_grep "author O Thor" actual &&
106 git update-ref refs/replace/$HASH2 $R &&
107 git show HEAD~5 >actual &&
108 test_grep "O Thor" actual &&
109 git show $HASH2 >actual &&
110 test_grep "O Thor" actual
113 test_expect_success
'test --no-replace-objects option' '
114 git cat-file commit $HASH2 >actual &&
115 test_grep "author O Thor" actual &&
116 git --no-replace-objects cat-file commit $HASH2 >actual &&
117 test_grep "author A U Thor" actual &&
118 git show $HASH2 >actual &&
119 test_grep "O Thor" actual &&
120 git --no-replace-objects show $HASH2 >actual &&
121 test_grep "A U Thor" actual
124 test_expect_success
'test GIT_NO_REPLACE_OBJECTS env variable' '
125 GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 >actual &&
126 test_grep "author A U Thor" actual &&
127 GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 >actual &&
128 test_grep "A U Thor" actual
131 test_expect_success
'test core.usereplacerefs config option' '
132 test_config core.usereplacerefs false &&
133 git cat-file commit $HASH2 >actual &&
134 test_grep "author A U Thor" actual &&
135 git show $HASH2 >actual &&
136 test_grep "A U Thor" actual
143 tagger T A Gger <> 0 +0000
147 test_expect_success
'tag replaced commit' '
148 git update-ref refs/tags/mytag $(git mktag <tag.sig)
151 test_expect_success
'"git fsck" works' '
152 git fsck main >fsck_main.out &&
153 test_grep "dangling commit $R" fsck_main.out &&
154 test_grep "dangling tag $(git show-ref -s refs/tags/mytag)" fsck_main.out &&
155 test -z "$(git fsck)"
158 test_expect_success
'repack, clone and fetch work' '
160 git clone --no-hardlinks . clone_dir &&
163 git show HEAD~5 >actual &&
164 test_grep "A U Thor" actual &&
165 git show $HASH2 >actual &&
166 test_grep "A U Thor" actual &&
167 git cat-file commit $R &&
169 test_must_fail git cat-file commit $R &&
170 git fetch ../ "refs/replace/*:refs/replace/*" &&
171 git show HEAD~5 >actual &&
172 test_grep "O Thor" actual &&
173 git show $HASH2 >actual &&
174 test_grep "O Thor" actual &&
175 git cat-file commit $R
179 test_expect_success
'"git replace" listing and deleting' '
180 test "$HASH2" = "$(git replace -l)" &&
181 test "$HASH2" = "$(git replace)" &&
182 aa=${HASH2%??????????????????????????????????????} &&
183 test "$HASH2" = "$(git replace --list "$aa*")" &&
184 test_must_fail git replace -d $R &&
185 test_must_fail git replace --delete &&
186 test_must_fail git replace -l -d $HASH2 &&
187 git replace -d $HASH2 &&
188 git show $HASH2 >actual &&
189 test_grep "A U Thor" actual &&
190 test -z "$(git replace -l)"
193 test_expect_success
'"git replace" replacing' '
194 git replace $HASH2 $R &&
195 git show $HASH2 >actual &&
196 test_grep "O Thor" actual &&
197 test_must_fail git replace $HASH2 $R &&
198 git replace -f $HASH2 $R &&
199 test_must_fail git replace -f &&
200 test "$HASH2" = "$(git replace)"
203 test_expect_success
'"git replace" resolves sha1' '
204 SHORTHASH2=$(git rev-parse --short=8 $HASH2) &&
205 git replace -d $SHORTHASH2 &&
206 git replace $SHORTHASH2 $R &&
207 git show $HASH2 >actual &&
208 test_grep "O Thor" actual &&
209 test_must_fail git replace $HASH2 $R &&
210 git replace -f $HASH2 $R &&
211 test_must_fail git replace --force &&
212 test "$HASH2" = "$(git replace)"
215 # This creates a side branch where the bug in H2
216 # does not appear because P2 is created by applying
217 # H2 and squashing H5 into it.
218 # P3, P4 and P6 are created by cherry-picking H3, H4
219 # and H6 respectively.
221 # At this point, we should have the following:
225 # H1-H2-H3-H4-H5-H6-H7
227 # Then we replace H6 with P6.
229 test_expect_success
'create parallel branch without the bug' '
230 git replace -d $HASH2 &&
231 git show $HASH2 >actual &&
232 test_grep "A U Thor" actual &&
233 git checkout $HASH1 &&
234 git cherry-pick $HASH2 &&
235 git show $HASH5 >actual &&
237 git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
238 PARA2=$(git rev-parse --verify HEAD) &&
239 git cherry-pick $HASH3 &&
240 PARA3=$(git rev-parse --verify HEAD) &&
241 git cherry-pick $HASH4 &&
242 PARA4=$(git rev-parse --verify HEAD) &&
243 git cherry-pick $HASH6 &&
244 PARA6=$(git rev-parse --verify HEAD) &&
245 git replace $HASH6 $PARA6 &&
247 cur=$(git rev-parse --verify HEAD) &&
248 test "$cur" = "$HASH7" &&
249 git log --pretty=oneline >actual &&
250 test_grep $PARA2 actual &&
251 git remote add cloned ./clone_dir
254 test_expect_success
'push to cloned repo' '
255 git push cloned $HASH6^:refs/heads/parallel &&
258 git checkout parallel &&
259 git log --pretty=oneline >actual &&
260 test_grep $PARA2 actual
264 test_expect_success
'push branch with replacement' '
265 git cat-file commit $PARA3 >actual &&
266 test_grep "author A U Thor" actual &&
267 S=$(sed -e "s/A U/O/" actual | git hash-object -t commit --stdin -w) &&
268 git cat-file commit $S >actual &&
269 test_grep "author O Thor" actual &&
270 git replace $PARA3 $S &&
271 git show $HASH6~2 >actual &&
272 test_grep "O Thor" actual &&
273 git show $PARA3 >actual &&
274 test_grep "O Thor" actual &&
275 git push cloned $HASH6^:refs/heads/parallel2 &&
278 git checkout parallel2 &&
279 git log --pretty=oneline >actual &&
280 test_grep $PARA3 actual &&
281 git show $PARA3 >actual &&
282 test_grep "A U Thor" actual
286 test_expect_success
'fetch branch with replacement' '
287 git branch tofetch $HASH6 &&
290 git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
291 git log --pretty=oneline parallel3 >output.txt &&
292 test_grep ! $PARA3 output.txt &&
293 git show $PARA3 >para3.txt &&
294 test_grep "A U Thor" para3.txt &&
295 git fetch origin "refs/replace/*:refs/replace/*" &&
296 git log --pretty=oneline parallel3 >output.txt &&
297 test_grep $PARA3 output.txt &&
298 git show $PARA3 >para3.txt &&
299 test_grep "O Thor" para3.txt
303 test_expect_success
'bisect and replacements' '
304 git bisect start $HASH7 $HASH1 &&
305 test "$PARA3" = "$(git rev-parse --verify HEAD)" &&
307 GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
308 test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
310 git --no-replace-objects bisect start $HASH7 $HASH1 &&
311 test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
315 test_expect_success
'index-pack and replacements' '
316 git --no-replace-objects rev-list --objects HEAD >actual &&
317 git --no-replace-objects pack-objects test- <actual &&
318 git index-pack test-*.pack
321 test_expect_success
'not just commits' '
322 echo replaced >file &&
324 REPLACED=$(git rev-parse :file) &&
325 mv file file.replaced &&
327 echo original >file &&
329 ORIGINAL=$(git rev-parse :file) &&
330 git update-ref refs/replace/$ORIGINAL $REPLACED &&
331 mv file file.original &&
334 test_cmp file.replaced file
337 test_expect_success
'replaced and replacement objects must be of the same type' '
338 test_must_fail git replace mytag $HASH1 &&
339 test_must_fail git replace HEAD^{tree} HEAD~1 &&
340 BLOB=$(git rev-parse :file) &&
341 test_must_fail git replace HEAD^ $BLOB
344 test_expect_success
'-f option bypasses the type check' '
345 git replace -f mytag $HASH1 &&
346 git replace --force HEAD^{tree} HEAD~1 &&
347 git replace -f HEAD^ $BLOB
350 test_expect_success
'git cat-file --batch works on replace objects' '
351 git replace >actual &&
352 test_grep $PARA3 actual &&
353 echo $PARA3 | git cat-file --batch
356 test_expect_success
'test --format bogus' '
357 test_must_fail git replace --format bogus >/dev/null 2>&1
360 test_expect_success
'test --format short' '
361 git replace --format=short >actual &&
362 git replace >expected &&
363 test_cmp expected actual
366 test_expect_success
'test --format medium' '
367 H1=$(git --no-replace-objects rev-parse HEAD~1) &&
368 HT=$(git --no-replace-objects rev-parse HEAD^{tree}) &&
369 MYTAG=$(git --no-replace-objects rev-parse mytag) &&
371 echo "$H1 -> $BLOB" &&
372 echo "$BLOB -> $REPLACED" &&
374 echo "$PARA3 -> $S" &&
375 echo "$MYTAG -> $HASH1"
376 } | sort >expected &&
377 git replace -l --format medium >output &&
378 sort output >actual &&
379 test_cmp expected actual
382 test_expect_success
'test --format long' '
384 echo "$H1 (commit) -> $BLOB (blob)" &&
385 echo "$BLOB (blob) -> $REPLACED (blob)" &&
386 echo "$HT (tree) -> $H1 (commit)" &&
387 echo "$PARA3 (commit) -> $S (commit)" &&
388 echo "$MYTAG (tag) -> $HASH1 (commit)"
389 } | sort >expected &&
390 git replace --format=long >output &&
391 sort output >actual &&
392 test_cmp expected actual
395 test_expect_success
'setup fake editors' '
396 write_script fakeeditor <<-\EOF &&
397 sed -e "s/A U Thor/A fake Thor/" "$1" >"$1.new"
400 write_script failingfakeeditor <<-\EOF
406 test_expect_success
'--edit with and without already replaced object' '
407 test_must_fail env GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" &&
408 GIT_EDITOR=./fakeeditor git replace --force --edit "$PARA3" &&
409 git replace -l >actual &&
410 test_grep "$PARA3" actual &&
411 git cat-file commit "$PARA3" >actual &&
412 test_grep "A fake Thor" actual &&
413 git replace -d "$PARA3" &&
414 GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" &&
415 git replace -l >actual &&
416 test_grep "$PARA3" actual &&
417 git cat-file commit "$PARA3" >actual &&
418 test_grep "A fake Thor" actual
421 test_expect_success
'--edit and change nothing or command failed' '
422 git replace -d "$PARA3" &&
423 test_must_fail env GIT_EDITOR=true git replace --edit "$PARA3" &&
424 test_must_fail env GIT_EDITOR="./failingfakeeditor" git replace --edit "$PARA3" &&
425 GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" &&
426 git replace -l >actual &&
427 test_grep "$PARA3" actual &&
428 git cat-file commit "$PARA3" >actual &&
429 test_grep "A fake Thor" actual
432 test_expect_success
'replace ref cleanup' '
433 test -n "$(git replace)" &&
434 git replace -d $(git replace) &&
435 test -z "$(git replace)"
438 test_expect_success
'--graft with and without already replaced object' '
439 git log --oneline >log &&
440 test_line_count = 7 log &&
441 git replace --graft $HASH5 &&
442 git log --oneline >log &&
443 test_line_count = 3 log &&
444 commit_has_parents $HASH5 &&
445 test_must_fail git replace --graft $HASH5 $HASH4 $HASH3 &&
446 git replace --force -g $HASH5 $HASH4 $HASH3 &&
447 commit_has_parents $HASH5 $HASH4 $HASH3 &&
448 git replace -d $HASH5
451 test_expect_success
'--graft using a tag as the new parent' '
452 git tag new_parent $HASH5 &&
453 git replace --graft $HASH7 new_parent &&
454 commit_has_parents $HASH7 $HASH5 &&
455 git replace -d $HASH7 &&
456 git tag -a -m "annotated new parent tag" annotated_new_parent $HASH5 &&
457 git replace --graft $HASH7 annotated_new_parent &&
458 commit_has_parents $HASH7 $HASH5 &&
459 git replace -d $HASH7
462 test_expect_success
'--graft using a tag as the replaced object' '
463 git tag replaced_object $HASH7 &&
464 git replace --graft replaced_object $HASH5 &&
465 commit_has_parents $HASH7 $HASH5 &&
466 git replace -d $HASH7 &&
467 git tag -a -m "annotated replaced object tag" annotated_replaced_object $HASH7 &&
468 git replace --graft annotated_replaced_object $HASH5 &&
469 commit_has_parents $HASH7 $HASH5 &&
470 git replace -d $HASH7
473 test_expect_success GPG
'set up a signed commit' '
474 echo "line 17" >>hello &&
475 echo "line 18" >>hello &&
478 git commit --quiet -S -m "hello: 2 more lines in a signed commit" &&
479 HASH8=$(git rev-parse --verify HEAD) &&
480 git verify-commit $HASH8
483 test_expect_success GPG
'--graft with a signed commit' '
484 git cat-file commit $HASH8 >orig &&
485 git replace --graft $HASH8 &&
486 git cat-file commit $HASH8 >repl &&
487 commit_has_parents $HASH8 &&
488 test_must_fail git verify-commit $HASH8 &&
489 sed -n -e "/^tree /p" -e "/^author /p" -e "/^committer /p" orig >expected &&
491 sed -e "/^$/q" repl >actual &&
492 test_cmp expected actual &&
493 git replace -d $HASH8
496 test_expect_success GPG
'set up a merge commit with a mergetag' '
497 git reset --hard HEAD &&
498 git checkout -b test_branch HEAD~2 &&
499 echo "line 1 from test branch" >>hello &&
500 echo "line 2 from test branch" >>hello &&
503 git commit -m "hello: 2 more lines from a test branch" &&
504 HASH9=$(git rev-parse --verify HEAD) &&
505 git tag -s -m "tag for testing with a mergetag" test_tag HEAD &&
507 git merge -s ours test_tag &&
508 HASH10=$(git rev-parse --verify HEAD) &&
509 git cat-file commit $HASH10 >actual &&
510 test_grep "^mergetag object" actual
513 test_expect_success GPG
'--graft on a commit with a mergetag' '
514 test_must_fail git replace --graft $HASH10 $HASH8^1 &&
515 git replace --graft $HASH10 $HASH8^1 $HASH9 &&
516 git replace -d $HASH10
519 test_expect_success
'--convert-graft-file' '
520 git checkout -b with-graft-file &&
522 git reset --hard root2^ &&
524 test_commit after-root1 &&
526 git merge -m merge-root2 root2 &&
528 : add and convert graft file &&
529 printf "%s\n%s %s\n\n# comment\n%s\n" \
530 $(git rev-parse HEAD^^ HEAD^ HEAD^^ HEAD^2) \
532 git status 2>stderr &&
533 test_grep "hint:.*grafts is deprecated" stderr &&
534 git replace --convert-graft-file 2>stderr &&
535 test_grep ! "hint:.*grafts is deprecated" stderr &&
536 test_path_is_missing .git/info/grafts &&
538 : verify that the history is now "grafted" &&
539 git rev-list HEAD >out &&
540 test_line_count = 4 out &&
542 : create invalid graft file and verify that it is not deleted &&
543 test_when_finished "rm -f .git/info/grafts" &&
544 echo $EMPTY_BLOB $EMPTY_TREE >.git/info/grafts &&
545 test_must_fail git replace --convert-graft-file 2>err &&
546 test_grep "$EMPTY_BLOB $EMPTY_TREE" err &&
547 test_grep "$EMPTY_BLOB $EMPTY_TREE" .git/info/grafts