Blob


1 #!/bin/sh
2 #
3 # Copyright (c) 2019 Stefan Sperling <stsp@openbsd.org>
4 #
5 # Permission to use, copy, modify, and distribute this software for any
6 # purpose with or without fee is hereby granted, provided that the above
7 # copyright notice and this permission notice appear in all copies.
8 #
9 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 . ./common.sh
19 test_cherrypick_basic() {
20 local testroot=`test_init cherrypick_basic`
22 got checkout $testroot/repo $testroot/wt > /dev/null
23 ret=$?
24 if [ $ret -ne 0 ]; then
25 test_done "$testroot" "$ret"
26 return 1
27 fi
29 (cd $testroot/repo && git checkout -q -b newbranch)
30 echo "modified delta on branch" > $testroot/repo/gamma/delta
31 git_commit $testroot/repo -m "committing to delta on newbranch"
33 echo "modified alpha on branch" > $testroot/repo/alpha
34 (cd $testroot/repo && git rm -q beta)
35 echo "new file on branch" > $testroot/repo/epsilon/new
36 (cd $testroot/repo && git add epsilon/new)
37 git_commit $testroot/repo -m "committing more changes on newbranch"
39 local branch_rev=`git_show_head $testroot/repo`
41 echo "modified new file on branch" > $testroot/repo/epsilon/new
42 git_commit $testroot/repo -m "committing more changes on newbranch"
43 local branch_rev2=`git_show_head $testroot/repo`
45 (cd $testroot/wt && got cherrypick $branch_rev > $testroot/stdout)
47 echo "G alpha" > $testroot/stdout.expected
48 echo "D beta" >> $testroot/stdout.expected
49 echo "A epsilon/new" >> $testroot/stdout.expected
50 echo "Merged commit $branch_rev" >> $testroot/stdout.expected
52 cmp -s $testroot/stdout.expected $testroot/stdout
53 ret=$?
54 if [ $ret -ne 0 ]; then
55 diff -u $testroot/stdout.expected $testroot/stdout
56 test_done "$testroot" "$ret"
57 return 1
58 fi
60 echo "modified alpha on branch" > $testroot/content.expected
61 cat $testroot/wt/alpha > $testroot/content
62 cmp -s $testroot/content.expected $testroot/content
63 ret=$?
64 if [ $ret -ne 0 ]; then
65 diff -u $testroot/content.expected $testroot/content
66 test_done "$testroot" "$ret"
67 return 1
68 fi
70 if [ -e $testroot/wt/beta ]; then
71 echo "removed file beta still exists on disk" >&2
72 test_done "$testroot" "1"
73 return 1
74 fi
76 echo "new file on branch" > $testroot/content.expected
77 cat $testroot/wt/epsilon/new > $testroot/content
78 cmp -s $testroot/content.expected $testroot/content
79 ret=$?
80 if [ $ret -ne 0 ]; then
81 diff -u $testroot/content.expected $testroot/content
82 test_done "$testroot" "$ret"
83 return 1
84 fi
86 echo 'M alpha' > $testroot/stdout.expected
87 echo 'D beta' >> $testroot/stdout.expected
88 echo 'A epsilon/new' >> $testroot/stdout.expected
90 (cd $testroot/wt && got status > $testroot/stdout)
92 cmp -s $testroot/stdout.expected $testroot/stdout
93 ret=$?
94 if [ $ret -ne 0 ]; then
95 diff -u $testroot/stdout.expected $testroot/stdout
96 test_done "$testroot" "$ret"
97 return 1
98 fi
100 (cd $testroot/wt && got cherrypick $branch_rev2 > $testroot/stdout)
102 echo "G epsilon/new" > $testroot/stdout.expected
103 echo "Merged commit $branch_rev2" >> $testroot/stdout.expected
105 cmp -s $testroot/stdout.expected $testroot/stdout
106 ret=$?
107 if [ $ret -ne 0 ]; then
108 diff -u $testroot/stdout.expected $testroot/stdout
109 test_done "$testroot" "$ret"
110 return 1
111 fi
113 echo 'M alpha' > $testroot/stdout.expected
114 echo 'D beta' >> $testroot/stdout.expected
115 echo 'A epsilon/new' >> $testroot/stdout.expected
117 (cd $testroot/wt && got status > $testroot/stdout)
119 cmp -s $testroot/stdout.expected $testroot/stdout
120 ret=$?
121 if [ $ret -ne 0 ]; then
122 diff -u $testroot/stdout.expected $testroot/stdout
123 fi
124 test_done "$testroot" "$ret"
127 test_cherrypick_root_commit() {
128 local testroot=`test_init cherrypick_root_commit`
130 got checkout $testroot/repo $testroot/wt > /dev/null
131 ret=$?
132 if [ $ret -ne 0 ]; then
133 test_done "$testroot" "$ret"
134 return 1
135 fi
137 (cd $testroot/repo && git checkout -q -b newbranch)
138 (cd $testroot/repo && git rm -q alpha)
139 (cd $testroot/repo && git rm -q beta)
140 (cd $testroot/repo && git rm -q epsilon/zeta)
141 (cd $testroot/repo && git rm -q gamma/delta)
142 mkdir -p $testroot/repo/epsilon
143 echo "new file on branch" > $testroot/repo/epsilon/new
144 (cd $testroot/repo && git add epsilon/new)
145 git_commit $testroot/repo -m "committing on newbranch"
147 echo "modified new file on branch" >> $testroot/repo/epsilon/new
148 git_commit $testroot/repo -m "committing on newbranch again"
150 tree=`git_show_tree $testroot/repo`
151 root_commit=`git_commit_tree $testroot/repo "new root commit" $tree`
153 (cd $testroot/wt && got cherrypick $root_commit > $testroot/stdout)
155 echo "A epsilon/new" > $testroot/stdout.expected
156 echo "Merged commit $root_commit" >> $testroot/stdout.expected
158 cmp -s $testroot/stdout.expected $testroot/stdout
159 ret=$?
160 if [ $ret -ne 0 ]; then
161 diff -u $testroot/stdout.expected $testroot/stdout
162 test_done "$testroot" "$ret"
163 return 1
164 fi
166 echo "new file on branch" > $testroot/content.expected
167 echo "modified new file on branch" >> $testroot/content.expected
168 cat $testroot/wt/epsilon/new > $testroot/content
169 cmp -s $testroot/content.expected $testroot/content
170 ret=$?
171 if [ $ret -ne 0 ]; then
172 diff -u $testroot/content.expected $testroot/content
173 test_done "$testroot" "$ret"
174 return 1
175 fi
177 echo 'A epsilon/new' > $testroot/stdout.expected
179 (cd $testroot/wt && got status > $testroot/stdout)
181 cmp -s $testroot/stdout.expected $testroot/stdout
182 ret=$?
183 if [ $ret -ne 0 ]; then
184 diff -u $testroot/stdout.expected $testroot/stdout
185 fi
186 test_done "$testroot" "$ret"
189 test_cherrypick_into_work_tree_with_conflicts() {
190 local testroot=`test_init cherrypick_into_work_tree_with_conflicts`
192 got checkout $testroot/repo $testroot/wt > /dev/null
193 ret=$?
194 if [ $ret -ne 0 ]; then
195 test_done "$testroot" "$ret"
196 return 1
197 fi
199 (cd $testroot/repo && git checkout -q -b newbranch)
200 echo "modified delta on branch" > $testroot/repo/gamma/delta
201 git_commit $testroot/repo -m "committing to delta on newbranch"
203 echo "modified alpha on branch" > $testroot/repo/alpha
204 (cd $testroot/repo && git rm -q beta)
205 echo "new file on branch" > $testroot/repo/epsilon/new
206 (cd $testroot/repo && git add epsilon/new)
207 git_commit $testroot/repo -m "committing more changes on newbranch"
209 local branch_rev=`git_show_head $testroot/repo`
211 # fake a merge conflict
212 echo '<<<<<<<' > $testroot/wt/alpha
213 echo 'alpha' >> $testroot/wt/alpha
214 echo '=======' >> $testroot/wt/alpha
215 echo 'alpha, too' >> $testroot/wt/alpha
216 echo '>>>>>>>' >> $testroot/wt/alpha
217 cp $testroot/wt/alpha $testroot/content.expected
219 echo "C alpha" > $testroot/stdout.expected
220 (cd $testroot/wt && got status > $testroot/stdout)
221 cmp -s $testroot/stdout.expected $testroot/stdout
222 ret=$?
223 if [ $ret -ne 0 ]; then
224 diff -u $testroot/stdout.expected $testroot/stdout
225 test_done "$testroot" "$ret"
226 return 1
227 fi
229 (cd $testroot/wt && got cherrypick $branch_rev \
230 > $testroot/stdout 2> $testroot/stderr)
231 ret=$?
232 if [ $ret -eq 0 ]; then
233 echo "cherrypick succeeded unexpectedly" >&2
234 test_done "$testroot" "1"
235 return 1
236 fi
238 echo -n > $testroot/stdout.expected
239 echo -n "got: work tree contains conflicted files; " \
240 > $testroot/stderr.expected
241 echo "these conflicts must be resolved first" \
242 >> $testroot/stderr.expected
244 cmp -s $testroot/stdout.expected $testroot/stdout
245 ret=$?
246 if [ $ret -ne 0 ]; then
247 diff -u $testroot/stdout.expected $testroot/stdout
248 test_done "$testroot" "$ret"
249 return 1
250 fi
252 cmp -s $testroot/stderr.expected $testroot/stderr
253 ret=$?
254 if [ $ret -ne 0 ]; then
255 diff -u $testroot/stderr.expected $testroot/stderr
256 test_done "$testroot" "$ret"
257 return 1
258 fi
260 cmp -s $testroot/content.expected $testroot/wt/alpha
261 ret=$?
262 if [ $ret -ne 0 ]; then
263 diff -u $testroot/content.expected $testroot/wt/alpha
264 fi
265 test_done "$testroot" "$ret"
268 test_cherrypick_into_work_tree_with_mixed_commits() {
269 local testroot=`test_init cherrypick_into_work_tree_with_mixed_commits`
270 local first_rev=`git_show_head $testroot/repo`
272 echo "modified alpha" > $testroot/repo/alpha
273 git_commit $testroot/repo -m "committing to alpha"
274 local second_rev=`git_show_head $testroot/repo`
276 got checkout $testroot/repo $testroot/wt > /dev/null
277 ret=$?
278 if [ $ret -ne 0 ]; then
279 test_done "$testroot" "$ret"
280 return 1
281 fi
283 (cd $testroot/repo && git checkout -q -b newbranch)
284 echo "modified delta on branch" > $testroot/repo/gamma/delta
285 git_commit $testroot/repo -m "committing to delta on newbranch"
287 (cd $testroot/repo && git rm -q beta)
288 echo "new file on branch" > $testroot/repo/epsilon/new
289 (cd $testroot/repo && git add epsilon/new)
290 git_commit $testroot/repo -m "committing more changes on newbranch"
292 local branch_rev=`git_show_head $testroot/repo`
294 (cd $testroot/wt && got update -c $first_rev alpha >/dev/null)
296 (cd $testroot/wt && got cherrypick $branch_rev \
297 > $testroot/stdout 2> $testroot/stderr)
298 ret=$?
299 if [ $ret -eq 0 ]; then
300 echo "cherrypick succeeded unexpectedly" >&2
301 test_done "$testroot" "1"
302 return 1
303 fi
305 echo -n > $testroot/stdout.expected
306 echo -n "got: work tree contains files from multiple base commits; " \
307 > $testroot/stderr.expected
308 echo "the entire work tree must be updated first" \
309 >> $testroot/stderr.expected
311 cmp -s $testroot/stdout.expected $testroot/stdout
312 ret=$?
313 if [ $ret -ne 0 ]; then
314 diff -u $testroot/stdout.expected $testroot/stdout
315 test_done "$testroot" "$ret"
316 return 1
317 fi
319 cmp -s $testroot/stderr.expected $testroot/stderr
320 ret=$?
321 if [ $ret -ne 0 ]; then
322 diff -u $testroot/stderr.expected $testroot/stderr
323 fi
324 test_done "$testroot" "$ret"
328 test_cherrypick_modified_submodule() {
329 local testroot=`test_init cherrypick_modified_submodules`
331 make_single_file_repo $testroot/repo2 foo
333 (cd $testroot/repo && git submodule -q add ../repo2)
334 (cd $testroot/repo && git commit -q -m 'adding submodule')
336 got checkout $testroot/repo $testroot/wt > /dev/null
338 echo "modified foo" > $testroot/repo2/foo
339 (cd $testroot/repo2 && git commit -q -a -m 'modified a submodule')
341 (cd $testroot/repo && git checkout -q -b newbranch)
342 # Update the repo/repo2 submodule link on newbranch
343 (cd $testroot/repo && git -C repo2 pull -q)
344 (cd $testroot/repo && git add repo2)
345 git_commit $testroot/repo -m "modified submodule link"
346 local commit_id=`git_show_head $testroot/repo`
348 # This cherrypick is a no-op because Got's file index
349 # does not track submodules.
350 (cd $testroot/wt && got cherrypick $commit_id > $testroot/stdout)
352 echo -n > $testroot/stdout.expected
353 cmp -s $testroot/stdout.expected $testroot/stdout
354 ret=$?
355 if [ $ret -ne 0 ]; then
356 diff -u $testroot/stdout.expected $testroot/stdout
357 fi
358 test_done "$testroot" "$ret"
361 test_cherrypick_added_submodule() {
362 local testroot=`test_init cherrypick_added_submodules`
364 got checkout $testroot/repo $testroot/wt > /dev/null
366 make_single_file_repo $testroot/repo2 foo
368 # Add the repo/repo2 submodule on newbranch
369 (cd $testroot/repo && git checkout -q -b newbranch)
370 (cd $testroot/repo && git submodule -q add ../repo2)
371 (cd $testroot/repo && git commit -q -m 'adding submodule')
372 local commit_id=`git_show_head $testroot/repo`
374 (cd $testroot/wt && got cherrypick $commit_id > $testroot/stdout)
376 echo "A .gitmodules" > $testroot/stdout.expected
377 echo "Merged commit $commit_id" >> $testroot/stdout.expected
378 cmp -s $testroot/stdout.expected $testroot/stdout
379 ret=$?
380 if [ $ret -ne 0 ]; then
381 diff -u $testroot/stdout.expected $testroot/stdout
382 fi
383 test_done "$testroot" "$ret"
386 test_cherrypick_conflict_wt_file_vs_repo_submodule() {
387 local testroot=`test_init cherrypick_conflict_wt_file_vs_repo_submodule`
389 got checkout $testroot/repo $testroot/wt > /dev/null
391 # Add a file which will clash with the submodule
392 echo "This is a file called repo2" > $testroot/wt/repo2
393 (cd $testroot/wt && got add repo2 > /dev/null)
394 (cd $testroot/wt && got commit -m 'add file repo2' > /dev/null)
395 ret=$?
396 if [ $ret -ne 0 ]; then
397 echo "commit failed unexpectedly" >&2
398 test_done "$testroot" "1"
399 return 1
400 fi
402 make_single_file_repo $testroot/repo2 foo
404 # Add the repo/repo2 submodule on newbranch
405 (cd $testroot/repo && git checkout -q -b newbranch)
406 (cd $testroot/repo && git submodule -q add ../repo2)
407 (cd $testroot/repo && git commit -q -m 'adding submodule')
408 local commit_id=`git_show_head $testroot/repo`
410 # Modify the clashing file such that any modifications brought
411 # in by 'got cherrypick' would require a merge.
412 echo "This file was changed" > $testroot/wt/repo2
414 (cd $testroot/wt && got update >/dev/null)
415 (cd $testroot/wt && got cherrypick $commit_id > $testroot/stdout)
417 echo "A .gitmodules" > $testroot/stdout.expected
418 echo "Merged commit $commit_id" >> $testroot/stdout.expected
419 cmp -s $testroot/stdout.expected $testroot/stdout
420 ret=$?
421 if [ $ret -ne 0 ]; then
422 diff -u $testroot/stdout.expected $testroot/stdout
423 test_done "$testroot" "$ret"
424 return 1
425 fi
427 (cd $testroot/wt && got status > $testroot/stdout)
429 echo "A .gitmodules" > $testroot/stdout.expected
430 echo "M repo2" >> $testroot/stdout.expected
431 cmp -s $testroot/stdout.expected $testroot/stdout
432 ret=$?
433 if [ $ret -ne 0 ]; then
434 diff -u $testroot/stdout.expected $testroot/stdout
435 fi
436 test_done "$testroot" "$ret"
439 test_cherrypick_modified_symlinks() {
440 local testroot=`test_init cherrypick_modified_symlinks`
442 (cd $testroot/repo && ln -s alpha alpha.link)
443 (cd $testroot/repo && ln -s epsilon epsilon.link)
444 (cd $testroot/repo && ln -s /etc/passwd passwd.link)
445 (cd $testroot/repo && ln -s ../beta epsilon/beta.link)
446 (cd $testroot/repo && ln -s nonexistent nonexistent.link)
447 (cd $testroot/repo && git add .)
448 git_commit $testroot/repo -m "add symlinks"
449 local commit_id1=`git_show_head $testroot/repo`
451 got tree -r $testroot/repo -R -c $commit_id1 \
452 > $testroot/stdout
453 cat > $testroot/stdout.expected <<EOF
454 alpha
455 alpha.link@ -> alpha
456 beta
457 epsilon/
458 epsilon/beta.link@ -> ../beta
459 epsilon/zeta
460 epsilon.link@ -> epsilon
461 gamma/
462 gamma/delta
463 nonexistent.link@ -> nonexistent
464 passwd.link@ -> /etc/passwd
465 EOF
466 cmp -s $testroot/stdout.expected $testroot/stdout
467 ret=$?
468 if [ $ret -ne 0 ]; then
469 diff -u $testroot/stdout.expected $testroot/stdout
470 test_done "$testroot" "$ret"
471 return 1
472 fi
474 got branch -r $testroot/repo foo
476 got checkout -b foo $testroot/repo $testroot/wt > /dev/null
478 (cd $testroot/repo && ln -sf beta alpha.link)
479 (cd $testroot/repo && ln -sfh gamma epsilon.link)
480 (cd $testroot/repo && ln -sf ../gamma/delta epsilon/beta.link)
481 (cd $testroot/repo && ln -sf .got/foo $testroot/repo/dotgotfoo.link)
482 (cd $testroot/repo && git rm -q nonexistent.link)
483 (cd $testroot/repo && ln -sf epsilon/zeta zeta.link)
484 (cd $testroot/repo && git add .)
485 git_commit $testroot/repo -m "change symlinks"
486 local commit_id2=`git_show_head $testroot/repo`
488 (cd $testroot/wt && got cherrypick $commit_id2 > $testroot/stdout)
490 echo "G alpha.link" > $testroot/stdout.expected
491 echo "G epsilon/beta.link" >> $testroot/stdout.expected
492 echo "A dotgotfoo.link" >> $testroot/stdout.expected
493 echo "G epsilon.link" >> $testroot/stdout.expected
494 echo "D nonexistent.link" >> $testroot/stdout.expected
495 echo "A zeta.link" >> $testroot/stdout.expected
496 echo "Merged commit $commit_id2" >> $testroot/stdout.expected
497 cmp -s $testroot/stdout.expected $testroot/stdout
498 ret=$?
499 if [ $ret -ne 0 ]; then
500 diff -u $testroot/stdout.expected $testroot/stdout
501 test_done "$testroot" "$ret"
502 return 1
503 fi
505 if ! [ -h $testroot/wt/alpha.link ]; then
506 echo "alpha.link is not a symlink"
507 test_done "$testroot" "1"
508 return 1
509 fi
511 readlink $testroot/wt/alpha.link > $testroot/stdout
512 echo "beta" > $testroot/stdout.expected
513 cmp -s $testroot/stdout.expected $testroot/stdout
514 ret=$?
515 if [ $ret -ne 0 ]; then
516 diff -u $testroot/stdout.expected $testroot/stdout
517 test_done "$testroot" "$ret"
518 return 1
519 fi
521 if ! [ -h $testroot/wt/dotgotfoo.link ]; then
522 echo "dotgotfoo.link is not a symlink"
523 test_done "$testroot" "1"
524 return 1
525 fi
527 readlink $testroot/wt/dotgotfoo.link > $testroot/stdout
528 echo ".got/foo" > $testroot/stdout.expected
529 cmp -s $testroot/stdout.expected $testroot/stdout
530 ret=$?
531 if [ $ret -ne 0 ]; then
532 diff -u $testroot/stdout.expected $testroot/stdout
533 test_done "$testroot" "$ret"
534 return 1
535 fi
537 if ! [ -h $testroot/wt/epsilon.link ]; then
538 echo "epsilon.link is not a symlink"
539 test_done "$testroot" "1"
540 return 1
541 fi
543 readlink $testroot/wt/epsilon.link > $testroot/stdout
544 echo "gamma" > $testroot/stdout.expected
545 cmp -s $testroot/stdout.expected $testroot/stdout
546 ret=$?
547 if [ $ret -ne 0 ]; then
548 diff -u $testroot/stdout.expected $testroot/stdout
549 test_done "$testroot" "$ret"
550 return 1
551 fi
553 if [ -h $testroot/wt/passwd.link ]; then
554 echo -n "passwd.link symlink points outside of work tree: " >&2
555 readlink $testroot/wt/passwd.link >&2
556 test_done "$testroot" "1"
557 return 1
558 fi
560 echo -n "/etc/passwd" > $testroot/content.expected
561 cp $testroot/wt/passwd.link $testroot/content
563 cmp -s $testroot/content.expected $testroot/content
564 ret=$?
565 if [ $ret -ne 0 ]; then
566 diff -u $testroot/content.expected $testroot/content
567 test_done "$testroot" "$ret"
568 return 1
569 fi
571 readlink $testroot/wt/epsilon/beta.link > $testroot/stdout
572 echo "../gamma/delta" > $testroot/stdout.expected
573 cmp -s $testroot/stdout.expected $testroot/stdout
574 ret=$?
575 if [ $ret -ne 0 ]; then
576 diff -u $testroot/stdout.expected $testroot/stdout
577 test_done "$testroot" "$ret"
578 return 1
579 fi
581 if [ -h $testroot/wt/nonexistent.link ]; then
582 echo -n "nonexistent.link still exists on disk: " >&2
583 readlink $testroot/wt/nonexistent.link >&2
584 test_done "$testroot" "1"
585 return 1
586 fi
588 (cd $testroot/wt && got commit -m 'commit cherrypick result' \
589 > /dev/null 2>$testroot/stderr)
590 ret=$?
591 if [ $ret -eq 0 ]; then
592 echo "got commit succeeded unexpectedly" >&2
593 test_done "$testroot" "1"
594 return 1
595 fi
596 echo -n "got: $testroot/wt/dotgotfoo.link: symbolic link points " \
597 > $testroot/stderr.expected
598 echo "outside of paths under version control" \
599 >> $testroot/stderr.expected
600 cmp -s $testroot/stderr.expected $testroot/stderr
601 ret=$?
602 if [ $ret -ne 0 ]; then
603 diff -u $testroot/stderr.expected $testroot/stderr
604 test_done "$testroot" "$ret"
605 return 1
606 fi
608 (cd $testroot/wt && got commit -S -m 'commit cherrypick result' \
609 > /dev/null)
610 ret=$?
611 if [ $ret -ne 0 ]; then
612 echo "got commit failed unexpectedly" >&2
613 test_done "$testroot" "$ret"
614 return 1
615 fi
616 local commit_id2=`git_show_head $testroot/repo`
618 got tree -r $testroot/repo -R -c $commit_id2 \
619 > $testroot/stdout
620 cat > $testroot/stdout.expected <<EOF
621 alpha
622 alpha.link@ -> beta
623 beta
624 dotgotfoo.link@ -> .got/foo
625 epsilon/
626 epsilon/beta.link@ -> ../gamma/delta
627 epsilon/zeta
628 epsilon.link@ -> gamma
629 gamma/
630 gamma/delta
631 passwd.link@ -> /etc/passwd
632 zeta.link@ -> epsilon/zeta
633 EOF
634 cmp -s $testroot/stdout.expected $testroot/stdout
635 ret=$?
636 if [ $ret -ne 0 ]; then
637 diff -u $testroot/stdout.expected $testroot/stdout
638 fi
639 test_done "$testroot" "$ret"
642 test_cherrypick_symlink_conflicts() {
643 local testroot=`test_init cherrypick_symlink_conflicts`
645 (cd $testroot/repo && ln -s alpha alpha.link)
646 (cd $testroot/repo && ln -s epsilon epsilon.link)
647 (cd $testroot/repo && ln -s /etc/passwd passwd.link)
648 (cd $testroot/repo && ln -s ../beta epsilon/beta.link)
649 (cd $testroot/repo && ln -s nonexistent nonexistent.link)
650 (cd $testroot/repo && ln -sf epsilon/zeta zeta.link)
651 (cd $testroot/repo && git add .)
652 git_commit $testroot/repo -m "add symlinks"
653 local commit_id1=`git_show_head $testroot/repo`
655 (cd $testroot/repo && ln -sf beta alpha.link)
656 (cd $testroot/repo && ln -sf beta boo.link)
657 (cd $testroot/repo && ln -sfh gamma epsilon.link)
658 (cd $testroot/repo && ln -sf ../gamma/delta epsilon/beta.link)
659 echo 'this is regular file foo' > $testroot/repo/dotgotfoo.link
660 (cd $testroot/repo && ln -sf .got/bar dotgotbar.link)
661 (cd $testroot/repo && git rm -q nonexistent.link)
662 (cd $testroot/repo && ln -sf gamma/delta zeta.link)
663 (cd $testroot/repo && ln -sf alpha new.link)
664 (cd $testroot/repo && git add .)
665 git_commit $testroot/repo -m "change symlinks"
666 local commit_id2=`git_show_head $testroot/repo`
668 got branch -r $testroot/repo -c $commit_id1 foo
669 got checkout -b foo $testroot/repo $testroot/wt > /dev/null
671 # modified symlink to file A vs modified symlink to file B
672 (cd $testroot/wt && ln -sf gamma/delta alpha.link)
673 # modified symlink to dir A vs modified symlink to file B
674 (cd $testroot/wt && ln -sfh beta epsilon.link)
675 # modeified symlink to file A vs modified symlink to dir B
676 (cd $testroot/wt && ln -sfh ../gamma epsilon/beta.link)
677 # added regular file A vs added bad symlink to file A
678 (cd $testroot/wt && ln -sf .got/foo dotgotfoo.link)
679 (cd $testroot/wt && got add dotgotfoo.link > /dev/null)
680 # added bad symlink to file A vs added regular file A
681 echo 'this is regular file bar' > $testroot/wt/dotgotbar.link
682 (cd $testroot/wt && got add dotgotbar.link > /dev/null)
683 # added symlink to file A vs unversioned file A
684 echo 'this is unversioned file boo' > $testroot/wt/boo.link
685 # removed symlink to non-existent file A vs modified symlink
686 # to nonexistent file B
687 (cd $testroot/wt && ln -sf nonexistent2 nonexistent.link)
688 # modified symlink to file A vs removed symlink to file A
689 (cd $testroot/wt && got rm zeta.link > /dev/null)
690 # added symlink to file A vs added symlink to file B
691 (cd $testroot/wt && ln -sf beta new.link)
692 (cd $testroot/wt && got add new.link > /dev/null)
693 (cd $testroot/wt && got commit -S -m "change symlinks on foo" \
694 > /dev/null)
696 (cd $testroot/wt && got update >/dev/null)
697 (cd $testroot/wt && got cherrypick $commit_id2 > $testroot/stdout)
699 echo -n > $testroot/stdout.expected
700 echo "C alpha.link" >> $testroot/stdout.expected
701 echo "C epsilon/beta.link" >> $testroot/stdout.expected
702 echo "? boo.link" >> $testroot/stdout.expected
703 echo "C epsilon.link" >> $testroot/stdout.expected
704 echo "C dotgotbar.link" >> $testroot/stdout.expected
705 echo "C dotgotfoo.link" >> $testroot/stdout.expected
706 echo "D nonexistent.link" >> $testroot/stdout.expected
707 echo "! zeta.link" >> $testroot/stdout.expected
708 echo "C new.link" >> $testroot/stdout.expected
709 echo "Merged commit $commit_id2" >> $testroot/stdout.expected
710 echo "Files with new merge conflicts: 6" >> $testroot/stdout.expected
711 echo -n "Files which had incoming changes but could not be found " \
712 >> $testroot/stdout.expected
713 echo "in the work tree: 1" >> $testroot/stdout.expected
714 echo -n "Files not merged because an unversioned file was found in " \
715 >> $testroot/stdout.expected
716 echo "the work tree: 1" >> $testroot/stdout.expected
717 cmp -s $testroot/stdout.expected $testroot/stdout
718 ret=$?
719 if [ $ret -ne 0 ]; then
720 diff -u $testroot/stdout.expected $testroot/stdout
721 test_done "$testroot" "$ret"
722 return 1
723 fi
725 if [ -h $testroot/wt/alpha.link ]; then
726 echo "alpha.link is a symlink"
727 test_done "$testroot" "1"
728 return 1
729 fi
731 echo "<<<<<<< merged change: commit $commit_id2" \
732 > $testroot/content.expected
733 echo "beta" >> $testroot/content.expected
734 echo "3-way merge base: commit $commit_id1" \
735 >> $testroot/content.expected
736 echo "alpha" >> $testroot/content.expected
737 echo "=======" >> $testroot/content.expected
738 echo "gamma/delta" >> $testroot/content.expected
739 echo '>>>>>>>' >> $testroot/content.expected
740 echo -n "" >> $testroot/content.expected
742 cp $testroot/wt/alpha.link $testroot/content
743 cmp -s $testroot/content.expected $testroot/content
744 ret=$?
745 if [ $ret -ne 0 ]; then
746 diff -u $testroot/content.expected $testroot/content
747 test_done "$testroot" "$ret"
748 return 1
749 fi
751 if [ -h $testroot/wt/boo.link ]; then
752 echo "boo.link is a symlink"
753 test_done "$testroot" "1"
754 return 1
755 fi
757 echo "this is unversioned file boo" > $testroot/content.expected
758 cp $testroot/wt/boo.link $testroot/content
759 cmp -s $testroot/content.expected $testroot/content
760 ret=$?
761 if [ $ret -ne 0 ]; then
762 diff -u $testroot/content.expected $testroot/content
763 test_done "$testroot" "$ret"
764 return 1
765 fi
767 if [ -h $testroot/wt/epsilon.link ]; then
768 echo "epsilon.link is a symlink"
769 test_done "$testroot" "1"
770 return 1
771 fi
773 echo "<<<<<<< merged change: commit $commit_id2" \
774 > $testroot/content.expected
775 echo "gamma" >> $testroot/content.expected
776 echo "3-way merge base: commit $commit_id1" \
777 >> $testroot/content.expected
778 echo "epsilon" >> $testroot/content.expected
779 echo "=======" >> $testroot/content.expected
780 echo "beta" >> $testroot/content.expected
781 echo '>>>>>>>' >> $testroot/content.expected
782 echo -n "" >> $testroot/content.expected
784 cp $testroot/wt/epsilon.link $testroot/content
785 cmp -s $testroot/content.expected $testroot/content
786 ret=$?
787 if [ $ret -ne 0 ]; then
788 diff -u $testroot/content.expected $testroot/content
789 test_done "$testroot" "$ret"
790 return 1
791 fi
793 if [ -h $testroot/wt/passwd.link ]; then
794 echo -n "passwd.link symlink points outside of work tree: " >&2
795 readlink $testroot/wt/passwd.link >&2
796 test_done "$testroot" "1"
797 return 1
798 fi
800 echo -n "/etc/passwd" > $testroot/content.expected
801 cp $testroot/wt/passwd.link $testroot/content
803 cmp -s $testroot/content.expected $testroot/content
804 ret=$?
805 if [ $ret -ne 0 ]; then
806 diff -u $testroot/content.expected $testroot/content
807 test_done "$testroot" "$ret"
808 return 1
809 fi
811 if [ -h $testroot/wt/epsilon/beta.link ]; then
812 echo "epsilon/beta.link is a symlink"
813 test_done "$testroot" "1"
814 return 1
815 fi
817 echo "<<<<<<< merged change: commit $commit_id2" \
818 > $testroot/content.expected
819 echo "../gamma/delta" >> $testroot/content.expected
820 echo "3-way merge base: commit $commit_id1" \
821 >> $testroot/content.expected
822 echo "../beta" >> $testroot/content.expected
823 echo "=======" >> $testroot/content.expected
824 echo "../gamma" >> $testroot/content.expected
825 echo '>>>>>>>' >> $testroot/content.expected
826 echo -n "" >> $testroot/content.expected
828 cp $testroot/wt/epsilon/beta.link $testroot/content
829 cmp -s $testroot/content.expected $testroot/content
830 ret=$?
831 if [ $ret -ne 0 ]; then
832 diff -u $testroot/content.expected $testroot/content
833 test_done "$testroot" "$ret"
834 return 1
835 fi
837 if [ -h $testroot/wt/nonexistent.link ]; then
838 echo -n "nonexistent.link still exists on disk: " >&2
839 readlink $testroot/wt/nonexistent.link >&2
840 test_done "$testroot" "1"
841 return 1
842 fi
844 if [ -h $testroot/wt/dotgotfoo.link ]; then
845 echo "dotgotfoo.link is a symlink"
846 test_done "$testroot" "1"
847 return 1
848 fi
850 echo "<<<<<<< merged change: commit $commit_id2" \
851 > $testroot/content.expected
852 echo "this is regular file foo" >> $testroot/content.expected
853 echo "=======" >> $testroot/content.expected
854 echo -n ".got/foo" >> $testroot/content.expected
855 echo '>>>>>>>' >> $testroot/content.expected
856 cp $testroot/wt/dotgotfoo.link $testroot/content
857 cmp -s $testroot/content.expected $testroot/content
858 ret=$?
859 if [ $ret -ne 0 ]; then
860 diff -u $testroot/content.expected $testroot/content
861 test_done "$testroot" "$ret"
862 return 1
863 fi
865 if [ -h $testroot/wt/dotgotbar.link ]; then
866 echo "dotgotbar.link is a symlink"
867 test_done "$testroot" "1"
868 return 1
869 fi
870 echo "<<<<<<< merged change: commit $commit_id2" \
871 > $testroot/content.expected
872 echo -n ".got/bar" >> $testroot/content.expected
873 echo "=======" >> $testroot/content.expected
874 echo "this is regular file bar" >> $testroot/content.expected
875 echo '>>>>>>>' >> $testroot/content.expected
876 echo -n "" >> $testroot/content.expected
877 cp $testroot/wt/dotgotbar.link $testroot/content
878 cmp -s $testroot/content.expected $testroot/content
879 ret=$?
880 if [ $ret -ne 0 ]; then
881 diff -u $testroot/content.expected $testroot/content
882 test_done "$testroot" "$ret"
883 return 1
884 fi
886 if [ -h $testroot/wt/new.link ]; then
887 echo "new.link is a symlink"
888 test_done "$testroot" "1"
889 return 1
890 fi
892 echo "<<<<<<< merged change: commit $commit_id2" \
893 > $testroot/content.expected
894 echo "alpha" >> $testroot/content.expected
895 echo "=======" >> $testroot/content.expected
896 echo "beta" >> $testroot/content.expected
897 echo '>>>>>>>' >> $testroot/content.expected
898 echo -n "" >> $testroot/content.expected
900 cp $testroot/wt/new.link $testroot/content
901 cmp -s $testroot/content.expected $testroot/content
902 ret=$?
903 if [ $ret -ne 0 ]; then
904 diff -u $testroot/content.expected $testroot/content
905 test_done "$testroot" "$ret"
906 return 1
907 fi
909 echo "A dotgotfoo.link" > $testroot/stdout.expected
910 echo "M new.link" >> $testroot/stdout.expected
911 echo "D nonexistent.link" >> $testroot/stdout.expected
912 (cd $testroot/wt && got status > $testroot/stdout)
913 ret=$?
914 if [ $ret -ne 0 ]; then
915 diff -u $testroot/stdout.expected $testroot/stdout
916 test_done "$testroot" "$ret"
917 return 1
918 fi
920 test_done "$testroot" "0"
923 test_cherrypick_with_path_prefix_and_empty_tree() {
924 local testroot=`test_init cherrypick_with_path_prefix_and_empty_tree 1`
926 (cd $testroot/repo && git commit --allow-empty \
927 -m "initial empty commit" >/dev/null)
929 (cd $testroot/repo && got br bar >/dev/null)
931 mkdir -p $testroot/repo/epsilon
932 echo "file foo" > $testroot/repo/epsilon/foo
933 (cd $testroot/repo && git add .)
934 git_commit $testroot/repo -m "add file foo"
935 local commit_id=`git_show_head $testroot/repo`
937 got checkout -b bar $testroot/repo $testroot/wt > /dev/null
938 ret=$?
939 if [ $ret -ne 0 ]; then
940 echo "got checkout failed unexpectedly" >&2
941 test_done "$testroot" "$ret"
942 return 1
943 fi
945 mkdir -p $testroot/wt/epsilon
946 echo "new file" > $testroot/wt/epsilon/new
947 (cd $testroot/wt && got add epsilon/new >/dev/null)
948 (cd $testroot/wt && got commit -m "add file on branch bar" > /dev/null)
950 got checkout -b bar -p epsilon $testroot/repo $testroot/wt2 > /dev/null
951 ret=$?
952 if [ $ret -ne 0 ]; then
953 echo "got checkout failed unexpectedly" >&2
954 test_done "$testroot" "$ret"
955 return 1
956 fi
957 (cd $testroot/wt2 && got cherrypick $commit_id > $testroot/stdout)
959 echo "A foo" > $testroot/stdout.expected
960 echo "Merged commit $commit_id" >> $testroot/stdout.expected
962 cmp -s $testroot/stdout.expected $testroot/stdout
963 ret=$?
964 if [ $ret -ne 0 ]; then
965 diff -u $testroot/stdout.expected $testroot/stdout
966 fi
967 test_done "$testroot" "$ret"
970 test_cherrypick_conflict_no_eol() {
971 local testroot=`test_init cherrypick_conflict_no_eol 1`
972 local content_a="aaa\naaa\naaa\naaa\naaa\naaa\n"
973 local content_b="aaa\naaa\nbbb\naaa\naaa\naaa\naaa"
974 local content_c="aaa\naaa\nccc\naaa\naaa\naaa\naaa"
976 printf "$content_a" > $testroot/repo/a
977 (cd $testroot/repo && git add a)
978 git_commit $testroot/repo -m "initial commit"
980 (cd $testroot/repo && got branch newbranch)
982 printf "$content_b" > $testroot/repo/a
983 git_commit $testroot/repo -m "change bbb"
985 printf "$content_c" > $testroot/repo/a
986 git_commit $testroot/repo -m "change ccc"
987 local ccc_commit=`git_show_head $testroot/repo`
989 got checkout -b newbranch $testroot/repo $testroot/wt > /dev/null
990 ret=$?
991 if [ $ret -ne 0 ]; then
992 test_done "$testroot" "$ret"
993 return 1
994 fi
996 (cd $testroot/wt && got cherrypick $ccc_commit > $testroot/stdout)
998 echo "C a" > $testroot/stdout.expected
999 echo "Merged commit $ccc_commit" >> $testroot/stdout.expected
1000 echo "Files with new merge conflicts: 1" >> $testroot/stdout.expected
1002 cmp -s $testroot/stdout.expected $testroot/stdout
1003 ret=$?
1004 if [ $ret -ne 0 ]; then
1005 diff -u $testroot/stdout.expected $testroot/stdout
1007 test_done "$testroot" "$ret"
1010 test_cherrypick_conflict_no_eol2() {
1011 local testroot=`test_init cherrypick_conflict_no_eol2 1`
1012 local content_a="aaa\naaa\naaa\naaa\naaa\naaa"
1013 local content_b="aaa\naaa\nbbb\naaa\naaa\naaa"
1014 local content_c="aaa\naaa\nbbb\naaa\naaa\naaa\n"
1016 printf "$content_a" > $testroot/repo/a
1017 (cd $testroot/repo && git add a)
1018 git_commit $testroot/repo -m "initial commit"
1020 (cd $testroot/repo && got branch newbranch)
1022 printf "$content_b" > $testroot/repo/a
1023 git_commit $testroot/repo -m "change bbb"
1025 printf "$content_c" > $testroot/repo/a
1026 git_commit $testroot/repo -m "change ccc"
1027 local ccc_commit=`git_show_head $testroot/repo`
1029 got checkout -b newbranch $testroot/repo $testroot/wt > /dev/null
1030 ret=$?
1031 if [ $ret -ne 0 ]; then
1032 test_done "$testroot" "$ret"
1033 return 1
1036 (cd $testroot/wt && got cherrypick $ccc_commit \
1037 > $testroot/stdout 2> $testroot/stderr)
1039 echo "C a" > $testroot/stdout.expected
1040 echo "Merged commit $ccc_commit" >> $testroot/stdout.expected
1041 echo "Files with new merge conflicts: 1" >> $testroot/stdout.expected
1043 cmp -s $testroot/stdout.expected $testroot/stdout
1044 ret=$?
1045 if [ $ret -ne 0 ]; then
1046 diff -u $testroot/stdout.expected $testroot/stdout
1048 test_done "$testroot" "$ret"
1051 test_cherrypick_unrelated_changes() {
1052 local testroot=`test_init cherrypick_unrelated_changes`
1054 # Sorry about the large HERE document but I have not found
1055 # a smaller reproduction recipe yet...
1056 cat > $testroot/repo/reference.c <<EOF
1057 const struct got_error *
1058 got_ref_alloc(struct got_reference **ref, const char *name,
1059 struct got_object_id *id)
1061 if (!is_valid_ref_name(name))
1062 return got_error_path(name, GOT_ERR_BAD_REF_NAME);
1064 return alloc_ref(ref, name, id, 0);
1067 static const struct got_error *
1068 parse_packed_ref_line(struct got_reference **ref, const char *abs_refname,
1069 const char *line)
1071 struct got_object_id id;
1072 const char *name;
1074 *ref = NULL;
1076 if (line[0] == '#' || line[0] == '^')
1077 return NULL;
1079 if (!got_parse_sha1_digest(id.sha1, line))
1080 return got_error(GOT_ERR_BAD_REF_DATA);
1082 if (abs_refname) {
1083 if (strcmp(line + SHA1_DIGEST_STRING_LENGTH, abs_refname) != 0)
1084 return NULL;
1085 name = abs_refname;
1086 } else
1087 name = line + SHA1_DIGEST_STRING_LENGTH;
1089 return alloc_ref(ref, name, &id, GOT_REF_IS_PACKED);
1092 static const struct got_error *
1093 open_packed_ref(struct got_reference **ref, FILE *f, const char **subdirs,
1094 int nsubdirs, const char *refname)
1096 const struct got_error *err = NULL;
1097 char *abs_refname;
1098 char *line = NULL;
1099 size_t linesize = 0;
1100 ssize_t linelen;
1101 int i, ref_is_absolute = (strncmp(refname, "refs/", 5) == 0);
1103 *ref = NULL;
1105 if (ref_is_absolute)
1106 abs_refname = (char *)refname;
1107 do {
1108 linelen = getline(&line, &linesize, f);
1109 if (linelen == -1) {
1110 if (feof(f))
1111 break;
1112 err = got_ferror(f, GOT_ERR_BAD_REF_DATA);
1113 break;
1115 if (linelen > 0 && line[linelen - 1] == '\n')
1116 line[linelen - 1] = '\0';
1117 for (i = 0; i < nsubdirs; i++) {
1118 if (!ref_is_absolute &&
1119 asprintf(&abs_refname, "refs/%s/%s", subdirs[i],
1120 refname) == -1)
1121 return got_error_from_errno("asprintf");
1122 err = parse_packed_ref_line(ref, abs_refname, line);
1123 if (!ref_is_absolute)
1124 free(abs_refname);
1125 if (err || *ref != NULL)
1126 break;
1128 if (err)
1129 break;
1130 } while (*ref == NULL);
1131 free(line);
1133 return err;
1136 static const struct got_error *
1137 open_ref(struct got_reference **ref, const char *path_refs, const char *subdir,
1138 const char *name, int lock)
1140 const struct got_error *err = NULL;
1141 char *path = NULL;
1142 char *absname = NULL;
1143 int ref_is_absolute = (strncmp(name, "refs/", 5) == 0);
1144 int ref_is_well_known = (subdir[0] == '\0' && is_well_known_ref(name));
1146 *ref = NULL;
1148 if (ref_is_absolute || ref_is_well_known) {
1149 if (asprintf(&path, "%s/%s", path_refs, name) == -1)
1150 return got_error_from_errno("asprintf");
1151 absname = (char *)name;
1152 } else {
1153 if (asprintf(&path, "%s/%s%s%s", path_refs, subdir,
1154 subdir[0] ? "/" : "", name) == -1)
1155 return got_error_from_errno("asprintf");
1157 if (asprintf(&absname, "refs/%s%s%s",
1158 subdir, subdir[0] ? "/" : "", name) == -1) {
1159 err = got_error_from_errno("asprintf");
1160 goto done;
1164 err = parse_ref_file(ref, name, absname, path, lock);
1165 done:
1166 if (!ref_is_absolute && !ref_is_well_known)
1167 free(absname);
1168 free(path);
1169 return err;
1172 const struct got_error *
1173 got_ref_open(struct got_reference **ref, struct got_repository *repo,
1174 const char *refname, int lock)
1176 const struct got_error *err = NULL;
1177 char *path_refs = NULL;
1178 const char *subdirs[] = {
1179 GOT_REF_HEADS, GOT_REF_TAGS, GOT_REF_REMOTES
1181 size_t i;
1182 int well_known = is_well_known_ref(refname);
1183 struct got_lockfile *lf = NULL;
1185 *ref = NULL;
1187 path_refs = get_refs_dir_path(repo, refname);
1188 if (path_refs == NULL) {
1189 err = got_error_from_errno2("get_refs_dir_path", refname);
1190 goto done;
1193 if (well_known) {
1194 err = open_ref(ref, path_refs, "", refname, lock);
1195 } else {
1196 char *packed_refs_path;
1197 FILE *f;
1199 /* Search on-disk refs before packed refs! */
1200 for (i = 0; i < nitems(subdirs); i++) {
1201 err = open_ref(ref, path_refs, subdirs[i], refname,
1202 lock);
1203 if ((err && err->code != GOT_ERR_NOT_REF) || *ref)
1204 goto done;
1207 packed_refs_path = got_repo_get_path_packed_refs(repo);
1208 if (packed_refs_path == NULL) {
1209 err = got_error_from_errno(
1210 "got_repo_get_path_packed_refs");
1211 goto done;
1214 if (lock) {
1215 err = got_lockfile_lock(&lf, packed_refs_path);
1216 if (err)
1217 goto done;
1219 f = fopen(packed_refs_path, "rb");
1220 free(packed_refs_path);
1221 if (f != NULL) {
1222 err = open_packed_ref(ref, f, subdirs, nitems(subdirs),
1223 refname);
1224 if (!err) {
1225 if (fclose(f) == EOF) {
1226 err = got_error_from_errno("fclose");
1227 got_ref_close(*ref);
1228 *ref = NULL;
1229 } else if (*ref)
1230 (*ref)->lf = lf;
1234 done:
1235 if (!err && *ref == NULL)
1236 err = got_error_not_ref(refname);
1237 if (err && lf)
1238 got_lockfile_unlock(lf);
1239 free(path_refs);
1240 return err;
1243 struct got_reference *
1244 got_ref_dup(struct got_reference *ref)
1246 struct got_reference *ret;
1248 ret = calloc(1, sizeof(*ret));
1249 if (ret == NULL)
1250 return NULL;
1252 ret->flags = ref->flags;
1253 if (ref->flags & GOT_REF_IS_SYMBOLIC) {
1254 ret->ref.symref.name = strdup(ref->ref.symref.name);
1255 if (ret->ref.symref.name == NULL) {
1256 free(ret);
1257 return NULL;
1259 ret->ref.symref.ref = strdup(ref->ref.symref.ref);
1260 if (ret->ref.symref.ref == NULL) {
1261 free(ret->ref.symref.name);
1262 free(ret);
1263 return NULL;
1265 } else {
1266 ret->ref.ref.name = strdup(ref->ref.ref.name);
1267 if (ret->ref.ref.name == NULL) {
1268 free(ret);
1269 return NULL;
1271 memcpy(ret->ref.ref.sha1, ref->ref.ref.sha1,
1272 sizeof(ret->ref.ref.sha1));
1275 return ret;
1278 const struct got_error *
1279 got_reflist_entry_dup(struct got_reflist_entry **newp,
1280 struct got_reflist_entry *re)
1282 const struct got_error *err = NULL;
1283 struct got_reflist_entry *new;
1285 *newp = NULL;
1287 new = malloc(sizeof(*new));
1288 if (new == NULL)
1289 return got_error_from_errno("malloc");
1291 new->ref = got_ref_dup(re->ref);
1292 if (new->ref == NULL) {
1293 err = got_error_from_errno("got_ref_dup");
1294 free(new);
1295 return err;
1298 *newp = new;
1299 return NULL;
1302 void
1303 got_ref_list_free(struct got_reflist_head *refs)
1305 struct got_reflist_entry *re;
1307 while ((re = TAILQ_FIRST(refs))) {
1308 TAILQ_REMOVE(refs, re, entry);
1309 free(re);
1313 EOF
1314 (cd $testroot/repo && git add reference.c)
1315 git_commit $testroot/repo -m "added reference.c file"
1316 local base_commit=`git_show_head $testroot/repo`
1318 got checkout $testroot/repo $testroot/wt > /dev/null
1319 ret=$?
1320 if [ $ret -ne 0 ]; then
1321 test_done "$testroot" "$ret"
1322 return 1
1325 (cd $testroot/repo && git checkout -q -b newbranch)
1326 ed -s $testroot/repo/reference.c <<EOF
1327 91a
1328 if (!is_valid_ref_name(name))
1329 return got_error_path(name, GOT_ERR_BAD_REF_NAME);
1334 EOF
1335 git_commit $testroot/repo -m "added lines on newbranch"
1336 local branch_rev1=`git_show_head $testroot/repo`
1338 ed -s $testroot/repo/reference.c <<EOF
1339 255a
1340 got_ref_close(re->ref);
1344 EOF
1345 git_commit $testroot/repo -m "more lines on newbranch"
1347 local branch_rev2=`git_show_head $testroot/repo`
1349 (cd $testroot/wt && got cherrypick $branch_rev2 > $testroot/stdout)
1351 echo "G reference.c" > $testroot/stdout.expected
1352 echo "Merged commit $branch_rev2" >> $testroot/stdout.expected
1354 cmp -s $testroot/stdout.expected $testroot/stdout
1355 ret=$?
1356 if [ $ret -ne 0 ]; then
1357 diff -u $testroot/stdout.expected $testroot/stdout
1358 test_done "$testroot" "$ret"
1359 return 1
1362 cat > $testroot/diff.expected <<EOF
1363 --- reference.c
1364 +++ reference.c
1365 @@ -250,6 +250,7 @@ got_ref_list_free(struct got_reflist_head *refs)
1367 while ((re = TAILQ_FIRST(refs))) {
1368 TAILQ_REMOVE(refs, re, entry);
1369 + got_ref_close(re->ref);
1370 free(re);
1373 EOF
1374 (cd $testroot/wt && got diff |
1375 egrep -v '^(diff|blob|file|commit|path)' > $testroot/diff)
1376 cmp -s $testroot/diff.expected $testroot/diff
1377 ret=$?
1378 if [ $ret -ne 0 ]; then
1379 diff -u $testroot/diff.expected $testroot/diff
1382 test_done "$testroot" "$ret"
1385 test_cherrypick_same_branch() {
1386 local testroot=`test_init cherrypick_same_branch`
1388 got checkout $testroot/repo $testroot/wt > /dev/null
1389 ret=$?
1390 if [ $ret -ne 0 ]; then
1391 test_done "$testroot" "$ret"
1392 return 1
1395 (cd $testroot/repo && git checkout -q -b newbranch)
1396 echo "modified delta on branch" > $testroot/repo/gamma/delta
1397 git_commit $testroot/repo -m "committing to delta on newbranch"
1399 echo "modified alpha on branch" > $testroot/repo/alpha
1400 (cd $testroot/repo && git rm -q beta)
1401 echo "new file on branch" > $testroot/repo/epsilon/new
1402 (cd $testroot/repo && git add epsilon/new)
1403 git_commit $testroot/repo -m "committing more changes on newbranch"
1405 local branch_rev=`git_show_head $testroot/repo`
1407 # picking a commit from the branch's own history does not make
1408 # sense but we should have test coverage for this case regardless
1409 (cd $testroot/wt && got up -b newbranch > /dev/null)
1410 (cd $testroot/wt && got cherrypick $branch_rev > $testroot/stdout)
1412 echo "G alpha" > $testroot/stdout.expected
1413 echo "! beta" >> $testroot/stdout.expected
1414 echo "G epsilon/new" >> $testroot/stdout.expected
1415 echo "Merged commit $branch_rev" >> $testroot/stdout.expected
1416 echo -n "Files which had incoming changes but could not be found " \
1417 >> $testroot/stdout.expected
1418 echo "in the work tree: 1" >> $testroot/stdout.expected
1420 cmp -s $testroot/stdout.expected $testroot/stdout
1421 ret=$?
1422 if [ $ret -ne 0 ]; then
1423 diff -u $testroot/stdout.expected $testroot/stdout
1425 test_done "$testroot" "$ret"
1428 test_cherrypick_dot_on_a_line_by_itself() {
1429 local testroot=`test_init cherrypick_dot_on_a_line_by_itself`
1431 got checkout $testroot/repo $testroot/wt > /dev/null
1432 ret=$?
1433 if [ $ret -ne 0 ]; then
1434 test_done "$testroot" "$ret"
1435 return 1
1438 (cd $testroot/repo && git checkout -q -b newbranch)
1439 printf "modified\n:delta\n.\non\n:branch\n" > $testroot/repo/gamma/delta
1440 git_commit $testroot/repo -m "committing to delta on newbranch"
1441 local branch_rev=`git_show_head $testroot/repo`
1443 (cd $testroot/wt && got cherrypick newbranch > $testroot/stdout)
1445 echo "G gamma/delta" >> $testroot/stdout.expected
1446 echo "Merged commit $branch_rev" >> $testroot/stdout.expected
1448 cmp -s $testroot/stdout.expected $testroot/stdout
1449 ret=$?
1450 if [ $ret -ne 0 ]; then
1451 diff -u $testroot/stdout.expected $testroot/stdout
1452 test_done "$testroot" "$ret"
1453 return 1
1456 printf "modified\n:delta\n.\non\n:branch\n" > $testroot/content.expected
1457 cat $testroot/wt/gamma/delta > $testroot/content
1458 cmp -s $testroot/content.expected $testroot/content
1459 ret=$?
1460 if [ $ret -ne 0 ]; then
1461 diff -u $testroot/content.expected $testroot/content
1463 test_done "$testroot" "$ret"
1466 test_cherrypick_binary_file() {
1467 local testroot=`test_init cherrypick_binary_file`
1468 local commit_id0=`git_show_head $testroot/repo`
1470 got checkout $testroot/repo $testroot/wt > /dev/null
1471 ret=$?
1472 if [ $ret -ne 0 ]; then
1473 test_done "$testroot" "$ret"
1474 return 1
1477 cp /bin/ls $testroot/wt/foo
1478 chmod 755 $testroot/wt/foo
1479 (cd $testroot/wt && got add foo >/dev/null)
1480 (cd $testroot/wt && got commit -m 'add binary file' > /dev/null)
1481 local commit_id1=`git_show_head $testroot/repo`
1483 cp /bin/cat $testroot/wt/foo
1484 chmod 755 $testroot/wt/foo
1485 (cd $testroot/wt && got commit -m 'change binary file' > /dev/null)
1486 local commit_id2=`git_show_head $testroot/repo`
1488 cp /bin/cp $testroot/wt/foo
1489 chmod 755 $testroot/wt/foo
1490 (cd $testroot/wt && got commit -m 'change binary file' > /dev/null)
1491 local commit_id3=`git_show_head $testroot/repo`
1493 (cd $testroot/wt && got rm foo >/dev/null)
1494 (cd $testroot/wt && got commit -m 'remove binary file' > /dev/null)
1495 local commit_id4=`git_show_head $testroot/repo`
1497 # backdate the work tree to make it usable for cherry-picking
1498 (cd $testroot/wt && got up -c $commit_id0 > /dev/null)
1500 # cherry-pick addition of a binary file
1501 (cd $testroot/wt && got cy $commit_id1 > $testroot/stdout)
1503 echo "A foo" > $testroot/stdout.expected
1504 echo "Merged commit $commit_id1" >> $testroot/stdout.expected
1506 cmp -s $testroot/stdout.expected $testroot/stdout
1507 ret=$?
1508 if [ $ret -ne 0 ]; then
1509 diff -u $testroot/stdout.expected $testroot/stdout
1510 test_done "$testroot" "$ret"
1511 return 1
1514 cp /bin/ls $testroot/content.expected
1515 chmod 755 $testroot/content.expected
1516 cp $testroot/wt/foo $testroot/content
1517 cmp -s $testroot/content.expected $testroot/content
1518 ret=$?
1519 if [ $ret -ne 0 ]; then
1520 diff -u $testroot/content.expected $testroot/content
1521 test_done "$testroot" "$ret"
1522 return 1
1525 # cherry-pick modification of a binary file
1526 (cd $testroot/wt && got cy $commit_id2 > $testroot/stdout)
1528 echo "G foo" > $testroot/stdout.expected
1529 echo "Merged commit $commit_id2" >> $testroot/stdout.expected
1531 cmp -s $testroot/stdout.expected $testroot/stdout
1532 ret=$?
1533 if [ $ret -ne 0 ]; then
1534 diff -u $testroot/stdout.expected $testroot/stdout
1535 test_done "$testroot" "$ret"
1536 return 1
1539 cp /bin/cat $testroot/content.expected
1540 chmod 755 $testroot/content.expected
1541 cp $testroot/wt/foo $testroot/content
1542 cmp -s $testroot/content.expected $testroot/content
1543 ret=$?
1544 if [ $ret -ne 0 ]; then
1545 diff -u $testroot/content.expected $testroot/content
1546 test_done "$testroot" "$ret"
1547 return 1
1550 # cherry-pick conflicting addition of a binary file
1551 (cd $testroot/wt && got cy $commit_id1 > $testroot/stdout)
1553 echo "C foo" > $testroot/stdout.expected
1554 echo "Merged commit $commit_id1" >> $testroot/stdout.expected
1555 echo "Files with new merge conflicts: 1" >> $testroot/stdout.expected
1557 cmp -s $testroot/stdout.expected $testroot/stdout
1558 ret=$?
1559 if [ $ret -ne 0 ]; then
1560 diff -u $testroot/stdout.expected $testroot/stdout
1561 test_done "$testroot" "$ret"
1562 return 1
1565 echo "Binary files differ and cannot be merged automatically:" \
1566 > $testroot/content.expected
1567 echo "<<<<<<< merged change: commit $commit_id1" \
1568 >> $testroot/content.expected
1569 echo -n "file " >> $testroot/content.expected
1570 ls $testroot/wt/foo-1-* >> $testroot/content.expected
1571 echo '=======' >> $testroot/content.expected
1572 echo -n "file " >> $testroot/content.expected
1573 ls $testroot/wt/foo-2-* >> $testroot/content.expected
1574 echo '>>>>>>>' >> $testroot/content.expected
1575 cp $testroot/wt/foo $testroot/content
1576 cmp -s $testroot/content.expected $testroot/content
1577 ret=$?
1578 if [ $ret -ne 0 ]; then
1579 diff -u $testroot/content.expected $testroot/content
1580 test_done "$testroot" "$ret"
1581 return 1
1584 # revert local changes to allow further testing
1585 (cd $testroot/wt && got revert -R . >/dev/null)
1587 (cd $testroot/wt && got status > $testroot/stdout)
1588 echo '? foo' > $testroot/stdout.expected
1589 echo -n '? ' >> $testroot/stdout.expected
1590 (cd $testroot/wt && ls foo-1-* >> $testroot/stdout.expected)
1591 echo -n '? ' >> $testroot/stdout.expected
1592 (cd $testroot/wt && ls foo-2-* >> $testroot/stdout.expected)
1593 cmp -s $testroot/stdout.expected $testroot/stdout
1594 ret=$?
1595 if [ $ret -ne 0 ]; then
1596 diff -u $testroot/stdout.expected $testroot/stdout
1597 test_done "$testroot" "$ret"
1598 return 1
1601 # tidy up
1602 rm $testroot/wt/foo $testroot/wt/foo-1-* $testroot/wt/foo-2-*
1603 (cd $testroot/wt && got up -c $commit_id1 > /dev/null)
1605 # cherry-pick conflicting modification of a binary file
1606 (cd $testroot/wt && got cy $commit_id3 > $testroot/stdout)
1608 echo "C foo" > $testroot/stdout.expected
1609 echo "Merged commit $commit_id3" >> $testroot/stdout.expected
1610 echo "Files with new merge conflicts: 1" >> $testroot/stdout.expected
1612 cmp -s $testroot/stdout.expected $testroot/stdout
1613 ret=$?
1614 if [ $ret -ne 0 ]; then
1615 diff -u $testroot/stdout.expected $testroot/stdout
1616 test_done "$testroot" "$ret"
1617 return 1
1620 echo "Binary files differ and cannot be merged automatically:" \
1621 > $testroot/content.expected
1622 echo '<<<<<<<' >> $testroot/content.expected
1623 echo -n "file " >> $testroot/content.expected
1624 ls $testroot/wt/foo-1-* >> $testroot/content.expected
1625 echo "||||||| 3-way merge base: commit $commit_id2" \
1626 >> $testroot/content.expected
1627 echo -n "file " >> $testroot/content.expected
1628 ls $testroot/wt/foo-orig-* >> $testroot/content.expected
1629 echo '=======' >> $testroot/content.expected
1630 echo -n "file " >> $testroot/content.expected
1631 ls $testroot/wt/foo-2-* >> $testroot/content.expected
1632 echo ">>>>>>> merged change: commit $commit_id3" \
1633 >> $testroot/content.expected
1634 cp $testroot/wt/foo $testroot/content
1635 cmp -s $testroot/content.expected $testroot/content
1636 ret=$?
1637 if [ $ret -ne 0 ]; then
1638 diff -u $testroot/content.expected $testroot/content
1639 test_done "$testroot" "$ret"
1640 return 1
1643 cp /bin/ls $testroot/content.expected
1644 chmod 755 $testroot/content.expected
1645 cat $testroot/wt/foo-1-* > $testroot/content
1646 cmp -s $testroot/content.expected $testroot/content
1647 ret=$?
1648 if [ $ret -ne 0 ]; then
1649 diff -u $testroot/content.expected $testroot/content
1650 test_done "$testroot" "$ret"
1651 return 1
1654 cp /bin/cp $testroot/content.expected
1655 chmod 755 $testroot/content.expected
1656 cat $testroot/wt/foo-2-* > $testroot/content
1657 cmp -s $testroot/content.expected $testroot/content
1658 ret=$?
1659 if [ $ret -ne 0 ]; then
1660 diff -u $testroot/content.expected $testroot/content
1661 test_done "$testroot" "$ret"
1662 return 1
1665 # revert local changes to allow further testing
1666 (cd $testroot/wt && got revert -R . > /dev/null)
1667 rm $testroot/wt/foo-1-*
1668 rm $testroot/wt/foo-2-*
1669 (cd $testroot/wt && got up -c $commit_id3 > /dev/null)
1671 # cherry-pick deletion of a binary file
1672 (cd $testroot/wt && got cy $commit_id4 > $testroot/stdout)
1674 echo "D foo" > $testroot/stdout.expected
1675 echo "Merged commit $commit_id4" >> $testroot/stdout.expected
1677 cmp -s $testroot/stdout.expected $testroot/stdout
1678 ret=$?
1679 if [ $ret -ne 0 ]; then
1680 diff -u $testroot/stdout.expected $testroot/stdout
1681 test_done "$testroot" "$ret"
1682 return 1
1685 if [ -e $testroot/wt/foo ]; then
1686 echo "removed file foo still exists on disk" >&2
1687 test_done "$testroot" "1"
1688 return 1
1690 test_done "$testroot" "0"
1693 test_parseargs "$@"
1694 run_test test_cherrypick_basic
1695 run_test test_cherrypick_root_commit
1696 run_test test_cherrypick_into_work_tree_with_conflicts
1697 run_test test_cherrypick_into_work_tree_with_mixed_commits
1698 run_test test_cherrypick_modified_submodule
1699 run_test test_cherrypick_added_submodule
1700 run_test test_cherrypick_conflict_wt_file_vs_repo_submodule
1701 run_test test_cherrypick_modified_symlinks
1702 run_test test_cherrypick_symlink_conflicts
1703 run_test test_cherrypick_with_path_prefix_and_empty_tree
1704 run_test test_cherrypick_conflict_no_eol
1705 run_test test_cherrypick_conflict_no_eol2
1706 run_test test_cherrypick_unrelated_changes
1707 run_test test_cherrypick_same_branch
1708 run_test test_cherrypick_dot_on_a_line_by_itself
1709 run_test test_cherrypick_binary_file