summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2017-03-22 20:32:55 +0000
committerGitHub <noreply@github.com>2017-03-22 20:32:55 +0000
commitf623cf894a7e2591e7b4424b24143d1a7584cf9e (patch)
treeeff28ba6a1a91f51fb71a07969e688fcf1ecc8cd
parent6fd6c67824d2caad9f8716a676820d8319f79a9d (diff)
parentb0c9bc920fabfd814946d555738ac7ba042154d7 (diff)
downloadlibgit2-f623cf894a7e2591e7b4424b24143d1a7584cf9e.tar.gz
Merge pull request #4163 from pks-t/pks/submodules-with-worktrees
Worktree fixes
-rw-r--r--include/git2/worktree.h12
-rw-r--r--src/refdb_fs.c10
-rw-r--r--src/submodule.c24
-rw-r--r--src/worktree.c148
-rw-r--r--src/worktree.h2
-rw-r--r--tests/resources/submodules/testrepo/.gitted/config3
-rw-r--r--tests/worktree/open.c111
-rw-r--r--tests/worktree/refs.c26
-rw-r--r--tests/worktree/submodule.c92
-rw-r--r--tests/worktree/worktree.c7
10 files changed, 300 insertions, 135 deletions
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
@@ -44,6 +44,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
*
* @param wt worktree handle to close. If NULL nothing occurs.
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,33 +174,47 @@ 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) {
+out:
+ git_buf_free(&path);
+
+ if (error)
+ git_worktree_free(wt);
+
+ 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;
}
- 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;
+ gitdir = git_repository_path(repo);
+ commondir = git_repository_commondir(repo);
+
+ if ((error = git_path_prettify_dir(&parent, "..", commondir)) < 0)
goto out;
- }
- wt->gitdir_path = git_buf_detach(&path);
- wt->locked = !!git_worktree_is_locked(NULL, wt);
- (*out) = wt;
+ /* The name is defined by the last component in '.git/worktree/%s' */
+ name = git_path_basename(gitdir);
-out:
- git_buf_free(&path);
+ if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0)
+ goto out;
+out:
if (error)
- git_worktree_free(wt);
+ free(name);
+ git_buf_free(&parent);
return error;
}
@@ -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/<name>" */
+ 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(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
goto out;
- if ((err = git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
+ 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
--- a/tests/resources/submodules/testrepo/.gitted/config
+++ b/tests/resources/submodules/testrepo/.gitted/config
@@ -4,9 +4,6 @@
bare = false
logallrefupdates = true
ignorecase = true
-[remote "origin"]
- fetch = +refs/heads/*:refs/remotes/origin/*
- url = /Users/rb/src/libgit2/tests/resources/testrepo.git
[branch "master"]
remote = origin
merge = refs/heads/master
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__repository(void)
+void test_worktree_open__initialize(void)
{
- worktree_fixture fixture =
- WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
setup_fixture_worktree(&fixture);
+}
- assert_worktree_valid(fixture.worktree, COMMON_REPO, WORKTREE_REPO);
-
+void test_worktree_open__cleanup(void)
+{
cleanup_fixture_worktree(&fixture);
}
+void test_worktree_open__repository(void)
+{
+ assert_worktree_valid(fixture.worktree, COMMON_REPO, WORKTREE_REPO);
+}
+
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);
-
- cl_assert(git_repository_path(fixture.worktree) != NULL);
- cl_assert(git_repository_workdir(fixture.worktree) != NULL);
+ git_worktree *opened, *lookedup;
- cl_assert(!fixture.repo->is_worktree);
- cl_assert(fixture.worktree->is_worktree);
+ cl_git_pass(git_worktree_open_from_repository(&opened, fixture.worktree));
+ cl_git_pass(git_worktree_lookup(&lookedup, fixture.repo, WORKTREE_REPO));
- cleanup_fixture_worktree(&fixture);
-}
+ 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);
-void test_worktree_open__submodule_worktree_child(void)
-{
- worktree_fixture parent_fixture =
- WORKTREE_FIXTURE_INIT("submodules", WORKTREE_PARENT);
- worktree_fixture child_fixture =
- WORKTREE_FIXTURE_INIT(NULL, WORKTREE_CHILD);
-
- 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);
+ git_worktree_free(opened);
+ git_worktree_free(lookedup);
}
-void test_worktree_open__open_discovered_submodule_worktree(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_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_worktree *wt;
- 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/"));