diff --git a/include/git2/worktree.h b/include/git2/worktree.h index cad1284fa..4c4f9284d 100644 --- a/include/git2/worktree.h +++ b/include/git2/worktree.h @@ -43,6 +43,18 @@ GIT_EXTERN(int) git_worktree_list(git_strarray *out, git_repository *repo); */ GIT_EXTERN(int) git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name); +/** + * Open a worktree of a given repository + * + * If a repository is not the main tree but a worktree, this + * function will look up the worktree inside the parent + * repository and create a new `git_worktree` structure. + * + * @param out Out-pointer for the newly allocated worktree + * @param repo Repository to look up worktree for + */ +GIT_EXTERN(int) git_worktree_open_from_repository(git_worktree **out, git_repository *repo); + /** * Free a previously allocated worktree * diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 450b3f3ec..b325d2763 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -739,6 +739,7 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char * { int error, filebuf_flags; git_buf ref_path = GIT_BUF_INIT; + const char *basedir; assert(file && backend && name); @@ -747,13 +748,18 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char * return GIT_EINVALIDSPEC; } + if (is_per_worktree_ref(name)) + basedir = backend->gitpath; + else + basedir = backend->commonpath; + /* Remove a possibly existing empty directory hierarchy * which name would collide with the reference name */ - if ((error = git_futils_rmdir_r(name, backend->gitpath, GIT_RMDIR_SKIP_NONEMPTY)) < 0) + if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0) return error; - if (git_buf_joinpath(&ref_path, backend->gitpath, name) < 0) + if (git_buf_joinpath(&ref_path, basedir, name) < 0) return -1; filebuf_flags = GIT_FILEBUF_FORCE; diff --git a/src/submodule.c b/src/submodule.c index 191cdf3dd..ddd4b0663 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -22,6 +22,7 @@ #include "iterator.h" #include "path.h" #include "index.h" +#include "worktree.h" #define GIT_MODULES_FILE ".gitmodules" @@ -2038,17 +2039,28 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo) static int get_url_base(git_buf *url, git_repository *repo) { int error; + git_worktree *wt = NULL; git_remote *remote = NULL; - if (!(error = lookup_default_remote(&remote, repo))) { + if ((error = lookup_default_remote(&remote, repo)) == 0) { error = git_buf_sets(url, git_remote_url(remote)); - git_remote_free(remote); - } - else if (error == GIT_ENOTFOUND) { - /* if repository does not have a default remote, use workdir instead */ + goto out; + } else if (error != GIT_ENOTFOUND) + goto out; + else giterr_clear(); + + /* if repository does not have a default remote, use workdir instead */ + if (git_repository_is_worktree(repo)) { + if ((error = git_worktree_open_from_repository(&wt, repo)) < 0) + goto out; + error = git_buf_sets(url, wt->parent_path); + } else error = git_buf_sets(url, git_repository_workdir(repo)); - } + +out: + git_remote_free(remote); + git_worktree_free(wt); return error; } diff --git a/src/worktree.c b/src/worktree.c index 5abc98945..393a088fe 100644 --- a/src/worktree.c +++ b/src/worktree.c @@ -14,11 +14,20 @@ #include "repository.h" #include "worktree.h" -static bool is_worktree_dir(git_buf *dir) +static bool is_worktree_dir(const char *dir) { - return git_path_contains_file(dir, "commondir") - && git_path_contains_file(dir, "gitdir") - && git_path_contains_file(dir, "HEAD"); + git_buf buf = GIT_BUF_INIT; + int error; + + if (git_buf_sets(&buf, dir) < 0) + return -1; + + error = git_path_contains_file(&buf, "commondir") + && git_path_contains_file(&buf, "gitdir") + && git_path_contains_file(&buf, "HEAD"); + + git_buf_free(&buf); + return error; } int git_worktree_list(git_strarray *wts, git_repository *repo) @@ -47,7 +56,7 @@ int git_worktree_list(git_strarray *wts, git_repository *repo) git_buf_truncate(&path, len); git_buf_puts(&path, worktree); - if (!is_worktree_dir(&path)) { + if (!is_worktree_dir(path.ptr)) { git_vector_remove(&worktrees, i); git__free(worktree); } @@ -112,6 +121,46 @@ out: return err; } +static int open_worktree_dir(git_worktree **out, const char *parent, const char *dir, const char *name) +{ + git_buf gitdir = GIT_BUF_INIT; + git_worktree *wt = NULL; + int error = 0; + + if (!is_worktree_dir(dir)) { + error = -1; + goto out; + } + + if ((wt = git__calloc(1, sizeof(struct git_repository))) == NULL) { + error = -1; + goto out; + } + + if ((wt->name = git__strdup(name)) == NULL + || (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL + || (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL + || (wt->parent_path = git__strdup(parent)) == NULL) { + error = -1; + goto out; + } + + if ((error = git_path_prettify_dir(&gitdir, dir, NULL)) < 0) + goto out; + wt->gitdir_path = git_buf_detach(&gitdir); + + wt->locked = !!git_worktree_is_locked(NULL, wt); + + *out = wt; + +out: + if (error) + git_worktree_free(wt); + git_buf_free(&gitdir); + + return error; +} + int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name) { git_buf path = GIT_BUF_INIT; @@ -125,27 +174,8 @@ int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *na if ((error = git_buf_printf(&path, "%s/worktrees/%s", repo->commondir, name)) < 0) goto out; - if (!is_worktree_dir(&path)) { - error = -1; + if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0) goto out; - } - - if ((wt = git__malloc(sizeof(struct git_repository))) == NULL) { - error = -1; - goto out; - } - - if ((wt->name = git__strdup(name)) == NULL - || (wt->commondir_path = git_worktree__read_link(path.ptr, "commondir")) == NULL - || (wt->gitlink_path = git_worktree__read_link(path.ptr, "gitdir")) == NULL - || (wt->parent_path = git__strdup(git_repository_path(repo))) == NULL) { - error = -1; - goto out; - } - wt->gitdir_path = git_buf_detach(&path); - wt->locked = !!git_worktree_is_locked(NULL, wt); - - (*out) = wt; out: git_buf_free(&path); @@ -156,6 +186,39 @@ out: return error; } +int git_worktree_open_from_repository(git_worktree **out, git_repository *repo) +{ + git_buf parent = GIT_BUF_INIT; + const char *gitdir, *commondir; + char *name = NULL; + int error = 0; + + if (!git_repository_is_worktree(repo)) { + giterr_set(GITERR_WORKTREE, "cannot open worktree of a non-worktree repo"); + error = -1; + goto out; + } + + gitdir = git_repository_path(repo); + commondir = git_repository_commondir(repo); + + if ((error = git_path_prettify_dir(&parent, "..", commondir)) < 0) + goto out; + + /* The name is defined by the last component in '.git/worktree/%s' */ + name = git_path_basename(gitdir); + + if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0) + goto out; + +out: + if (error) + free(name); + git_buf_free(&parent); + + return error; +} + void git_worktree_free(git_worktree *wt) { if (!wt) @@ -177,7 +240,7 @@ int git_worktree_validate(const git_worktree *wt) assert(wt); git_buf_puts(&buf, wt->gitdir_path); - if (!is_worktree_dir(&buf)) { + if (!is_worktree_dir(buf.ptr)) { giterr_set(GITERR_WORKTREE, "Worktree gitdir ('%s') is not valid", wt->gitlink_path); @@ -209,7 +272,7 @@ out: int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *worktree) { - git_buf path = GIT_BUF_INIT, buf = GIT_BUF_INIT; + git_buf gitdir = GIT_BUF_INIT, wddir = GIT_BUF_INIT, buf = GIT_BUF_INIT; git_reference *ref = NULL, *head = NULL; git_commit *commit = NULL; git_repository *wt = NULL; @@ -220,35 +283,39 @@ int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, *out = NULL; - /* Create worktree related files in commondir */ - if ((err = git_buf_joinpath(&path, repo->commondir, "worktrees")) < 0) + /* Create gitdir directory ".git/worktrees/" */ + if ((err = git_buf_joinpath(&gitdir, repo->commondir, "worktrees")) < 0) goto out; - if (!git_path_exists(path.ptr)) - if ((err = git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_EXCL)) < 0) + if (!git_path_exists(gitdir.ptr)) + if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) goto out; - if ((err = git_buf_joinpath(&path, path.ptr, name)) < 0) + if ((err = git_buf_joinpath(&gitdir, gitdir.ptr, name)) < 0) goto out; - if ((err = git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_EXCL)) < 0) + if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0) + goto out; + if ((err = git_path_prettify_dir(&gitdir, gitdir.ptr, NULL)) < 0) goto out; /* Create worktree work dir */ if ((err = git_futils_mkdir(worktree, 0755, GIT_MKDIR_EXCL)) < 0) goto out; + if ((err = git_path_prettify_dir(&wddir, worktree, NULL)) < 0) + goto out; /* Create worktree .git file */ - if ((err = git_buf_printf(&buf, "gitdir: %s\n", path.ptr)) < 0) + if ((err = git_buf_printf(&buf, "gitdir: %s\n", gitdir.ptr)) < 0) goto out; - if ((err = write_wtfile(worktree, ".git", &buf)) < 0) + if ((err = write_wtfile(wddir.ptr, ".git", &buf)) < 0) goto out; - /* Create commondir files */ - if ((err = git_buf_sets(&buf, repo->commondir)) < 0 + /* Create gitdir files */ + if ((err = git_path_prettify_dir(&buf, repo->commondir, NULL) < 0) || (err = git_buf_putc(&buf, '\n')) < 0 - || (err = write_wtfile(path.ptr, "commondir", &buf)) < 0) + || (err = write_wtfile(gitdir.ptr, "commondir", &buf)) < 0) goto out; - if ((err = git_buf_joinpath(&buf, worktree, ".git")) < 0 + if ((err = git_buf_joinpath(&buf, wddir.ptr, ".git")) < 0 || (err = git_buf_putc(&buf, '\n')) < 0 - || (err = write_wtfile(path.ptr, "gitdir", &buf)) < 0) + || (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0) goto out; /* Create new branch */ @@ -260,9 +327,9 @@ int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, goto out; /* Set worktree's HEAD */ - if ((err = git_repository_create_head(path.ptr, name)) < 0) + if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0) goto out; - if ((err = git_repository_open(&wt, worktree)) < 0) + if ((err = git_repository_open(&wt, wddir.ptr)) < 0) goto out; /* Checkout worktree's HEAD */ @@ -275,7 +342,8 @@ int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, goto out; out: - git_buf_free(&path); + git_buf_free(&gitdir); + git_buf_free(&wddir); git_buf_free(&buf); git_reference_free(ref); git_reference_free(head); @@ -394,7 +462,7 @@ int git_worktree_prune(git_worktree *wt, unsigned flags) } /* Delete gitdir in parent repository */ - if ((err = git_buf_printf(&path, "%s/worktrees/%s", wt->parent_path, wt->name)) < 0) + if ((err = git_buf_printf(&path, "%s/worktrees/%s", wt->commondir_path, wt->name)) < 0) goto out; if (!git_path_exists(path.ptr)) { diff --git a/src/worktree.h b/src/worktree.h index b8e527968..57c2e65f0 100644 --- a/src/worktree.h +++ b/src/worktree.h @@ -24,7 +24,7 @@ struct git_worktree { /* Path to the common directory contained in the parent * repository */ char *commondir_path; - /* Path to the parent's .git directory */ + /* Path to the parent's working directory */ char *parent_path; int locked:1; diff --git a/tests/resources/submodules/testrepo/.gitted/config b/tests/resources/submodules/testrepo/.gitted/config index d6dcad12b..8e5571191 100644 Binary files a/tests/resources/submodules/testrepo/.gitted/config and b/tests/resources/submodules/testrepo/.gitted/config differ diff --git a/tests/worktree/open.c b/tests/worktree/open.c index bdc8bcf9d..74b9007d9 100644 --- a/tests/worktree/open.c +++ b/tests/worktree/open.c @@ -1,13 +1,14 @@ #include "clar_libgit2.h" #include "repository.h" +#include "worktree.h" #include "worktree_helpers.h" -#define WORKTREE_PARENT "submodules-worktree-parent" -#define WORKTREE_CHILD "submodules-worktree-child" - #define COMMON_REPO "testrepo" #define WORKTREE_REPO "testrepo-worktree" +static worktree_fixture fixture = + WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); + static void assert_worktree_valid(git_repository *wt, const char *parentdir, const char *wtdir) { git_buf path = GIT_BUF_INIT; @@ -34,56 +35,46 @@ static void assert_worktree_valid(git_repository *wt, const char *parentdir, con git_buf_free(&path); } +void test_worktree_open__initialize(void) +{ + setup_fixture_worktree(&fixture); +} + +void test_worktree_open__cleanup(void) +{ + cleanup_fixture_worktree(&fixture); +} + void test_worktree_open__repository(void) { - worktree_fixture fixture = - WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); - setup_fixture_worktree(&fixture); - assert_worktree_valid(fixture.worktree, COMMON_REPO, WORKTREE_REPO); - - cleanup_fixture_worktree(&fixture); } void test_worktree_open__repository_through_workdir(void) { - worktree_fixture fixture = - WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); git_repository *wt; - setup_fixture_worktree(&fixture); - cl_git_pass(git_repository_open(&wt, WORKTREE_REPO)); assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO); git_repository_free(wt); - cleanup_fixture_worktree(&fixture); } void test_worktree_open__repository_through_gitlink(void) { - worktree_fixture fixture = - WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); git_repository *wt; - setup_fixture_worktree(&fixture); - cl_git_pass(git_repository_open(&wt, WORKTREE_REPO "/.git")); assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO); git_repository_free(wt); - cleanup_fixture_worktree(&fixture); } void test_worktree_open__repository_through_gitdir(void) { - worktree_fixture fixture = - WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); git_buf gitdir_path = GIT_BUF_INIT; git_repository *wt; - setup_fixture_worktree(&fixture); - cl_git_pass(git_buf_joinpath(&gitdir_path, COMMON_REPO, ".git")); cl_git_pass(git_buf_joinpath(&gitdir_path, gitdir_path.ptr, "worktrees")); cl_git_pass(git_buf_joinpath(&gitdir_path, gitdir_path.ptr, "testrepo-worktree")); @@ -93,18 +84,13 @@ void test_worktree_open__repository_through_gitdir(void) git_buf_free(&gitdir_path); git_repository_free(wt); - cleanup_fixture_worktree(&fixture); } void test_worktree_open__open_discovered_worktree(void) { - worktree_fixture fixture = - WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO); git_buf path = GIT_BUF_INIT; git_repository *repo; - setup_fixture_worktree(&fixture); - cl_git_pass(git_repository_discover(&path, git_repository_workdir(fixture.worktree), false, NULL)); cl_git_pass(git_repository_open(&repo, path.ptr)); @@ -113,13 +99,14 @@ void test_worktree_open__open_discovered_worktree(void) git_buf_free(&path); git_repository_free(repo); - cleanup_fixture_worktree(&fixture); } void test_worktree_open__repository_with_nonexistent_parent(void) { git_repository *repo; + cleanup_fixture_worktree(&fixture); + cl_fixture_sandbox(WORKTREE_REPO); cl_git_pass(p_chdir(WORKTREE_REPO)); cl_git_pass(cl_rename(".gitted", ".git")); @@ -130,65 +117,27 @@ void test_worktree_open__repository_with_nonexistent_parent(void) cl_fixture_cleanup(WORKTREE_REPO); } -void test_worktree_open__submodule_worktree_parent(void) +void test_worktree_open__open_from_repository(void) { - worktree_fixture fixture = - WORKTREE_FIXTURE_INIT("submodules", WORKTREE_PARENT); - setup_fixture_worktree(&fixture); + git_worktree *opened, *lookedup; - cl_assert(git_repository_path(fixture.worktree) != NULL); - cl_assert(git_repository_workdir(fixture.worktree) != NULL); + cl_git_pass(git_worktree_open_from_repository(&opened, fixture.worktree)); + cl_git_pass(git_worktree_lookup(&lookedup, fixture.repo, WORKTREE_REPO)); - cl_assert(!fixture.repo->is_worktree); - cl_assert(fixture.worktree->is_worktree); + cl_assert_equal_s(opened->name, lookedup->name); + cl_assert_equal_s(opened->gitdir_path, lookedup->gitdir_path); + cl_assert_equal_s(opened->gitlink_path, lookedup->gitlink_path); + cl_assert_equal_s(opened->parent_path, lookedup->parent_path); + cl_assert_equal_s(opened->commondir_path, lookedup->commondir_path); + cl_assert_equal_i(opened->locked, lookedup->locked); - cleanup_fixture_worktree(&fixture); + git_worktree_free(opened); + git_worktree_free(lookedup); } -void test_worktree_open__submodule_worktree_child(void) +void test_worktree_open__open_from_nonworktree_fails(void) { - worktree_fixture parent_fixture = - WORKTREE_FIXTURE_INIT("submodules", WORKTREE_PARENT); - worktree_fixture child_fixture = - WORKTREE_FIXTURE_INIT(NULL, WORKTREE_CHILD); + git_worktree *wt; - setup_fixture_worktree(&parent_fixture); - cl_git_pass(p_rename( - "submodules/testrepo/.gitted", - "submodules/testrepo/.git")); - setup_fixture_worktree(&child_fixture); - - cl_assert(!parent_fixture.repo->is_worktree); - cl_assert(parent_fixture.worktree->is_worktree); - cl_assert(child_fixture.worktree->is_worktree); - - cleanup_fixture_worktree(&child_fixture); - cleanup_fixture_worktree(&parent_fixture); -} - -void test_worktree_open__open_discovered_submodule_worktree(void) -{ - worktree_fixture parent_fixture = - WORKTREE_FIXTURE_INIT("submodules", WORKTREE_PARENT); - worktree_fixture child_fixture = - WORKTREE_FIXTURE_INIT(NULL, WORKTREE_CHILD); - git_buf path = GIT_BUF_INIT; - git_repository *repo; - - setup_fixture_worktree(&parent_fixture); - cl_git_pass(p_rename( - "submodules/testrepo/.gitted", - "submodules/testrepo/.git")); - setup_fixture_worktree(&child_fixture); - - cl_git_pass(git_repository_discover(&path, - git_repository_workdir(child_fixture.worktree), false, NULL)); - cl_git_pass(git_repository_open(&repo, path.ptr)); - cl_assert_equal_s(git_repository_workdir(child_fixture.worktree), - git_repository_workdir(repo)); - - git_buf_free(&path); - git_repository_free(repo); - cleanup_fixture_worktree(&child_fixture); - cleanup_fixture_worktree(&parent_fixture); + cl_git_fail(git_worktree_open_from_repository(&wt, fixture.repo)); } diff --git a/tests/worktree/refs.c b/tests/worktree/refs.c index ccac8be29..b9a05606d 100644 --- a/tests/worktree/refs.c +++ b/tests/worktree/refs.c @@ -1,4 +1,6 @@ #include "clar_libgit2.h" +#include "path.h" +#include "refs.h" #include "worktree.h" #include "worktree_helpers.h" @@ -128,3 +130,27 @@ void test_worktree_refs__delete_succeeds_after_pruning_worktree(void) cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); } + +void test_worktree_refs__creating_refs_uses_commondir(void) +{ + git_reference *head, *branch, *lookup; + git_commit *commit; + git_buf refpath = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&refpath, + git_repository_commondir(fixture.worktree), "refs/heads/testbranch")); + cl_assert(!git_path_exists(refpath.ptr)); + + cl_git_pass(git_repository_head(&head, fixture.worktree)); + cl_git_pass(git_commit_lookup(&commit, fixture.worktree, git_reference_target(head))); + cl_git_pass(git_branch_create(&branch, fixture.worktree, "testbranch", commit, 0)); + cl_git_pass(git_branch_lookup(&lookup, fixture.worktree, "testbranch", GIT_BRANCH_LOCAL)); + cl_assert(git_reference_cmp(branch, lookup) == 0); + cl_assert(git_path_exists(refpath.ptr)); + + git_reference_free(lookup); + git_reference_free(branch); + git_reference_free(head); + git_commit_free(commit); + git_buf_free(&refpath); +} diff --git a/tests/worktree/submodule.c b/tests/worktree/submodule.c new file mode 100644 index 000000000..562077597 --- /dev/null +++ b/tests/worktree/submodule.c @@ -0,0 +1,92 @@ +#include "clar_libgit2.h" +#include "repository.h" +#include "worktree.h" +#include "worktree_helpers.h" + +#define WORKTREE_PARENT "submodules-worktree-parent" +#define WORKTREE_CHILD "submodules-worktree-child" + +static worktree_fixture parent + = WORKTREE_FIXTURE_INIT("submodules", WORKTREE_PARENT); +static worktree_fixture child + = WORKTREE_FIXTURE_INIT(NULL, WORKTREE_CHILD); + +void test_worktree_submodule__initialize(void) +{ + setup_fixture_worktree(&parent); + + cl_git_pass(p_rename( + "submodules/testrepo/.gitted", + "submodules/testrepo/.git")); + + setup_fixture_worktree(&child); +} + +void test_worktree_submodule__cleanup(void) +{ + cleanup_fixture_worktree(&child); + cleanup_fixture_worktree(&parent); +} + +void test_worktree_submodule__submodule_worktree_parent(void) +{ + cl_assert(git_repository_path(parent.worktree) != NULL); + cl_assert(git_repository_workdir(parent.worktree) != NULL); + + cl_assert(!parent.repo->is_worktree); + cl_assert(parent.worktree->is_worktree); +} + +void test_worktree_submodule__submodule_worktree_child(void) +{ + cl_assert(!parent.repo->is_worktree); + cl_assert(parent.worktree->is_worktree); + cl_assert(child.worktree->is_worktree); +} + +void test_worktree_submodule__open_discovered_submodule_worktree(void) +{ + git_buf path = GIT_BUF_INIT; + git_repository *repo; + + cl_git_pass(git_repository_discover(&path, + git_repository_workdir(child.worktree), false, NULL)); + cl_git_pass(git_repository_open(&repo, path.ptr)); + cl_assert_equal_s(git_repository_workdir(child.worktree), + git_repository_workdir(repo)); + + git_buf_free(&path); + git_repository_free(repo); +} + +void test_worktree_submodule__resolve_relative_url(void) +{ + git_buf wt_path = GIT_BUF_INIT; + git_buf sm_relative_path = GIT_BUF_INIT, wt_relative_path = GIT_BUF_INIT; + git_repository *repo; + git_worktree *wt; + + cl_git_pass(git_futils_mkdir("subdir", 0755, GIT_MKDIR_PATH)); + cl_git_pass(git_path_prettify_dir(&wt_path, "subdir", NULL)); + cl_git_pass(git_buf_joinpath(&wt_path, wt_path.ptr, "wt")); + + /* Open child repository, which is a submodule */ + cl_git_pass(git_repository_open(&child.repo, WORKTREE_CHILD)); + + /* Create worktree of submodule repository */ + cl_git_pass(git_worktree_add(&wt, child.repo, "subdir", wt_path.ptr)); + cl_git_pass(git_repository_open_from_worktree(&repo, wt)); + + cl_git_pass(git_submodule_resolve_url(&sm_relative_path, repo, + "../" WORKTREE_CHILD)); + cl_git_pass(git_submodule_resolve_url(&wt_relative_path, child.repo, + "../" WORKTREE_CHILD)); + + cl_assert_equal_s(sm_relative_path.ptr, wt_relative_path.ptr); + + git_worktree_free(wt); + git_repository_free(repo); + git_buf_free(&wt_path); + git_buf_free(&sm_relative_path); + git_buf_free(&wt_relative_path); +} diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c index f0c423599..6e90e6ac0 100644 --- a/tests/worktree/worktree.c +++ b/tests/worktree/worktree.c @@ -115,11 +115,12 @@ void test_worktree_worktree__lookup(void) cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); - git_buf_printf(&gitdir_path, "%s/worktrees/%s", fixture.repo->commondir, "testrepo-worktree"); + cl_git_pass(git_buf_joinpath(&gitdir_path, fixture.repo->commondir, "worktrees/testrepo-worktree/")); cl_assert_equal_s(wt->gitdir_path, gitdir_path.ptr); - cl_assert_equal_s(wt->parent_path, fixture.repo->gitdir); + cl_assert_equal_s(wt->parent_path, fixture.repo->workdir); cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink); + cl_assert_equal_s(wt->commondir_path, fixture.repo->gitdir); cl_assert_equal_s(wt->commondir_path, fixture.repo->commondir); git_buf_free(&gitdir_path); @@ -305,7 +306,9 @@ void test_worktree_worktree__init_submodule(void) cl_git_pass(git_worktree_add(&worktree, sm, "repo-worktree", path.ptr)); cl_git_pass(git_repository_open_from_worktree(&wt, worktree)); + cl_git_pass(git_path_prettify_dir(&path, path.ptr, NULL)); cl_assert_equal_s(path.ptr, wt->workdir); + cl_git_pass(git_path_prettify_dir(&path, sm->commondir, NULL)); cl_assert_equal_s(sm->commondir, wt->commondir); cl_git_pass(git_buf_joinpath(&path, sm->gitdir, "worktrees/repo-worktree/"));