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_buf path
= GIT_BUF_INIT
;
47 cl_git_pass(git_buf_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_buf_truncate(&path
, len
);
58 cl_git_pass(git_buf_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_buf_truncate(&path
, len
);
69 cl_git_pass(git_buf_joinpath(&path
, path
.ptr
, filesets
[i
][j
]));
74 git_buf_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_buf gitdir_path
= GIT_BUF_INIT
;
105 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
107 cl_git_pass(git_buf_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_buf_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_buf buf
= GIT_BUF_INIT
, path
= GIT_BUF_INIT
;
148 cl_git_pass(git_buf_sets(&buf
, "/path/to/nonexistent/commondir"));
149 cl_git_pass(git_buf_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_buf_dispose(&buf
);
158 git_buf_dispose(&path
);
159 git_worktree_free(wt
);
162 void test_worktree_worktree__open_invalid_gitdir(void)
165 git_repository
*repo
;
166 git_buf buf
= GIT_BUF_INIT
, path
= GIT_BUF_INIT
;
168 cl_git_pass(git_buf_sets(&buf
, "/path/to/nonexistent/gitdir"));
169 cl_git_pass(git_buf_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_buf_dispose(&buf
);
178 git_buf_dispose(&path
);
179 git_worktree_free(wt
);
182 void test_worktree_worktree__open_invalid_parent(void)
185 git_repository
*repo
;
186 git_buf buf
= GIT_BUF_INIT
;
188 cl_git_pass(git_buf_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_buf_dispose(&buf
);
196 git_worktree_free(wt
);
199 void test_worktree_worktree__init(void)
202 git_repository
*repo
;
203 git_reference
*branch
;
204 git_buf path
= GIT_BUF_INIT
;
206 cl_git_pass(git_buf_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_buf_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_buf path
= GIT_BUF_INIT
;
226 git_worktree_add_options opts
= GIT_WORKTREE_ADD_OPTIONS_INIT
;
230 cl_git_pass(git_buf_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_buf_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_buf path
= GIT_BUF_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_buf_joinpath(&path
, fixture
.repo
->workdir
, "../worktree-new"));
257 cl_git_fail(git_worktree_add(&wt
, fixture
.repo
, "worktree-new", path
.ptr
, NULL
));
259 git_buf_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_buf path
= GIT_BUF_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_buf_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_buf_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
);
296 void test_worktree_worktree__init_existing_worktree(void)
299 git_buf path
= GIT_BUF_INIT
;
301 cl_git_pass(git_buf_joinpath(&path
, fixture
.repo
->workdir
, "../worktree-new"));
302 cl_git_fail(git_worktree_add(&wt
, fixture
.repo
, "testrepo-worktree", path
.ptr
, NULL
));
304 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
305 cl_assert_equal_s(wt
->gitlink_path
, fixture
.worktree
->gitlink
);
307 git_buf_dispose(&path
);
308 git_worktree_free(wt
);
311 void test_worktree_worktree__init_existing_path(void)
313 const char *wtfiles
[] = { "HEAD", "commondir", "gitdir", "index" };
315 git_buf path
= GIT_BUF_INIT
;
318 /* Delete files to verify they have not been created by
320 for (i
= 0; i
< ARRAY_SIZE(wtfiles
); i
++) {
321 cl_git_pass(git_buf_joinpath(&path
,
322 fixture
.worktree
->gitdir
, wtfiles
[i
]));
323 cl_git_pass(p_unlink(path
.ptr
));
326 cl_git_pass(git_buf_joinpath(&path
, fixture
.repo
->workdir
, "../testrepo-worktree"));
327 cl_git_fail(git_worktree_add(&wt
, fixture
.repo
, "worktree-new", path
.ptr
, NULL
));
329 /* Verify files have not been re-created */
330 for (i
= 0; i
< ARRAY_SIZE(wtfiles
); i
++) {
331 cl_git_pass(git_buf_joinpath(&path
,
332 fixture
.worktree
->gitdir
, wtfiles
[i
]));
333 cl_assert(!git_path_exists(path
.ptr
));
336 git_buf_dispose(&path
);
339 void test_worktree_worktree__init_submodule(void)
341 git_repository
*repo
, *sm
, *wt
;
342 git_worktree
*worktree
;
343 git_buf path
= GIT_BUF_INIT
;
345 cleanup_fixture_worktree(&fixture
);
346 repo
= setup_fixture_submod2();
348 cl_git_pass(git_buf_joinpath(&path
, repo
->workdir
, "sm_unchanged"));
349 cl_git_pass(git_repository_open(&sm
, path
.ptr
));
350 cl_git_pass(git_buf_joinpath(&path
, repo
->workdir
, "../worktree/"));
351 cl_git_pass(git_worktree_add(&worktree
, sm
, "repo-worktree", path
.ptr
, NULL
));
352 cl_git_pass(git_repository_open_from_worktree(&wt
, worktree
));
354 cl_git_pass(git_path_prettify_dir(&path
, path
.ptr
, NULL
));
355 cl_assert_equal_s(path
.ptr
, wt
->workdir
);
356 cl_git_pass(git_path_prettify_dir(&path
, sm
->commondir
, NULL
));
357 cl_assert_equal_s(sm
->commondir
, wt
->commondir
);
359 cl_git_pass(git_buf_joinpath(&path
, sm
->gitdir
, "worktrees/repo-worktree/"));
360 cl_assert_equal_s(path
.ptr
, wt
->gitdir
);
362 git_buf_dispose(&path
);
363 git_worktree_free(worktree
);
364 git_repository_free(sm
);
365 git_repository_free(wt
);
368 void test_worktree_worktree__validate(void)
372 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
373 cl_git_pass(git_worktree_validate(wt
));
375 git_worktree_free(wt
);
378 void test_worktree_worktree__name(void)
382 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
383 cl_assert_equal_s(git_worktree_name(wt
), "testrepo-worktree");
385 git_worktree_free(wt
);
388 void test_worktree_worktree__path(void)
391 git_buf expected_path
= GIT_BUF_INIT
;
393 cl_git_pass(git_buf_joinpath(&expected_path
, clar_sandbox_path(), "testrepo-worktree"));
394 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
395 cl_assert_equal_s(git_worktree_path(wt
), expected_path
.ptr
);
397 git_buf_dispose(&expected_path
);
398 git_worktree_free(wt
);
401 void test_worktree_worktree__validate_invalid_commondir(void)
405 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
406 git__free(wt
->commondir_path
);
407 wt
->commondir_path
= "/path/to/invalid/commondir";
409 cl_git_fail(git_worktree_validate(wt
));
411 wt
->commondir_path
= NULL
;
412 git_worktree_free(wt
);
415 void test_worktree_worktree__validate_invalid_gitdir(void)
419 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
420 git__free(wt
->gitdir_path
);
421 wt
->gitdir_path
= "/path/to/invalid/gitdir";
422 cl_git_fail(git_worktree_validate(wt
));
424 wt
->gitdir_path
= NULL
;
425 git_worktree_free(wt
);
428 void test_worktree_worktree__validate_invalid_parent(void)
432 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
433 git__free(wt
->parent_path
);
434 wt
->parent_path
= "/path/to/invalid/parent";
435 cl_git_fail(git_worktree_validate(wt
));
437 wt
->parent_path
= NULL
;
438 git_worktree_free(wt
);
441 void test_worktree_worktree__lock_with_reason(void)
444 git_buf reason
= GIT_BUF_INIT
;
446 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
448 cl_assert(!git_worktree_is_locked(NULL
, wt
));
449 cl_git_pass(git_worktree_lock(wt
, "because"));
450 cl_assert(git_worktree_is_locked(&reason
, wt
) > 0);
451 cl_assert_equal_s(reason
.ptr
, "because");
452 cl_assert(wt
->locked
);
454 git_buf_dispose(&reason
);
455 git_worktree_free(wt
);
458 void test_worktree_worktree__lock_without_reason(void)
461 git_buf reason
= GIT_BUF_INIT
;
463 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
465 cl_assert(!git_worktree_is_locked(NULL
, wt
));
466 cl_git_pass(git_worktree_lock(wt
, NULL
));
467 cl_assert(git_worktree_is_locked(&reason
, wt
) > 0);
468 cl_assert_equal_i(reason
.size
, 0);
469 cl_assert(wt
->locked
);
471 git_buf_dispose(&reason
);
472 git_worktree_free(wt
);
475 void test_worktree_worktree__unlock_unlocked_worktree(void)
479 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
480 cl_assert(!git_worktree_is_locked(NULL
, wt
));
481 cl_assert_equal_i(1, git_worktree_unlock(wt
));
482 cl_assert(!wt
->locked
);
484 git_worktree_free(wt
);
487 void test_worktree_worktree__unlock_locked_worktree(void)
491 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
492 cl_git_pass(git_worktree_lock(wt
, NULL
));
493 cl_assert(git_worktree_is_locked(NULL
, wt
));
494 cl_assert_equal_i(0, git_worktree_unlock(wt
));
495 cl_assert(!wt
->locked
);
497 git_worktree_free(wt
);
500 void test_worktree_worktree__prune_without_opts_fails(void)
503 git_repository
*repo
;
505 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
506 cl_git_fail(git_worktree_prune(wt
, NULL
));
508 /* Assert the repository is still valid */
509 cl_git_pass(git_repository_open_from_worktree(&repo
, wt
));
511 git_worktree_free(wt
);
512 git_repository_free(repo
);
515 void test_worktree_worktree__prune_valid(void)
517 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
519 git_repository
*repo
;
521 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
;
523 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
524 cl_git_pass(git_worktree_prune(wt
, &opts
));
526 /* Assert the repository is not valid anymore */
527 cl_git_fail(git_repository_open_from_worktree(&repo
, wt
));
529 git_worktree_free(wt
);
530 git_repository_free(repo
);
533 void test_worktree_worktree__prune_locked(void)
535 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
537 git_repository
*repo
;
539 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
540 cl_git_pass(git_worktree_lock(wt
, NULL
));
542 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
;
543 cl_git_fail(git_worktree_prune(wt
, &opts
));
544 /* Assert the repository is still valid */
545 cl_git_pass(git_repository_open_from_worktree(&repo
, wt
));
547 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
|GIT_WORKTREE_PRUNE_LOCKED
;
548 cl_git_pass(git_worktree_prune(wt
, &opts
));
550 git_worktree_free(wt
);
551 git_repository_free(repo
);
554 void test_worktree_worktree__prune_gitdir_only(void)
556 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
559 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
;
560 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
561 cl_git_pass(git_worktree_prune(wt
, &opts
));
563 cl_assert(!git_path_exists(wt
->gitdir_path
));
564 cl_assert(git_path_exists(wt
->gitlink_path
));
566 git_worktree_free(wt
);
569 void test_worktree_worktree__prune_worktree(void)
571 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
574 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
|GIT_WORKTREE_PRUNE_WORKING_TREE
;
576 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
577 cl_git_pass(git_worktree_prune(wt
, &opts
));
579 cl_assert(!git_path_exists(wt
->gitdir_path
));
580 cl_assert(!git_path_exists(wt
->gitlink_path
));
582 git_worktree_free(wt
);
585 static int foreach_worktree_cb(git_repository
*worktree
, void *payload
)
587 int *counter
= (int *)payload
;
591 cl_assert_equal_s(git_repository_path(fixture
.repo
),
592 git_repository_path(worktree
));
593 cl_assert(!git_repository_is_worktree(worktree
));
596 cl_assert_equal_s(git_repository_path(fixture
.worktree
),
597 git_repository_path(worktree
));
598 cl_assert(git_repository_is_worktree(worktree
));
601 cl_fail("more worktrees found than expected");
609 void test_worktree_worktree__foreach_worktree_lists_all_worktrees(void)
612 cl_git_pass(git_repository_foreach_worktree(fixture
.repo
, foreach_worktree_cb
, &counter
));
615 void test_worktree_worktree__validate_invalid_worktreedir(void)
619 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
620 p_rename("testrepo-worktree", "testrepo-worktree-tmp");
621 cl_git_fail(git_worktree_validate(wt
));
622 p_rename("testrepo-worktree-tmp", "testrepo-worktree");
624 git_worktree_free(wt
);