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_printf(&path
, "%s/worktrees/invalid",
48 fixture
.repo
->commondir
));
49 cl_git_pass(p_mkdir(path
.ptr
, 0755));
53 for (i
= 0; i
< ARRAY_SIZE(filesets
); i
++) {
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)));
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
);
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
]));
73 git_buf_dispose(&path
);
76 void test_worktree_worktree__list_in_worktree_repo(void)
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");
84 git_strarray_dispose(&wts
);
87 void test_worktree_worktree__list_without_worktrees(void)
92 repo
= cl_git_sandbox_init("testrepo2");
93 cl_git_pass(git_worktree_list(&wts
, repo
));
94 cl_assert_equal_i(wts
.count
, 0);
96 git_repository_free(repo
);
99 void test_worktree_worktree__lookup(void)
102 git_buf gitdir_path
= GIT_BUF_INIT
;
104 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
106 cl_git_pass(git_buf_joinpath(&gitdir_path
, fixture
.repo
->commondir
, "worktrees/testrepo-worktree/"));
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
);
114 git_buf_dispose(&gitdir_path
);
115 git_worktree_free(wt
);
118 void test_worktree_worktree__lookup_nonexistent_worktree(void)
122 cl_git_fail(git_worktree_lookup(&wt
, fixture
.repo
, "nonexistent"));
123 cl_assert_equal_p(wt
, NULL
);
126 void test_worktree_worktree__open(void)
129 git_repository
*repo
;
131 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
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
));
137 git_repository_free(repo
);
138 git_worktree_free(wt
);
141 void test_worktree_worktree__open_invalid_commondir(void)
144 git_repository
*repo
;
145 git_buf buf
= GIT_BUF_INIT
, path
= GIT_BUF_INIT
;
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));
153 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
154 cl_git_fail(git_repository_open_from_worktree(&repo
, wt
));
156 git_buf_dispose(&buf
);
157 git_buf_dispose(&path
);
158 git_worktree_free(wt
);
161 void test_worktree_worktree__open_invalid_gitdir(void)
164 git_repository
*repo
;
165 git_buf buf
= GIT_BUF_INIT
, path
= GIT_BUF_INIT
;
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));
173 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
174 cl_git_fail(git_repository_open_from_worktree(&repo
, wt
));
176 git_buf_dispose(&buf
);
177 git_buf_dispose(&path
);
178 git_worktree_free(wt
);
181 void test_worktree_worktree__open_invalid_parent(void)
184 git_repository
*repo
;
185 git_buf buf
= GIT_BUF_INIT
;
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));
191 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
192 cl_git_fail(git_repository_open_from_worktree(&repo
, wt
));
194 git_buf_dispose(&buf
);
195 git_worktree_free(wt
);
198 void test_worktree_worktree__init(void)
201 git_repository
*repo
;
202 git_reference
*branch
;
203 git_buf path
= GIT_BUF_INIT
;
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
));
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
));
213 git_buf_dispose(&path
);
214 git_worktree_free(wt
);
215 git_reference_free(branch
);
216 git_repository_free(repo
);
219 void test_worktree_worktree__add_locked(void)
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
;
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
));
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
));
238 git_buf_dispose(&path
);
239 git_worktree_free(wt
);
240 git_reference_free(branch
);
241 git_repository_free(repo
);
244 void test_worktree_worktree__init_existing_branch(void)
246 git_reference
*head
, *branch
;
249 git_buf path
= GIT_BUF_INIT
;
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));
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
));
258 git_buf_dispose(&path
);
259 git_commit_free(commit
);
260 git_reference_free(head
);
261 git_reference_free(branch
);
264 void test_worktree_worktree__add_with_explicit_branch(void)
266 git_reference
*head
, *branch
, *wthead
;
269 git_repository
*wtrepo
;
270 git_buf path
= GIT_BUF_INIT
;
271 git_worktree_add_options opts
= GIT_WORKTREE_ADD_OPTIONS_INIT
;
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));
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");
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
);
295 void test_worktree_worktree__init_existing_worktree(void)
298 git_buf path
= GIT_BUF_INIT
;
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
));
303 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
304 cl_assert_equal_s(wt
->gitlink_path
, fixture
.worktree
->gitlink
);
306 git_buf_dispose(&path
);
307 git_worktree_free(wt
);
310 void test_worktree_worktree__init_existing_path(void)
312 const char *wtfiles
[] = { "HEAD", "commondir", "gitdir", "index" };
314 git_buf path
= GIT_BUF_INIT
;
317 /* Delete files to verify they have not been created by
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
));
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
));
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
));
335 git_buf_dispose(&path
);
338 void test_worktree_worktree__init_submodule(void)
340 git_repository
*repo
, *sm
, *wt
;
341 git_worktree
*worktree
;
342 git_buf path
= GIT_BUF_INIT
;
344 cleanup_fixture_worktree(&fixture
);
345 repo
= setup_fixture_submod2();
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
));
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
);
358 cl_git_pass(git_buf_joinpath(&path
, sm
->gitdir
, "worktrees/repo-worktree/"));
359 cl_assert_equal_s(path
.ptr
, wt
->gitdir
);
361 git_buf_dispose(&path
);
362 git_worktree_free(worktree
);
363 git_repository_free(sm
);
364 git_repository_free(wt
);
367 void test_worktree_worktree__validate(void)
371 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
372 cl_git_pass(git_worktree_validate(wt
));
374 git_worktree_free(wt
);
377 void test_worktree_worktree__name(void)
381 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
382 cl_assert_equal_s(git_worktree_name(wt
), "testrepo-worktree");
384 git_worktree_free(wt
);
387 void test_worktree_worktree__path(void)
390 git_buf expected_path
= GIT_BUF_INIT
;
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
);
396 git_buf_dispose(&expected_path
);
397 git_worktree_free(wt
);
400 void test_worktree_worktree__validate_invalid_commondir(void)
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";
408 cl_git_fail(git_worktree_validate(wt
));
410 wt
->commondir_path
= NULL
;
411 git_worktree_free(wt
);
414 void test_worktree_worktree__validate_invalid_gitdir(void)
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
));
423 wt
->gitdir_path
= NULL
;
424 git_worktree_free(wt
);
427 void test_worktree_worktree__validate_invalid_parent(void)
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
));
436 wt
->parent_path
= NULL
;
437 git_worktree_free(wt
);
440 void test_worktree_worktree__lock_with_reason(void)
443 git_buf reason
= GIT_BUF_INIT
;
445 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
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
);
453 git_buf_dispose(&reason
);
454 git_worktree_free(wt
);
457 void test_worktree_worktree__lock_without_reason(void)
460 git_buf reason
= GIT_BUF_INIT
;
462 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
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
);
470 git_buf_dispose(&reason
);
471 git_worktree_free(wt
);
474 void test_worktree_worktree__unlock_unlocked_worktree(void)
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
);
483 git_worktree_free(wt
);
486 void test_worktree_worktree__unlock_locked_worktree(void)
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
);
496 git_worktree_free(wt
);
499 void test_worktree_worktree__prune_without_opts_fails(void)
502 git_repository
*repo
;
504 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
505 cl_git_fail(git_worktree_prune(wt
, NULL
));
507 /* Assert the repository is still valid */
508 cl_git_pass(git_repository_open_from_worktree(&repo
, wt
));
510 git_worktree_free(wt
);
511 git_repository_free(repo
);
514 void test_worktree_worktree__prune_valid(void)
516 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
518 git_repository
*repo
;
520 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
;
522 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
523 cl_git_pass(git_worktree_prune(wt
, &opts
));
525 /* Assert the repository is not valid anymore */
526 cl_git_fail(git_repository_open_from_worktree(&repo
, wt
));
528 git_worktree_free(wt
);
529 git_repository_free(repo
);
532 void test_worktree_worktree__prune_locked(void)
534 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
536 git_repository
*repo
;
538 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
539 cl_git_pass(git_worktree_lock(wt
, NULL
));
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
));
546 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
|GIT_WORKTREE_PRUNE_LOCKED
;
547 cl_git_pass(git_worktree_prune(wt
, &opts
));
549 git_worktree_free(wt
);
550 git_repository_free(repo
);
553 void test_worktree_worktree__prune_gitdir_only(void)
555 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
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
));
562 cl_assert(!git_path_exists(wt
->gitdir_path
));
563 cl_assert(git_path_exists(wt
->gitlink_path
));
565 git_worktree_free(wt
);
568 void test_worktree_worktree__prune_worktree(void)
570 git_worktree_prune_options opts
= GIT_WORKTREE_PRUNE_OPTIONS_INIT
;
573 opts
.flags
= GIT_WORKTREE_PRUNE_VALID
|GIT_WORKTREE_PRUNE_WORKING_TREE
;
575 cl_git_pass(git_worktree_lookup(&wt
, fixture
.repo
, "testrepo-worktree"));
576 cl_git_pass(git_worktree_prune(wt
, &opts
));
578 cl_assert(!git_path_exists(wt
->gitdir_path
));
579 cl_assert(!git_path_exists(wt
->gitlink_path
));
581 git_worktree_free(wt
);
584 static int foreach_worktree_cb(git_repository
*worktree
, void *payload
)
586 int *counter
= (int *)payload
;
590 cl_assert_equal_s(git_repository_path(fixture
.repo
),
591 git_repository_path(worktree
));
592 cl_assert(!git_repository_is_worktree(worktree
));
595 cl_assert_equal_s(git_repository_path(fixture
.worktree
),
596 git_repository_path(worktree
));
597 cl_assert(git_repository_is_worktree(worktree
));
600 cl_fail("more worktrees found than expected");
608 void test_worktree_worktree__foreach_worktree_lists_all_worktrees(void)
611 cl_git_pass(git_repository_foreach_worktree(fixture
.repo
, foreach_worktree_cb
, &counter
));