1 #include "clar_libgit2.h"
2 #include "worktree_helpers.h"
3 #include "submodule/submodule_helpers.h"
6 #include "repository.h"
9 #define COMMON_REPO "testrepo"
10 #define WORKTREE_REPO "testrepo-worktree"
12 static worktree_fixture fixture
=
13 WORKTREE_FIXTURE_INIT(COMMON_REPO
, WORKTREE_REPO
);
15 void test_worktree_worktree__initialize(void)
17 setup_fixture_worktree(&fixture
);
20 void test_worktree_worktree__cleanup(void)
22 cleanup_fixture_worktree(&fixture
);
25 void test_worktree_worktree__list(void)
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");
33 git_strarray_dispose(&wts
);
36 void test_worktree_worktree__list_with_invalid_worktree_dirs(void)
38 const char *filesets
[3][2] = {
39 { "gitdir", "commondir" },
41 { "HEAD", "commondir" },
43 git_str path
= GIT_STR_INIT
;
47 cl_git_pass(git_str_joinpath(&path
,
48 fixture
.repo
->commondir
,
49 "worktrees/invalid"));
50 cl_git_pass(p_mkdir(path
.ptr
, 0755));
54 for (i
= 0; i
< ARRAY_SIZE(filesets
); i
++) {
56 for (j
= 0; j
< ARRAY_SIZE(filesets
[i
]); j
++) {
57 git_str_truncate(&path
, len
);
58 cl_git_pass(git_str_joinpath(&path
, path
.ptr
, filesets
[i
][j
]));
59 cl_git_pass(p_close(p_creat(path
.ptr
, 0644)));
62 cl_git_pass(git_worktree_list(&wts
, fixture
.worktree
));
63 cl_assert_equal_i(wts
.count
, 1);
64 cl_assert_equal_s(wts
.strings
[0], "testrepo-worktree");
65 git_strarray_dispose(&wts
);
67 for (j
= 0; j
< ARRAY_SIZE(filesets
[i
]); j
++) {
68 git_str_truncate(&path
, len
);
69 cl_git_pass(git_str_joinpath(&path
, path
.ptr
, filesets
[i
][j
]));
74 git_str_dispose(&path
);
77 void test_worktree_worktree__list_in_worktree_repo(void)
81 cl_git_pass(git_worktree_list(&wts
, fixture
.worktree
));
82 cl_assert_equal_i(wts
.count
, 1);
83 cl_assert_equal_s(wts
.strings
[0], "testrepo-worktree");
85 git_strarray_dispose(&wts
);
88 void test_worktree_worktree__list_without_worktrees(void)
93 repo
= cl_git_sandbox_init("testrepo2");
94 cl_git_pass(git_worktree_list(&wts
, repo
));
95 cl_assert_equal_i(wts
.count
, 0);
97 git_repository_free(repo
);
100 void test_worktree_worktree__lookup(void)
103 git_str gitdir_path
= GIT_STR_INIT
;
105 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
107 cl_git_pass(git_str_joinpath(&gitdir_path
, fixture
.repo
->commondir
, "worktrees/testrepo-worktree/"));
109 cl_assert_equal_s(wt
->gitdir_path
, gitdir_path
.ptr
);
110 cl_assert_equal_s(wt
->parent_path
, fixture
.repo
->workdir
);
111 cl_assert_equal_s(wt
->gitlink_path
, fixture
.worktree
->gitlink
);
112 cl_assert_equal_s(wt
->commondir_path
, fixture
.repo
->gitdir
);
113 cl_assert_equal_s(wt
->commondir_path
, fixture
.repo
->commondir
);
115 git_str_dispose(&gitdir_path
);
116 git_worktree_free(wt
);
119 void test_worktree_worktree__lookup_nonexistent_worktree(void)
123 cl_git_fail(git_worktree_lookup(&wt
, fixture
.repo
, "nonexistent"));
124 cl_assert_equal_p(wt
, NULL
);
127 void test_worktree_worktree__open(void)
130 git_repository
*repo
;
132 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
134 cl_git_pass(git_repository_open_from_worktree(&repo
, wt
));
135 cl_assert_equal_s(git_repository_workdir(repo
),
136 git_repository_workdir(fixture
.worktree
));
138 git_repository_free(repo
);
139 git_worktree_free(wt
);
142 void test_worktree_worktree__open_invalid_commondir(void)
145 git_repository
*repo
;
146 git_str buf
= GIT_STR_INIT
, path
= GIT_STR_INIT
;
148 cl_git_pass(git_str_sets(&buf
, "/path/to/nonexistent/commondir"));
149 cl_git_pass(git_str_joinpath(&path
,
150 fixture
.repo
->commondir
,
151 "worktrees/testrepo-worktree/commondir"));
152 cl_git_pass(git_futils_writebuffer(&buf
, path
.ptr
, O_RDWR
, 0644));
154 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
155 cl_git_fail(git_repository_open_from_worktree(&repo
, wt
));
157 git_str_dispose(&buf
);
158 git_str_dispose(&path
);
159 git_worktree_free(wt
);
162 void test_worktree_worktree__open_invalid_gitdir(void)
165 git_repository
*repo
;
166 git_str buf
= GIT_STR_INIT
, path
= GIT_STR_INIT
;
168 cl_git_pass(git_str_sets(&buf
, "/path/to/nonexistent/gitdir"));
169 cl_git_pass(git_str_joinpath(&path
,
170 fixture
.repo
->commondir
,
171 "worktrees/testrepo-worktree/gitdir"));
172 cl_git_pass(git_futils_writebuffer(&buf
, path
.ptr
, O_RDWR
, 0644));
174 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
175 cl_git_fail(git_repository_open_from_worktree(&repo
, wt
));
177 git_str_dispose(&buf
);
178 git_str_dispose(&path
);
179 git_worktree_free(wt
);
182 void test_worktree_worktree__open_invalid_parent(void)
185 git_repository
*repo
;
186 git_str buf
= GIT_STR_INIT
;
188 cl_git_pass(git_str_sets(&buf
, "/path/to/nonexistent/gitdir"));
189 cl_git_pass(git_futils_writebuffer(&buf
,
190 fixture
.worktree
->gitlink
, O_RDWR
, 0644));
192 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
193 cl_git_fail(git_repository_open_from_worktree(&repo
, wt
));
195 git_str_dispose(&buf
);
196 git_worktree_free(wt
);
199 void test_worktree_worktree__init(void)
202 git_repository
*repo
;
203 git_reference
*branch
;
204 git_str path
= GIT_STR_INIT
;
206 cl_git_pass(git_str_joinpath(&path
, fixture
.repo
->workdir
, "../worktree-new"));
207 cl_git_pass(git_worktree_add(&wt
, fixture
.repo
, "worktree-new", path
.ptr
, NULL
));
209 /* Open and verify created repo */
210 cl_git_pass(git_repository_open(&repo
, path
.ptr
));
211 cl_assert(git__suffixcmp(git_repository_workdir(repo
), "worktree-new/") == 0);
212 cl_git_pass(git_branch_lookup(&branch
, repo
, "worktree-new", GIT_BRANCH_LOCAL
));
214 git_str_dispose(&path
);
215 git_worktree_free(wt
);
216 git_reference_free(branch
);
217 git_repository_free(repo
);
220 void test_worktree_worktree__add_locked(void)
223 git_repository
*repo
;
224 git_reference
*branch
;
225 git_str path
= GIT_STR_INIT
;
226 git_worktree_add_options opts
= GIT_WORKTREE_ADD_OPTIONS_INIT
;
230 cl_git_pass(git_str_joinpath(&path
, fixture
.repo
->workdir
, "../worktree-locked"));
231 cl_git_pass(git_worktree_add(&wt
, fixture
.repo
, "worktree-locked", path
.ptr
, &opts
));
233 /* Open and verify created repo */
234 cl_assert(git_worktree_is_locked(NULL
, wt
));
235 cl_git_pass(git_repository_open(&repo
, path
.ptr
));
236 cl_assert(git__suffixcmp(git_repository_workdir(repo
), "worktree-locked/") == 0);
237 cl_git_pass(git_branch_lookup(&branch
, repo
, "worktree-locked", GIT_BRANCH_LOCAL
));
239 git_str_dispose(&path
);
240 git_worktree_free(wt
);
241 git_reference_free(branch
);
242 git_repository_free(repo
);
245 void test_worktree_worktree__init_existing_branch(void)
247 git_reference
*head
, *branch
;
250 git_str path
= GIT_STR_INIT
;
252 cl_git_pass(git_repository_head(&head
, fixture
.repo
));
253 cl_git_pass(git_commit_lookup(&commit
, fixture
.repo
, &head
->target
.oid
));
254 cl_git_pass(git_branch_create(&branch
, fixture
.repo
, "worktree-new", commit
, false));
256 cl_git_pass(git_str_joinpath(&path
, fixture
.repo
->workdir
, "../worktree-new"));
257 cl_git_fail(git_worktree_add(&wt
, fixture
.repo
, "worktree-new", path
.ptr
, NULL
));
259 git_str_dispose(&path
);
260 git_commit_free(commit
);
261 git_reference_free(head
);
262 git_reference_free(branch
);
265 void test_worktree_worktree__add_with_explicit_branch(void)
267 git_reference
*head
, *branch
, *wthead
;
270 git_repository
*wtrepo
;
271 git_str path
= GIT_STR_INIT
;
272 git_worktree_add_options opts
= GIT_WORKTREE_ADD_OPTIONS_INIT
;
274 cl_git_pass(git_repository_head(&head
, fixture
.repo
));
275 cl_git_pass(git_commit_lookup(&commit
, fixture
.repo
, &head
->target
.oid
));
276 cl_git_pass(git_branch_create(&branch
, fixture
.repo
, "worktree-with-ref", commit
, false));
280 cl_git_pass(git_str_joinpath(&path
, fixture
.repo
->workdir
, "../worktree-with-different-name"));
281 cl_git_pass(git_worktree_add(&wt
, fixture
.repo
, "worktree-with-different-name", path
.ptr
, &opts
));
282 cl_git_pass(git_repository_open_from_worktree(&wtrepo
, wt
));
283 cl_git_pass(git_repository_head(&wthead
, wtrepo
));
284 cl_assert_equal_s(git_reference_name(wthead
), "refs/heads/worktree-with-ref");
286 git_str_dispose(&path
);
287 git_commit_free(commit
);
288 git_reference_free(head
);
289 git_reference_free(branch
);
290 git_reference_free(wthead
);
291 git_repository_free(wtrepo
);
292 git_worktree_free(wt
);
295 void test_worktree_worktree__add_no_checkout(void)
298 git_repository
*wtrepo
;
300 git_str path
= GIT_STR_INIT
;
301 git_worktree_add_options opts
= GIT_WORKTREE_ADD_OPTIONS_INIT
;
303 opts
.checkout_options
.checkout_strategy
= GIT_CHECKOUT_NONE
;
305 cl_git_pass(git_str_joinpath(&path
, fixture
.repo
->workdir
, "../worktree-no-checkout"));
306 cl_git_pass(git_worktree_add(&wt
, fixture
.repo
, "worktree-no-checkout", path
.ptr
, &opts
));
308 cl_git_pass(git_repository_open(&wtrepo
, path
.ptr
));
309 cl_git_pass(git_repository_index(&index
, wtrepo
));
310 cl_assert_equal_i(git_index_entrycount(index
), 0);
312 git_str_dispose(&path
);
313 git_worktree_free(wt
);
314 git_index_free(index
);
315 git_repository_free(wtrepo
);
318 void test_worktree_worktree__init_existing_worktree(void)
321 git_str path
= GIT_STR_INIT
;
323 cl_git_pass(git_str_joinpath(&path
, fixture
.repo
->workdir
, "../worktree-new"));
324 cl_git_fail(git_worktree_add(&wt
, fixture
.repo
, "testrepo-worktree", path
.ptr
, NULL
));
326 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
327 cl_assert_equal_s(wt
->gitlink_path
, fixture
.worktree
->gitlink
);
329 git_str_dispose(&path
);
330 git_worktree_free(wt
);
333 void test_worktree_worktree__init_existing_path(void)
335 const char *wtfiles
[] = { "HEAD", "commondir", "gitdir", "index" };
337 git_str path
= GIT_STR_INIT
;
340 /* Delete files to verify they have not been created by
342 for (i
= 0; i
< ARRAY_SIZE(wtfiles
); i
++) {
343 cl_git_pass(git_str_joinpath(&path
,
344 fixture
.worktree
->gitdir
, wtfiles
[i
]));
345 cl_git_pass(p_unlink(path
.ptr
));
348 cl_git_pass(git_str_joinpath(&path
, fixture
.repo
->workdir
, "../testrepo-worktree"));
349 cl_git_fail(git_worktree_add(&wt
, fixture
.repo
, "worktree-new", path
.ptr
, NULL
));
351 /* Verify files have not been re-created */
352 for (i
= 0; i
< ARRAY_SIZE(wtfiles
); i
++) {
353 cl_git_pass(git_str_joinpath(&path
,
354 fixture
.worktree
->gitdir
, wtfiles
[i
]));
355 cl_assert(!git_fs_path_exists(path
.ptr
));
358 git_str_dispose(&path
);
361 void test_worktree_worktree__init_submodule(void)
363 git_repository
*repo
, *sm
, *wt
;
364 git_worktree
*worktree
;
365 git_str path
= GIT_STR_INIT
;
367 cleanup_fixture_worktree(&fixture
);
368 repo
= setup_fixture_submod2();
370 cl_git_pass(git_str_joinpath(&path
, repo
->workdir
, "sm_unchanged"));
371 cl_git_pass(git_repository_open(&sm
, path
.ptr
));
372 cl_git_pass(git_str_joinpath(&path
, repo
->workdir
, "../worktree/"));
373 cl_git_pass(git_worktree_add(&worktree
, sm
, "repo-worktree", path
.ptr
, NULL
));
374 cl_git_pass(git_repository_open_from_worktree(&wt
, worktree
));
376 cl_git_pass(git_fs_path_prettify_dir(&path
, path
.ptr
, NULL
));
377 cl_assert_equal_s(path
.ptr
, wt
->workdir
);
378 cl_git_pass(git_fs_path_prettify_dir(&path
, sm
->commondir
, NULL
));
379 cl_assert_equal_s(sm
->commondir
, wt
->commondir
);
381 cl_git_pass(git_str_joinpath(&path
, sm
->gitdir
, "worktrees/repo-worktree/"));
382 cl_assert_equal_s(path
.ptr
, wt
->gitdir
);
384 git_str_dispose(&path
);
385 git_worktree_free(worktree
);
386 git_repository_free(sm
);
387 git_repository_free(wt
);
390 void test_worktree_worktree__validate(void)
394 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
395 cl_git_pass(git_worktree_validate(wt
));
397 git_worktree_free(wt
);
400 void test_worktree_worktree__name(void)
404 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
405 cl_assert_equal_s(git_worktree_name(wt
), "testrepo-worktree");
407 git_worktree_free(wt
);
410 void test_worktree_worktree__path(void)
413 git_str expected_path
= GIT_STR_INIT
;
415 cl_git_pass(git_str_joinpath(&expected_path
, clar_sandbox_path(), "testrepo-worktree"));
416 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
417 cl_assert_equal_s(git_worktree_path(wt
), expected_path
.ptr
);
419 git_str_dispose(&expected_path
);
420 git_worktree_free(wt
);
423 void test_worktree_worktree__validate_invalid_commondir(void)
427 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
428 git__free(wt
->commondir_path
);
429 wt
->commondir_path
= "/path/to/invalid/commondir";
431 cl_git_fail(git_worktree_validate(wt
));
433 wt
->commondir_path
= NULL
;
434 git_worktree_free(wt
);
437 void test_worktree_worktree__validate_invalid_gitdir(void)
441 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
442 git__free(wt
->gitdir_path
);
443 wt
->gitdir_path
= "/path/to/invalid/gitdir";
444 cl_git_fail(git_worktree_validate(wt
));
446 wt
->gitdir_path
= NULL
;
447 git_worktree_free(wt
);
450 void test_worktree_worktree__validate_invalid_parent(void)
454 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
455 git__free(wt
->parent_path
);
456 wt
->parent_path
= "/path/to/invalid/parent";
457 cl_git_fail(git_worktree_validate(wt
));
459 wt
->parent_path
= NULL
;
460 git_worktree_free(wt
);
463 void test_worktree_worktree__lock_with_reason(void)
466 git_buf reason
= GIT_BUF_INIT
;
468 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
470 cl_assert(!git_worktree_is_locked(NULL
, wt
));
471 cl_git_pass(git_worktree_lock(wt
, "because"));
472 cl_assert(git_worktree_is_locked(&reason
, wt
) > 0);
473 cl_assert_equal_s(reason
.ptr
, "because");
474 cl_assert(wt
->locked
);
476 git_buf_dispose(&reason
);
477 git_worktree_free(wt
);
480 void test_worktree_worktree__lock_without_reason(void)
483 git_buf reason
= GIT_BUF_INIT
;
485 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
487 cl_assert(!git_worktree_is_locked(NULL
, wt
));
488 cl_git_pass(git_worktree_lock(wt
, NULL
));
489 cl_assert(git_worktree_is_locked(&reason
, wt
) > 0);
490 cl_assert_equal_i(reason
.size
, 0);
491 cl_assert(wt
->locked
);
493 git_buf_dispose(&reason
);
494 git_worktree_free(wt
);
497 void test_worktree_worktree__unlock_unlocked_worktree(void)
501 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
502 cl_assert(!git_worktree_is_locked(NULL
, wt
));
503 cl_assert_equal_i(1, git_worktree_unlock(wt
));
504 cl_assert(!wt
->locked
);
506 git_worktree_free(wt
);
509 void test_worktree_worktree__unlock_locked_worktree(void)
513 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
514 cl_git_pass(git_worktree_lock(wt
, NULL
));
515 cl_assert(git_worktree_is_locked(NULL
, wt
));
516 cl_assert_equal_i(0, git_worktree_unlock(wt
));
517 cl_assert(!wt
->locked
);
519 git_worktree_free(wt
);
522 void test_worktree_worktree__prune_without_opts_fails(void)
525 git_repository
*repo
;
527 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
528 cl_git_fail(git_worktree_prune(wt
, NULL
));
530 /* Assert the repository is still valid */
531 cl_git_pass(git_repository_open_from_worktree(&repo
, wt
));
533 git_worktree_free(wt
);
534 git_repository_free(repo
);
537 void test_worktree_worktree__prune_valid(void)
539 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
541 git_repository
*repo
;
543 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
;
545 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
546 cl_git_pass(git_worktree_prune(wt
, &opts
));
548 /* Assert the repository is not valid anymore */
549 cl_git_fail(git_repository_open_from_worktree(&repo
, wt
));
551 git_worktree_free(wt
);
552 git_repository_free(repo
);
555 void test_worktree_worktree__prune_locked(void)
557 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
559 git_repository
*repo
;
561 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
562 cl_git_pass(git_worktree_lock(wt
, NULL
));
564 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
;
565 cl_git_fail(git_worktree_prune(wt
, &opts
));
566 /* Assert the repository is still valid */
567 cl_git_pass(git_repository_open_from_worktree(&repo
, wt
));
569 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
|GIT_WORKTREE_PRUNE_LOCKED
;
570 cl_git_pass(git_worktree_prune(wt
, &opts
));
572 git_worktree_free(wt
);
573 git_repository_free(repo
);
576 void test_worktree_worktree__prune_gitdir_only(void)
578 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
581 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
;
582 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
583 cl_git_pass(git_worktree_prune(wt
, &opts
));
585 cl_assert(!git_fs_path_exists(wt
->gitdir_path
));
586 cl_assert(git_fs_path_exists(wt
->gitlink_path
));
588 git_worktree_free(wt
);
591 void test_worktree_worktree__prune_worktree(void)
593 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
596 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
|GIT_WORKTREE_PRUNE_WORKING_TREE
;
598 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
599 cl_git_pass(git_worktree_prune(wt
, &opts
));
601 cl_assert(!git_fs_path_exists(wt
->gitdir_path
));
602 cl_assert(!git_fs_path_exists(wt
->gitlink_path
));
604 git_worktree_free(wt
);
607 static int foreach_worktree_cb(git_repository
*worktree
, void *payload
)
609 int *counter
= (int *)payload
;
613 cl_assert_equal_s(git_repository_path(fixture
.repo
),
614 git_repository_path(worktree
));
615 cl_assert(!git_repository_is_worktree(worktree
));
618 cl_assert_equal_s(git_repository_path(fixture
.worktree
),
619 git_repository_path(worktree
));
620 cl_assert(git_repository_is_worktree(worktree
));
623 cl_fail("more worktrees found than expected");
631 void test_worktree_worktree__foreach_worktree_lists_all_worktrees(void)
634 cl_git_pass(git_repository_foreach_worktree(fixture
.repo
, foreach_worktree_cb
, &counter
));
637 void test_worktree_worktree__validate_invalid_worktreedir(void)
641 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
642 p_rename("testrepo-worktree", "testrepo-worktree-tmp");
643 cl_git_fail(git_worktree_validate(wt
));
644 p_rename("testrepo-worktree-tmp", "testrepo-worktree");
646 git_worktree_free(wt
);