summaryrefslogtreecommitdiffstats
path: root/t/t2402-worktree-list.sh
blob: 33ea9cb21ba07c9563530b54da06753eaa993fe2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#!/bin/sh

test_description='test git worktree list'

GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME

TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh

test_expect_success 'setup' '
	test_commit init
'

test_expect_success 'rev-parse --git-common-dir on main worktree' '
	git rev-parse --git-common-dir >actual &&
	echo .git >expected &&
	test_cmp expected actual &&
	mkdir sub &&
	git -C sub rev-parse --git-common-dir >actual2 &&
	echo ../.git >expected2 &&
	test_cmp expected2 actual2
'

test_expect_success 'rev-parse --git-path objects linked worktree' '
	echo "$(git rev-parse --show-toplevel)/.git/objects" >expect &&
	test_when_finished "rm -rf linked-tree actual expect && git worktree prune" &&
	git worktree add --detach linked-tree main &&
	git -C linked-tree rev-parse --git-path objects >actual &&
	test_cmp expect actual
'

test_expect_success '"list" all worktrees from main' '
	echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
	test_when_finished "rm -rf here out actual expect && git worktree prune" &&
	git worktree add --detach here main &&
	echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
	git worktree list >out &&
	sed "s/  */ /g" <out >actual &&
	test_cmp expect actual
'

test_expect_success '"list" all worktrees from linked' '
	echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
	test_when_finished "rm -rf here out actual expect && git worktree prune" &&
	git worktree add --detach here main &&
	echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
	git -C here worktree list >out &&
	sed "s/  */ /g" <out >actual &&
	test_cmp expect actual
'

test_expect_success '"list" all worktrees --porcelain' '
	echo "worktree $(git rev-parse --show-toplevel)" >expect &&
	echo "HEAD $(git rev-parse HEAD)" >>expect &&
	echo "branch $(git symbolic-ref HEAD)" >>expect &&
	echo >>expect &&
	test_when_finished "rm -rf here actual expect && git worktree prune" &&
	git worktree add --detach here main &&
	echo "worktree $(git -C here rev-parse --show-toplevel)" >>expect &&
	echo "HEAD $(git rev-parse HEAD)" >>expect &&
	echo "detached" >>expect &&
	echo >>expect &&
	git worktree list --porcelain >actual &&
	test_cmp expect actual
'

test_expect_success '"list" all worktrees --porcelain -z' '
	test_when_finished "rm -rf here _actual actual expect &&
				git worktree prune" &&
	printf "worktree %sQHEAD %sQbranch %sQQ" \
		"$(git rev-parse --show-toplevel)" \
		$(git rev-parse HEAD --symbolic-full-name HEAD) >expect &&
	git worktree add --detach here main &&
	printf "worktree %sQHEAD %sQdetachedQQ" \
		"$(git -C here rev-parse --show-toplevel)" \
		"$(git rev-parse HEAD)" >>expect &&
	git worktree list --porcelain -z >_actual &&
	nul_to_q <_actual >actual &&
	test_cmp expect actual
'

test_expect_success '"list" -z fails without --porcelain' '
	test_must_fail git worktree list -z
'

test_expect_success '"list" all worktrees with locked annotation' '
	test_when_finished "rm -rf locked unlocked out && git worktree prune" &&
	git worktree add --detach locked main &&
	git worktree add --detach unlocked main &&
	git worktree lock locked &&
	test_when_finished "git worktree unlock locked" &&
	git worktree list >out &&
	grep "/locked  *[0-9a-f].* locked$" out &&
	! grep "/unlocked  *[0-9a-f].* locked$" out
'

test_expect_success '"list" all worktrees --porcelain with locked' '
	test_when_finished "rm -rf locked1 locked2 unlocked out actual expect && git worktree prune" &&
	echo "locked" >expect &&
	echo "locked with reason" >>expect &&
	git worktree add --detach locked1 &&
	git worktree add --detach locked2 &&
	# unlocked worktree should not be annotated with "locked"
	git worktree add --detach unlocked &&
	git worktree lock locked1 &&
	test_when_finished "git worktree unlock locked1" &&
	git worktree lock locked2 --reason "with reason" &&
	test_when_finished "git worktree unlock locked2" &&
	git worktree list --porcelain >out &&
	grep "^locked" out >actual &&
	test_cmp expect actual
'

test_expect_success '"list" all worktrees --porcelain with locked reason newline escaped' '
	test_when_finished "rm -rf locked_lf locked_crlf out actual expect && git worktree prune" &&
	printf "locked \"locked\\\\r\\\\nreason\"\n" >expect &&
	printf "locked \"locked\\\\nreason\"\n" >>expect &&
	git worktree add --detach locked_lf &&
	git worktree add --detach locked_crlf &&
	git worktree lock locked_lf --reason "$(printf "locked\nreason")" &&
	test_when_finished "git worktree unlock locked_lf" &&
	git worktree lock locked_crlf --reason "$(printf "locked\r\nreason")" &&
	test_when_finished "git worktree unlock locked_crlf" &&
	git worktree list --porcelain >out &&
	grep "^locked" out >actual &&
	test_cmp expect actual
'

test_expect_success '"list" all worktrees with prunable annotation' '
	test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
	git worktree add --detach prunable &&
	git worktree add --detach unprunable &&
	rm -rf prunable &&
	git worktree list >out &&
	grep "/prunable  *[0-9a-f].* prunable$" out &&
	! grep "/unprunable  *[0-9a-f].* prunable$"
'

test_expect_success '"list" all worktrees --porcelain with prunable' '
	test_when_finished "rm -rf prunable out && git worktree prune" &&
	git worktree add --detach prunable &&
	rm -rf prunable &&
	git worktree list --porcelain >out &&
	sed -n "/^worktree .*\/prunable$/,/^$/p" <out >only_prunable &&
	test_grep "^prunable gitdir file points to non-existent location$" only_prunable
'

test_expect_success '"list" all worktrees with prunable consistent with "prune"' '
	test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
	git worktree add --detach prunable &&
	git worktree add --detach unprunable &&
	rm -rf prunable &&
	git worktree list >out &&
	grep "/prunable  *[0-9a-f].* prunable$" out &&
	! grep "/unprunable  *[0-9a-f].* unprunable$" out &&
	git worktree prune --verbose 2>out &&
	test_grep "^Removing worktrees/prunable" out &&
	test_grep ! "^Removing worktrees/unprunable" out
'

test_expect_success '"list" --verbose and --porcelain mutually exclusive' '
	test_must_fail git worktree list --verbose --porcelain
'

test_expect_success '"list" all worktrees --verbose with locked' '
	test_when_finished "rm -rf locked1 locked2 out actual expect && git worktree prune" &&
	git worktree add locked1 --detach &&
	git worktree add locked2 --detach &&
	git worktree lock locked1 &&
	test_when_finished "git worktree unlock locked1" &&
	git worktree lock locked2 --reason "with reason" &&
	test_when_finished "git worktree unlock locked2" &&
	echo "$(git -C locked2 rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >expect &&
	printf "\tlocked: with reason\n" >>expect &&
	git worktree list --verbose >out &&
	grep "/locked1  *[0-9a-f].* locked$" out &&
	sed -n "s/  */ /g;/\/locked2  *[0-9a-f].*$/,/locked: .*$/p" <out >actual &&
	test_cmp actual expect
'

test_expect_success '"list" all worktrees --verbose with prunable' '
	test_when_finished "rm -rf prunable out actual expect && git worktree prune" &&
	git worktree add prunable --detach &&
	echo "$(git -C prunable rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >expect &&
	printf "\tprunable: gitdir file points to non-existent location\n" >>expect &&
	rm -rf prunable &&
	git worktree list --verbose >out &&
	sed -n "s/  */ /g;/\/prunable  *[0-9a-f].*$/,/prunable: .*$/p" <out >actual &&
	test_cmp actual expect
'

test_expect_success 'bare repo setup' '
	git init --bare bare1 &&
	echo "data" >file1 &&
	git add file1 &&
	git commit -m"File1: add data" &&
	git push bare1 main &&
	git reset --hard HEAD^
'

test_expect_success '"list" all worktrees from bare main' '
	test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
	git -C bare1 worktree add --detach ../there main &&
	echo "$(pwd)/bare1 (bare)" >expect &&
	echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
	git -C bare1 worktree list >out &&
	sed "s/  */ /g" <out >actual &&
	test_cmp expect actual
'

test_expect_success '"list" all worktrees --porcelain from bare main' '
	test_when_finished "rm -rf there actual expect && git -C bare1 worktree prune" &&
	git -C bare1 worktree add --detach ../there main &&
	echo "worktree $(pwd)/bare1" >expect &&
	echo "bare" >>expect &&
	echo >>expect &&
	echo "worktree $(git -C there rev-parse --show-toplevel)" >>expect &&
	echo "HEAD $(git -C there rev-parse HEAD)" >>expect &&
	echo "detached" >>expect &&
	echo >>expect &&
	git -C bare1 worktree list --porcelain >actual &&
	test_cmp expect actual
'

test_expect_success '"list" all worktrees from linked with a bare main' '
	test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
	git -C bare1 worktree add --detach ../there main &&
	echo "$(pwd)/bare1 (bare)" >expect &&
	echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
	git -C there worktree list >out &&
	sed "s/  */ /g" <out >actual &&
	test_cmp expect actual
'

test_expect_success 'bare repo cleanup' '
	rm -rf bare1
'

test_expect_success 'broken main worktree still at the top' '
	git init broken-main &&
	(
		cd broken-main &&
		test_commit new &&
		git worktree add linked &&
		cat >expected <<-EOF &&
		worktree $(pwd)
		HEAD $ZERO_OID

		EOF
		cd linked &&
		echo "worktree $(pwd)" >expected &&
		(cd ../ && test-tool ref-store main create-symref HEAD .broken ) &&
		git worktree list --porcelain >out &&
		head -n 3 out >actual &&
		test_cmp ../expected actual &&
		git worktree list >out &&
		head -n 1 out >actual.2 &&
		grep -F "(error)" actual.2
	)
'

test_expect_success 'linked worktrees are sorted' '
	mkdir sorted &&
	git init sorted/main &&
	(
		cd sorted/main &&
		test_tick &&
		test_commit new &&
		git worktree add ../first &&
		git worktree add ../second &&
		git worktree list --porcelain >out &&
		grep ^worktree out >actual
	) &&
	cat >expected <<-EOF &&
	worktree $(pwd)/sorted/main
	worktree $(pwd)/sorted/first
	worktree $(pwd)/sorted/second
	EOF
	test_cmp expected sorted/main/actual
'

test_expect_success 'worktree path when called in .git directory' '
	git worktree list >list1 &&
	git -C .git worktree list >list2 &&
	test_cmp list1 list2
'

test_done