]> git.proxmox.com Git - libgit2.git/blob - tests/worktree/worktree.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / tests / worktree / worktree.c
1 #include "clar_libgit2.h"
2 #include "worktree_helpers.h"
3 #include "submodule/submodule_helpers.h"
4
5 #include "checkout.h"
6 #include "repository.h"
7 #include "worktree.h"
8
9 #define COMMON_REPO "testrepo"
10 #define WORKTREE_REPO "testrepo-worktree"
11
12 static worktree_fixture fixture =
13 WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
14
15 void test_worktree_worktree__initialize(void)
16 {
17 setup_fixture_worktree(&fixture);
18 }
19
20 void test_worktree_worktree__cleanup(void)
21 {
22 cleanup_fixture_worktree(&fixture);
23 }
24
25 void test_worktree_worktree__list(void)
26 {
27 git_strarray wts;
28
29 cl_git_pass(git_worktree_list(&wts, fixture.repo));
30 cl_assert_equal_i(wts.count, 1);
31 cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
32
33 git_strarray_dispose(&wts);
34 }
35
36 void test_worktree_worktree__list_with_invalid_worktree_dirs(void)
37 {
38 const char *filesets[3][2] = {
39 { "gitdir", "commondir" },
40 { "gitdir", "HEAD" },
41 { "HEAD", "commondir" },
42 };
43 git_buf path = GIT_BUF_INIT;
44 git_strarray wts;
45 size_t i, j, len;
46
47 cl_git_pass(git_buf_printf(&path, "%s/worktrees/invalid",
48 fixture.repo->commondir));
49 cl_git_pass(p_mkdir(path.ptr, 0755));
50
51 len = path.size;
52
53 for (i = 0; i < ARRAY_SIZE(filesets); i++) {
54
55 for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) {
56 git_buf_truncate(&path, len);
57 cl_git_pass(git_buf_joinpath(&path, path.ptr, filesets[i][j]));
58 cl_git_pass(p_close(p_creat(path.ptr, 0644)));
59 }
60
61 cl_git_pass(git_worktree_list(&wts, fixture.worktree));
62 cl_assert_equal_i(wts.count, 1);
63 cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
64 git_strarray_dispose(&wts);
65
66 for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) {
67 git_buf_truncate(&path, len);
68 cl_git_pass(git_buf_joinpath(&path, path.ptr, filesets[i][j]));
69 p_unlink(path.ptr);
70 }
71 }
72
73 git_buf_dispose(&path);
74 }
75
76 void test_worktree_worktree__list_in_worktree_repo(void)
77 {
78 git_strarray wts;
79
80 cl_git_pass(git_worktree_list(&wts, fixture.worktree));
81 cl_assert_equal_i(wts.count, 1);
82 cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
83
84 git_strarray_dispose(&wts);
85 }
86
87 void test_worktree_worktree__list_without_worktrees(void)
88 {
89 git_repository *repo;
90 git_strarray wts;
91
92 repo = cl_git_sandbox_init("testrepo2");
93 cl_git_pass(git_worktree_list(&wts, repo));
94 cl_assert_equal_i(wts.count, 0);
95
96 git_repository_free(repo);
97 }
98
99 void test_worktree_worktree__lookup(void)
100 {
101 git_worktree *wt;
102 git_buf gitdir_path = GIT_BUF_INIT;
103
104 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
105
106 cl_git_pass(git_buf_joinpath(&gitdir_path, fixture.repo->commondir, "worktrees/testrepo-worktree/"));
107
108 cl_assert_equal_s(wt->gitdir_path, gitdir_path.ptr);
109 cl_assert_equal_s(wt->parent_path, fixture.repo->workdir);
110 cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink);
111 cl_assert_equal_s(wt->commondir_path, fixture.repo->gitdir);
112 cl_assert_equal_s(wt->commondir_path, fixture.repo->commondir);
113
114 git_buf_dispose(&gitdir_path);
115 git_worktree_free(wt);
116 }
117
118 void test_worktree_worktree__lookup_nonexistent_worktree(void)
119 {
120 git_worktree *wt;
121
122 cl_git_fail(git_worktree_lookup(&wt, fixture.repo, "nonexistent"));
123 cl_assert_equal_p(wt, NULL);
124 }
125
126 void test_worktree_worktree__open(void)
127 {
128 git_worktree *wt;
129 git_repository *repo;
130
131 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
132
133 cl_git_pass(git_repository_open_from_worktree(&repo, wt));
134 cl_assert_equal_s(git_repository_workdir(repo),
135 git_repository_workdir(fixture.worktree));
136
137 git_repository_free(repo);
138 git_worktree_free(wt);
139 }
140
141 void test_worktree_worktree__open_invalid_commondir(void)
142 {
143 git_worktree *wt;
144 git_repository *repo;
145 git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
146
147 cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/commondir"));
148 cl_git_pass(git_buf_printf(&path,
149 "%s/worktrees/testrepo-worktree/commondir",
150 fixture.repo->commondir));
151 cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
152
153 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
154 cl_git_fail(git_repository_open_from_worktree(&repo, wt));
155
156 git_buf_dispose(&buf);
157 git_buf_dispose(&path);
158 git_worktree_free(wt);
159 }
160
161 void test_worktree_worktree__open_invalid_gitdir(void)
162 {
163 git_worktree *wt;
164 git_repository *repo;
165 git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
166
167 cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir"));
168 cl_git_pass(git_buf_printf(&path,
169 "%s/worktrees/testrepo-worktree/gitdir",
170 fixture.repo->commondir));
171 cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
172
173 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
174 cl_git_fail(git_repository_open_from_worktree(&repo, wt));
175
176 git_buf_dispose(&buf);
177 git_buf_dispose(&path);
178 git_worktree_free(wt);
179 }
180
181 void test_worktree_worktree__open_invalid_parent(void)
182 {
183 git_worktree *wt;
184 git_repository *repo;
185 git_buf buf = GIT_BUF_INIT;
186
187 cl_git_pass(git_buf_sets(&buf, "/path/to/nonexistent/gitdir"));
188 cl_git_pass(git_futils_writebuffer(&buf,
189 fixture.worktree->gitlink, O_RDWR, 0644));
190
191 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
192 cl_git_fail(git_repository_open_from_worktree(&repo, wt));
193
194 git_buf_dispose(&buf);
195 git_worktree_free(wt);
196 }
197
198 void test_worktree_worktree__init(void)
199 {
200 git_worktree *wt;
201 git_repository *repo;
202 git_reference *branch;
203 git_buf path = GIT_BUF_INIT;
204
205 cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
206 cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
207
208 /* Open and verify created repo */
209 cl_git_pass(git_repository_open(&repo, path.ptr));
210 cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-new/") == 0);
211 cl_git_pass(git_branch_lookup(&branch, repo, "worktree-new", GIT_BRANCH_LOCAL));
212
213 git_buf_dispose(&path);
214 git_worktree_free(wt);
215 git_reference_free(branch);
216 git_repository_free(repo);
217 }
218
219 void test_worktree_worktree__add_locked(void)
220 {
221 git_worktree *wt;
222 git_repository *repo;
223 git_reference *branch;
224 git_buf path = GIT_BUF_INIT;
225 git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
226
227 opts.lock = 1;
228
229 cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-locked"));
230 cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-locked", path.ptr, &opts));
231
232 /* Open and verify created repo */
233 cl_assert(git_worktree_is_locked(NULL, wt));
234 cl_git_pass(git_repository_open(&repo, path.ptr));
235 cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-locked/") == 0);
236 cl_git_pass(git_branch_lookup(&branch, repo, "worktree-locked", GIT_BRANCH_LOCAL));
237
238 git_buf_dispose(&path);
239 git_worktree_free(wt);
240 git_reference_free(branch);
241 git_repository_free(repo);
242 }
243
244 void test_worktree_worktree__init_existing_branch(void)
245 {
246 git_reference *head, *branch;
247 git_commit *commit;
248 git_worktree *wt;
249 git_buf path = GIT_BUF_INIT;
250
251 cl_git_pass(git_repository_head(&head, fixture.repo));
252 cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
253 cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new", commit, false));
254
255 cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
256 cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
257
258 git_buf_dispose(&path);
259 git_commit_free(commit);
260 git_reference_free(head);
261 git_reference_free(branch);
262 }
263
264 void test_worktree_worktree__add_with_explicit_branch(void)
265 {
266 git_reference *head, *branch, *wthead;
267 git_commit *commit;
268 git_worktree *wt;
269 git_repository *wtrepo;
270 git_buf path = GIT_BUF_INIT;
271 git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
272
273 cl_git_pass(git_repository_head(&head, fixture.repo));
274 cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
275 cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-with-ref", commit, false));
276
277 opts.ref = branch;
278
279 cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-with-different-name"));
280 cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-with-different-name", path.ptr, &opts));
281 cl_git_pass(git_repository_open_from_worktree(&wtrepo, wt));
282 cl_git_pass(git_repository_head(&wthead, wtrepo));
283 cl_assert_equal_s(git_reference_name(wthead), "refs/heads/worktree-with-ref");
284
285 git_buf_dispose(&path);
286 git_commit_free(commit);
287 git_reference_free(head);
288 git_reference_free(branch);
289 git_reference_free(wthead);
290 git_repository_free(wtrepo);
291 git_worktree_free(wt);
292 }
293
294
295 void test_worktree_worktree__init_existing_worktree(void)
296 {
297 git_worktree *wt;
298 git_buf path = GIT_BUF_INIT;
299
300 cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
301 cl_git_fail(git_worktree_add(&wt, fixture.repo, "testrepo-worktree", path.ptr, NULL));
302
303 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
304 cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink);
305
306 git_buf_dispose(&path);
307 git_worktree_free(wt);
308 }
309
310 void test_worktree_worktree__init_existing_path(void)
311 {
312 const char *wtfiles[] = { "HEAD", "commondir", "gitdir", "index" };
313 git_worktree *wt;
314 git_buf path = GIT_BUF_INIT;
315 unsigned i;
316
317 /* Delete files to verify they have not been created by
318 * the init call */
319 for (i = 0; i < ARRAY_SIZE(wtfiles); i++) {
320 cl_git_pass(git_buf_joinpath(&path,
321 fixture.worktree->gitdir, wtfiles[i]));
322 cl_git_pass(p_unlink(path.ptr));
323 }
324
325 cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../testrepo-worktree"));
326 cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
327
328 /* Verify files have not been re-created */
329 for (i = 0; i < ARRAY_SIZE(wtfiles); i++) {
330 cl_git_pass(git_buf_joinpath(&path,
331 fixture.worktree->gitdir, wtfiles[i]));
332 cl_assert(!git_path_exists(path.ptr));
333 }
334
335 git_buf_dispose(&path);
336 }
337
338 void test_worktree_worktree__init_submodule(void)
339 {
340 git_repository *repo, *sm, *wt;
341 git_worktree *worktree;
342 git_buf path = GIT_BUF_INIT;
343
344 cleanup_fixture_worktree(&fixture);
345 repo = setup_fixture_submod2();
346
347 cl_git_pass(git_buf_joinpath(&path, repo->workdir, "sm_unchanged"));
348 cl_git_pass(git_repository_open(&sm, path.ptr));
349 cl_git_pass(git_buf_joinpath(&path, repo->workdir, "../worktree/"));
350 cl_git_pass(git_worktree_add(&worktree, sm, "repo-worktree", path.ptr, NULL));
351 cl_git_pass(git_repository_open_from_worktree(&wt, worktree));
352
353 cl_git_pass(git_path_prettify_dir(&path, path.ptr, NULL));
354 cl_assert_equal_s(path.ptr, wt->workdir);
355 cl_git_pass(git_path_prettify_dir(&path, sm->commondir, NULL));
356 cl_assert_equal_s(sm->commondir, wt->commondir);
357
358 cl_git_pass(git_buf_joinpath(&path, sm->gitdir, "worktrees/repo-worktree/"));
359 cl_assert_equal_s(path.ptr, wt->gitdir);
360
361 git_buf_dispose(&path);
362 git_worktree_free(worktree);
363 git_repository_free(sm);
364 git_repository_free(wt);
365 }
366
367 void test_worktree_worktree__validate(void)
368 {
369 git_worktree *wt;
370
371 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
372 cl_git_pass(git_worktree_validate(wt));
373
374 git_worktree_free(wt);
375 }
376
377 void test_worktree_worktree__name(void)
378 {
379 git_worktree *wt;
380
381 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
382 cl_assert_equal_s(git_worktree_name(wt), "testrepo-worktree");
383
384 git_worktree_free(wt);
385 }
386
387 void test_worktree_worktree__path(void)
388 {
389 git_worktree *wt;
390 git_buf expected_path = GIT_BUF_INIT;
391
392 cl_git_pass(git_buf_joinpath(&expected_path, clar_sandbox_path(), "testrepo-worktree"));
393 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
394 cl_assert_equal_s(git_worktree_path(wt), expected_path.ptr);
395
396 git_buf_dispose(&expected_path);
397 git_worktree_free(wt);
398 }
399
400 void test_worktree_worktree__validate_invalid_commondir(void)
401 {
402 git_worktree *wt;
403
404 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
405 git__free(wt->commondir_path);
406 wt->commondir_path = "/path/to/invalid/commondir";
407
408 cl_git_fail(git_worktree_validate(wt));
409
410 wt->commondir_path = NULL;
411 git_worktree_free(wt);
412 }
413
414 void test_worktree_worktree__validate_invalid_gitdir(void)
415 {
416 git_worktree *wt;
417
418 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
419 git__free(wt->gitdir_path);
420 wt->gitdir_path = "/path/to/invalid/gitdir";
421 cl_git_fail(git_worktree_validate(wt));
422
423 wt->gitdir_path = NULL;
424 git_worktree_free(wt);
425 }
426
427 void test_worktree_worktree__validate_invalid_parent(void)
428 {
429 git_worktree *wt;
430
431 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
432 git__free(wt->parent_path);
433 wt->parent_path = "/path/to/invalid/parent";
434 cl_git_fail(git_worktree_validate(wt));
435
436 wt->parent_path = NULL;
437 git_worktree_free(wt);
438 }
439
440 void test_worktree_worktree__lock_with_reason(void)
441 {
442 git_worktree *wt;
443 git_buf reason = GIT_BUF_INIT;
444
445 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
446
447 cl_assert(!git_worktree_is_locked(NULL, wt));
448 cl_git_pass(git_worktree_lock(wt, "because"));
449 cl_assert(git_worktree_is_locked(&reason, wt) > 0);
450 cl_assert_equal_s(reason.ptr, "because");
451 cl_assert(wt->locked);
452
453 git_buf_dispose(&reason);
454 git_worktree_free(wt);
455 }
456
457 void test_worktree_worktree__lock_without_reason(void)
458 {
459 git_worktree *wt;
460 git_buf reason = GIT_BUF_INIT;
461
462 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
463
464 cl_assert(!git_worktree_is_locked(NULL, wt));
465 cl_git_pass(git_worktree_lock(wt, NULL));
466 cl_assert(git_worktree_is_locked(&reason, wt) > 0);
467 cl_assert_equal_i(reason.size, 0);
468 cl_assert(wt->locked);
469
470 git_buf_dispose(&reason);
471 git_worktree_free(wt);
472 }
473
474 void test_worktree_worktree__unlock_unlocked_worktree(void)
475 {
476 git_worktree *wt;
477
478 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
479 cl_assert(!git_worktree_is_locked(NULL, wt));
480 cl_assert_equal_i(1, git_worktree_unlock(wt));
481 cl_assert(!wt->locked);
482
483 git_worktree_free(wt);
484 }
485
486 void test_worktree_worktree__unlock_locked_worktree(void)
487 {
488 git_worktree *wt;
489
490 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
491 cl_git_pass(git_worktree_lock(wt, NULL));
492 cl_assert(git_worktree_is_locked(NULL, wt));
493 cl_assert_equal_i(0, git_worktree_unlock(wt));
494 cl_assert(!wt->locked);
495
496 git_worktree_free(wt);
497 }
498
499 void test_worktree_worktree__prune_without_opts_fails(void)
500 {
501 git_worktree *wt;
502 git_repository *repo;
503
504 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
505 cl_git_fail(git_worktree_prune(wt, NULL));
506
507 /* Assert the repository is still valid */
508 cl_git_pass(git_repository_open_from_worktree(&repo, wt));
509
510 git_worktree_free(wt);
511 git_repository_free(repo);
512 }
513
514 void test_worktree_worktree__prune_valid(void)
515 {
516 git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
517 git_worktree *wt;
518 git_repository *repo;
519
520 opts.flags = GIT_WORKTREE_PRUNE_VALID;
521
522 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
523 cl_git_pass(git_worktree_prune(wt, &opts));
524
525 /* Assert the repository is not valid anymore */
526 cl_git_fail(git_repository_open_from_worktree(&repo, wt));
527
528 git_worktree_free(wt);
529 git_repository_free(repo);
530 }
531
532 void test_worktree_worktree__prune_locked(void)
533 {
534 git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
535 git_worktree *wt;
536 git_repository *repo;
537
538 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
539 cl_git_pass(git_worktree_lock(wt, NULL));
540
541 opts.flags = GIT_WORKTREE_PRUNE_VALID;
542 cl_git_fail(git_worktree_prune(wt, &opts));
543 /* Assert the repository is still valid */
544 cl_git_pass(git_repository_open_from_worktree(&repo, wt));
545
546 opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_LOCKED;
547 cl_git_pass(git_worktree_prune(wt, &opts));
548
549 git_worktree_free(wt);
550 git_repository_free(repo);
551 }
552
553 void test_worktree_worktree__prune_gitdir_only(void)
554 {
555 git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
556 git_worktree *wt;
557
558 opts.flags = GIT_WORKTREE_PRUNE_VALID;
559 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
560 cl_git_pass(git_worktree_prune(wt, &opts));
561
562 cl_assert(!git_path_exists(wt->gitdir_path));
563 cl_assert(git_path_exists(wt->gitlink_path));
564
565 git_worktree_free(wt);
566 }
567
568 void test_worktree_worktree__prune_worktree(void)
569 {
570 git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
571 git_worktree *wt;
572
573 opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_WORKING_TREE;
574
575 cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
576 cl_git_pass(git_worktree_prune(wt, &opts));
577
578 cl_assert(!git_path_exists(wt->gitdir_path));
579 cl_assert(!git_path_exists(wt->gitlink_path));
580
581 git_worktree_free(wt);
582 }
583
584 static int foreach_worktree_cb(git_repository *worktree, void *payload)
585 {
586 int *counter = (int *)payload;
587
588 switch (*counter) {
589 case 0:
590 cl_assert_equal_s(git_repository_path(fixture.repo),
591 git_repository_path(worktree));
592 cl_assert(!git_repository_is_worktree(worktree));
593 break;
594 case 1:
595 cl_assert_equal_s(git_repository_path(fixture.worktree),
596 git_repository_path(worktree));
597 cl_assert(git_repository_is_worktree(worktree));
598 break;
599 default:
600 cl_fail("more worktrees found than expected");
601 }
602
603 (*counter)++;
604
605 return 0;
606 }
607
608 void test_worktree_worktree__foreach_worktree_lists_all_worktrees(void)
609 {
610 int counter = 0;
611 cl_git_pass(git_repository_foreach_worktree(fixture.repo, foreach_worktree_cb, &counter));
612 }