1#!/bin/sh
2
3test_description='test git worktree add'
4
5GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7
8. ./test-lib.sh
9
10. "$TEST_DIRECTORY"/lib-rebase.sh
11
12test_expect_success 'setup' '
13	test_commit init
14'
15
16test_expect_success '"add" an existing worktree' '
17	mkdir -p existing/subtree &&
18	test_must_fail git worktree add --detach existing main
19'
20
21test_expect_success '"add" an existing empty worktree' '
22	mkdir existing_empty &&
23	git worktree add --detach existing_empty main
24'
25
26test_expect_success '"add" using shorthand - fails when no previous branch' '
27	test_must_fail git worktree add existing_short -
28'
29
30test_expect_success '"add" using - shorthand' '
31	git checkout -b newbranch &&
32	echo hello >myworld &&
33	git add myworld &&
34	git commit -m myworld &&
35	git checkout main &&
36	git worktree add short-hand - &&
37	echo refs/heads/newbranch >expect &&
38	git -C short-hand rev-parse --symbolic-full-name HEAD >actual &&
39	test_cmp expect actual
40'
41
42test_expect_success '"add" refuses to checkout locked branch' '
43	test_must_fail git worktree add zere main &&
44	! test -d zere &&
45	! test -d .git/worktrees/zere
46'
47
48test_expect_success 'checking out paths not complaining about linked checkouts' '
49	(
50	cd existing_empty &&
51	echo dirty >>init.t &&
52	git checkout main -- init.t
53	)
54'
55
56test_expect_success '"add" worktree' '
57	git rev-parse HEAD >expect &&
58	git worktree add --detach here main &&
59	(
60		cd here &&
61		test_cmp ../init.t init.t &&
62		test_must_fail git symbolic-ref HEAD &&
63		git rev-parse HEAD >actual &&
64		test_cmp ../expect actual &&
65		git fsck
66	)
67'
68
69test_expect_success '"add" worktree with lock' '
70	git worktree add --detach --lock here-with-lock main &&
71	test_when_finished "git worktree unlock here-with-lock || :" &&
72	test -f .git/worktrees/here-with-lock/locked
73'
74
75test_expect_success '"add" worktree with lock and reason' '
76	lock_reason="why not" &&
77	git worktree add --detach --lock --reason "$lock_reason" here-with-lock-reason main &&
78	test_when_finished "git worktree unlock here-with-lock-reason || :" &&
79	test -f .git/worktrees/here-with-lock-reason/locked &&
80	echo "$lock_reason" >expect &&
81	test_cmp expect .git/worktrees/here-with-lock-reason/locked
82'
83
84test_expect_success '"add" worktree with reason but no lock' '
85	test_must_fail git worktree add --detach --reason "why not" here-with-reason-only main &&
86	test_path_is_missing .git/worktrees/here-with-reason-only/locked
87'
88
89test_expect_success '"add" worktree from a subdir' '
90	(
91		mkdir sub &&
92		cd sub &&
93		git worktree add --detach here main &&
94		cd here &&
95		test_cmp ../../init.t init.t
96	)
97'
98
99test_expect_success '"add" from a linked checkout' '
100	(
101		cd here &&
102		git worktree add --detach nested-here main &&
103		cd nested-here &&
104		git fsck
105	)
106'
107
108test_expect_success '"add" worktree creating new branch' '
109	git worktree add -b newmain there main &&
110	(
111		cd there &&
112		test_cmp ../init.t init.t &&
113		git symbolic-ref HEAD >actual &&
114		echo refs/heads/newmain >expect &&
115		test_cmp expect actual &&
116		git fsck
117	)
118'
119
120test_expect_success 'die the same branch is already checked out' '
121	(
122		cd here &&
123		test_must_fail git checkout newmain
124	)
125'
126
127test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '
128	head=$(git -C there rev-parse --git-path HEAD) &&
129	ref=$(git -C there symbolic-ref HEAD) &&
130	rm "$head" &&
131	ln -s "$ref" "$head" &&
132	test_must_fail git -C here checkout newmain
133'
134
135test_expect_success 'not die the same branch is already checked out' '
136	(
137		cd here &&
138		git worktree add --force anothernewmain newmain
139	)
140'
141
142test_expect_success 'not die on re-checking out current branch' '
143	(
144		cd there &&
145		git checkout newmain
146	)
147'
148
149test_expect_success '"add" from a bare repo' '
150	(
151		git clone --bare . bare &&
152		cd bare &&
153		git worktree add -b bare-main ../there2 main
154	)
155'
156
157test_expect_success 'checkout from a bare repo without "add"' '
158	(
159		cd bare &&
160		test_must_fail git checkout main
161	)
162'
163
164test_expect_success '"add" default branch of a bare repo' '
165	(
166		git clone --bare . bare2 &&
167		cd bare2 &&
168		git worktree add ../there3 main
169	)
170'
171
172test_expect_success 'checkout with grafts' '
173	test_when_finished rm .git/info/grafts &&
174	test_commit abc &&
175	SHA1=$(git rev-parse HEAD) &&
176	test_commit def &&
177	test_commit xyz &&
178	echo "$(git rev-parse HEAD) $SHA1" >.git/info/grafts &&
179	cat >expected <<-\EOF &&
180	xyz
181	abc
182	EOF
183	git log --format=%s -2 >actual &&
184	test_cmp expected actual &&
185	git worktree add --detach grafted main &&
186	git --git-dir=grafted/.git log --format=%s -2 >actual &&
187	test_cmp expected actual
188'
189
190test_expect_success '"add" from relative HEAD' '
191	test_commit a &&
192	test_commit b &&
193	test_commit c &&
194	git rev-parse HEAD~1 >expected &&
195	git worktree add relhead HEAD~1 &&
196	git -C relhead rev-parse HEAD >actual &&
197	test_cmp expected actual
198'
199
200test_expect_success '"add -b" with <branch> omitted' '
201	git worktree add -b burble flornk &&
202	test_cmp_rev HEAD burble
203'
204
205test_expect_success '"add --detach" with <branch> omitted' '
206	git worktree add --detach fishhook &&
207	git rev-parse HEAD >expected &&
208	git -C fishhook rev-parse HEAD >actual &&
209	test_cmp expected actual &&
210	test_must_fail git -C fishhook symbolic-ref HEAD
211'
212
213test_expect_success '"add" with <branch> omitted' '
214	git worktree add wiffle/bat &&
215	test_cmp_rev HEAD bat
216'
217
218test_expect_success '"add" checks out existing branch of dwimd name' '
219	git branch dwim HEAD~1 &&
220	git worktree add dwim &&
221	test_cmp_rev HEAD~1 dwim &&
222	(
223		cd dwim &&
224		test_cmp_rev HEAD dwim
225	)
226'
227
228test_expect_success '"add <path>" dwim fails with checked out branch' '
229	git checkout -b test-branch &&
230	test_must_fail git worktree add test-branch &&
231	test_path_is_missing test-branch
232'
233
234test_expect_success '"add --force" with existing dwimd name doesnt die' '
235	git checkout test-branch &&
236	git worktree add --force test-branch
237'
238
239test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' '
240	git worktree add --detach mish/mash &&
241	test_must_fail git rev-parse mash -- &&
242	test_must_fail git -C mish/mash symbolic-ref HEAD
243'
244
245test_expect_success '"add" -b/-B mutually exclusive' '
246	test_must_fail git worktree add -b poodle -B poodle bamboo main
247'
248
249test_expect_success '"add" -b/--detach mutually exclusive' '
250	test_must_fail git worktree add -b poodle --detach bamboo main
251'
252
253test_expect_success '"add" -B/--detach mutually exclusive' '
254	test_must_fail git worktree add -B poodle --detach bamboo main
255'
256
257test_expect_success '"add -B" fails if the branch is checked out' '
258	git rev-parse newmain >before &&
259	test_must_fail git worktree add -B newmain bamboo main &&
260	git rev-parse newmain >after &&
261	test_cmp before after
262'
263
264test_expect_success 'add -B' '
265	git worktree add -B poodle bamboo2 main^ &&
266	git -C bamboo2 symbolic-ref HEAD >actual &&
267	echo refs/heads/poodle >expected &&
268	test_cmp expected actual &&
269	test_cmp_rev main^ poodle
270'
271
272test_expect_success 'add --quiet' '
273	git worktree add --quiet another-worktree main 2>actual &&
274	test_must_be_empty actual
275'
276
277test_expect_success 'local clone from linked checkout' '
278	git clone --local here here-clone &&
279	( cd here-clone && git fsck )
280'
281
282test_expect_success 'local clone --shared from linked checkout' '
283	git -C bare worktree add --detach ../baretree &&
284	git clone --local --shared baretree bare-clone &&
285	grep /bare/ bare-clone/.git/objects/info/alternates
286'
287
288test_expect_success '"add" worktree with --no-checkout' '
289	git worktree add --no-checkout -b swamp swamp &&
290	! test -e swamp/init.t &&
291	git -C swamp reset --hard &&
292	test_cmp init.t swamp/init.t
293'
294
295test_expect_success '"add" worktree with --checkout' '
296	git worktree add --checkout -b swmap2 swamp2 &&
297	test_cmp init.t swamp2/init.t
298'
299
300test_expect_success 'put a worktree under rebase' '
301	git worktree add under-rebase &&
302	(
303		cd under-rebase &&
304		set_fake_editor &&
305		FAKE_LINES="edit 1" git rebase -i HEAD^ &&
306		git worktree list | grep "under-rebase.*detached HEAD"
307	)
308'
309
310test_expect_success 'add a worktree, checking out a rebased branch' '
311	test_must_fail git worktree add new-rebase under-rebase &&
312	! test -d new-rebase
313'
314
315test_expect_success 'checking out a rebased branch from another worktree' '
316	git worktree add new-place &&
317	test_must_fail git -C new-place checkout under-rebase
318'
319
320test_expect_success 'not allow to delete a branch under rebase' '
321	(
322		cd under-rebase &&
323		test_must_fail git branch -D under-rebase
324	)
325'
326
327test_expect_success 'rename a branch under rebase not allowed' '
328	test_must_fail git branch -M under-rebase rebase-with-new-name
329'
330
331test_expect_success 'check out from current worktree branch ok' '
332	(
333		cd under-rebase &&
334		git checkout under-rebase &&
335		git checkout - &&
336		git rebase --abort
337	)
338'
339
340test_expect_success 'checkout a branch under bisect' '
341	git worktree add under-bisect &&
342	(
343		cd under-bisect &&
344		git bisect start &&
345		git bisect bad &&
346		git bisect good HEAD~2 &&
347		git worktree list | grep "under-bisect.*detached HEAD" &&
348		test_must_fail git worktree add new-bisect under-bisect &&
349		! test -d new-bisect
350	)
351'
352
353test_expect_success 'rename a branch under bisect not allowed' '
354	test_must_fail git branch -M under-bisect bisect-with-new-name
355'
356# Is branch "refs/heads/$1" set to pull from "$2/$3"?
357test_branch_upstream () {
358	printf "%s\n" "$2" "refs/heads/$3" >expect.upstream &&
359	{
360		git config "branch.$1.remote" &&
361		git config "branch.$1.merge"
362	} >actual.upstream &&
363	test_cmp expect.upstream actual.upstream
364}
365
366test_expect_success '--track sets up tracking' '
367	test_when_finished rm -rf track &&
368	git worktree add --track -b track track main &&
369	test_branch_upstream track . main
370'
371
372# setup remote repository $1 and repository $2 with $1 set up as
373# remote.  The remote has two branches, main and foo.
374setup_remote_repo () {
375	git init $1 &&
376	(
377		cd $1 &&
378		test_commit $1_main &&
379		git checkout -b foo &&
380		test_commit upstream_foo
381	) &&
382	git init $2 &&
383	(
384		cd $2 &&
385		test_commit $2_main &&
386		git remote add $1 ../$1 &&
387		git config remote.$1.fetch \
388			"refs/heads/*:refs/remotes/$1/*" &&
389		git fetch --all
390	)
391}
392
393test_expect_success '--no-track avoids setting up tracking' '
394	test_when_finished rm -rf repo_upstream repo_local foo &&
395	setup_remote_repo repo_upstream repo_local &&
396	(
397		cd repo_local &&
398		git worktree add --no-track -b foo ../foo repo_upstream/foo
399	) &&
400	(
401		cd foo &&
402		test_must_fail git config "branch.foo.remote" &&
403		test_must_fail git config "branch.foo.merge" &&
404		test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
405	)
406'
407
408test_expect_success '"add" <path> <non-existent-branch> fails' '
409	test_must_fail git worktree add foo non-existent
410'
411
412test_expect_success '"add" <path> <branch> dwims' '
413	test_when_finished rm -rf repo_upstream repo_dwim foo &&
414	setup_remote_repo repo_upstream repo_dwim &&
415	git init repo_dwim &&
416	(
417		cd repo_dwim &&
418		git worktree add ../foo foo
419	) &&
420	(
421		cd foo &&
422		test_branch_upstream foo repo_upstream foo &&
423		test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
424	)
425'
426
427test_expect_success '"add" <path> <branch> dwims with checkout.defaultRemote' '
428	test_when_finished rm -rf repo_upstream repo_dwim foo &&
429	setup_remote_repo repo_upstream repo_dwim &&
430	git init repo_dwim &&
431	(
432		cd repo_dwim &&
433		git remote add repo_upstream2 ../repo_upstream &&
434		git fetch repo_upstream2 &&
435		test_must_fail git worktree add ../foo foo &&
436		git -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&
437		git status -uno --porcelain >status.actual &&
438		test_must_be_empty status.actual
439	) &&
440	(
441		cd foo &&
442		test_branch_upstream foo repo_upstream foo &&
443		test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
444	)
445'
446
447test_expect_success 'git worktree add does not match remote' '
448	test_when_finished rm -rf repo_a repo_b foo &&
449	setup_remote_repo repo_a repo_b &&
450	(
451		cd repo_b &&
452		git worktree add ../foo
453	) &&
454	(
455		cd foo &&
456		test_must_fail git config "branch.foo.remote" &&
457		test_must_fail git config "branch.foo.merge" &&
458		test_cmp_rev ! refs/remotes/repo_a/foo refs/heads/foo
459	)
460'
461
462test_expect_success 'git worktree add --guess-remote sets up tracking' '
463	test_when_finished rm -rf repo_a repo_b foo &&
464	setup_remote_repo repo_a repo_b &&
465	(
466		cd repo_b &&
467		git worktree add --guess-remote ../foo
468	) &&
469	(
470		cd foo &&
471		test_branch_upstream foo repo_a foo &&
472		test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
473	)
474'
475
476test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
477	test_when_finished rm -rf repo_a repo_b foo &&
478	setup_remote_repo repo_a repo_b &&
479	(
480		cd repo_b &&
481		git config worktree.guessRemote true &&
482		git worktree add ../foo
483	) &&
484	(
485		cd foo &&
486		test_branch_upstream foo repo_a foo &&
487		test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
488	)
489'
490
491test_expect_success 'git worktree --no-guess-remote option overrides config' '
492	test_when_finished rm -rf repo_a repo_b foo &&
493	setup_remote_repo repo_a repo_b &&
494	(
495		cd repo_b &&
496		git config worktree.guessRemote true &&
497		git worktree add --no-guess-remote ../foo
498	) &&
499	(
500		cd foo &&
501		test_must_fail git config "branch.foo.remote" &&
502		test_must_fail git config "branch.foo.merge" &&
503		test_cmp_rev ! refs/remotes/repo_a/foo refs/heads/foo
504	)
505'
506
507post_checkout_hook () {
508	gitdir=${1:-.git}
509	test_when_finished "rm -f $gitdir/hooks/post-checkout" &&
510	mkdir -p $gitdir/hooks &&
511	write_script $gitdir/hooks/post-checkout <<-\EOF
512	{
513		echo $*
514		git rev-parse --git-dir --show-toplevel
515	} >hook.actual
516	EOF
517}
518
519test_expect_success '"add" invokes post-checkout hook (branch)' '
520	post_checkout_hook &&
521	{
522		echo $ZERO_OID $(git rev-parse HEAD) 1 &&
523		echo $(pwd)/.git/worktrees/gumby &&
524		echo $(pwd)/gumby
525	} >hook.expect &&
526	git worktree add gumby &&
527	test_cmp hook.expect gumby/hook.actual
528'
529
530test_expect_success '"add" invokes post-checkout hook (detached)' '
531	post_checkout_hook &&
532	{
533		echo $ZERO_OID $(git rev-parse HEAD) 1 &&
534		echo $(pwd)/.git/worktrees/grumpy &&
535		echo $(pwd)/grumpy
536	} >hook.expect &&
537	git worktree add --detach grumpy &&
538	test_cmp hook.expect grumpy/hook.actual
539'
540
541test_expect_success '"add --no-checkout" suppresses post-checkout hook' '
542	post_checkout_hook &&
543	rm -f hook.actual &&
544	git worktree add --no-checkout gloopy &&
545	test_path_is_missing gloopy/hook.actual
546'
547
548test_expect_success '"add" in other worktree invokes post-checkout hook' '
549	post_checkout_hook &&
550	{
551		echo $ZERO_OID $(git rev-parse HEAD) 1 &&
552		echo $(pwd)/.git/worktrees/guppy &&
553		echo $(pwd)/guppy
554	} >hook.expect &&
555	git -C gloopy worktree add --detach ../guppy &&
556	test_cmp hook.expect guppy/hook.actual
557'
558
559test_expect_success '"add" in bare repo invokes post-checkout hook' '
560	rm -rf bare &&
561	git clone --bare . bare &&
562	{
563		echo $ZERO_OID $(git --git-dir=bare rev-parse HEAD) 1 &&
564		echo $(pwd)/bare/worktrees/goozy &&
565		echo $(pwd)/goozy
566	} >hook.expect &&
567	post_checkout_hook bare &&
568	git -C bare worktree add --detach ../goozy &&
569	test_cmp hook.expect goozy/hook.actual
570'
571
572test_expect_success '"add" an existing but missing worktree' '
573	git worktree add --detach pneu &&
574	test_must_fail git worktree add --detach pneu &&
575	rm -fr pneu &&
576	test_must_fail git worktree add --detach pneu &&
577	git worktree add --force --detach pneu
578'
579
580test_expect_success '"add" an existing locked but missing worktree' '
581	git worktree add --detach gnoo &&
582	git worktree lock gnoo &&
583	test_when_finished "git worktree unlock gnoo || :" &&
584	rm -fr gnoo &&
585	test_must_fail git worktree add --detach gnoo &&
586	test_must_fail git worktree add --force --detach gnoo &&
587	git worktree add --force --force --detach gnoo
588'
589
590test_expect_success '"add" not tripped up by magic worktree matching"' '
591	# if worktree "sub1/bar" exists, "git worktree add bar" in distinct
592	# directory `sub2` should not mistakenly complain that `bar` is an
593	# already-registered worktree
594	mkdir sub1 sub2 &&
595	git -C sub1 --git-dir=../.git worktree add --detach bozo &&
596	git -C sub2 --git-dir=../.git worktree add --detach bozo
597'
598
599test_expect_success FUNNYNAMES 'sanitize generated worktree name' '
600	git worktree add --detach ".  weird*..?.lock.lock" &&
601	test -d .git/worktrees/---weird-.-
602'
603
604test_expect_success '"add" should not fail because of another bad worktree' '
605	git init add-fail &&
606	(
607		cd add-fail &&
608		test_commit first &&
609		mkdir sub &&
610		git worktree add sub/to-be-deleted &&
611		rm -rf sub &&
612		git worktree add second
613	)
614'
615
616test_expect_success '"add" with uninitialized submodule, with submodule.recurse unset' '
617	test_create_repo submodule &&
618	test_commit -C submodule first &&
619	test_create_repo project &&
620	git -C project submodule add ../submodule &&
621	git -C project add submodule &&
622	test_tick &&
623	git -C project commit -m add_sub &&
624	git clone project project-clone &&
625	git -C project-clone worktree add ../project-2
626'
627test_expect_success '"add" with uninitialized submodule, with submodule.recurse set' '
628	git -C project-clone -c submodule.recurse worktree add ../project-3
629'
630
631test_expect_success '"add" with initialized submodule, with submodule.recurse unset' '
632	git -C project-clone submodule update --init &&
633	git -C project-clone worktree add ../project-4
634'
635
636test_expect_success '"add" with initialized submodule, with submodule.recurse set' '
637	git -C project-clone -c submodule.recurse worktree add ../project-5
638'
639
640test_done
641