submodule: resolve URLs relative to main worktree

It is possible to specify submodule URLs relative to the repository
location. E.g. having a submodule with URL "../submodule" will look for
the submodule at "repo/../submodule".

With the introduction of worktrees, though, we cannot simply resolve the
URL relative to the repository location itself. If the repository for
which a URL is to be resolved is a working tree, we have to resolve the
URL relative to the parent's repository path. Otherwise, the URL would
change depending on where the working tree is located.

Fix this by special-casing when we have a working tree while getting the
URL base.
This commit is contained in:
Patrick Steinhardt 2017-03-15 13:38:54 +01:00
parent 097f0105b4
commit b0c9bc920f
3 changed files with 51 additions and 6 deletions

View File

@ -22,6 +22,7 @@
#include "iterator.h" #include "iterator.h"
#include "path.h" #include "path.h"
#include "index.h" #include "index.h"
#include "worktree.h"
#define GIT_MODULES_FILE ".gitmodules" #define GIT_MODULES_FILE ".gitmodules"
@ -2030,17 +2031,28 @@ static int lookup_default_remote(git_remote **remote, git_repository *repo)
static int get_url_base(git_buf *url, git_repository *repo) static int get_url_base(git_buf *url, git_repository *repo)
{ {
int error; int error;
git_worktree *wt = NULL;
git_remote *remote = 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)); error = git_buf_sets(url, git_remote_url(remote));
git_remote_free(remote); goto out;
} } else if (error != GIT_ENOTFOUND)
else if (error == GIT_ENOTFOUND) { goto out;
/* if repository does not have a default remote, use workdir instead */ else
giterr_clear(); 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)); error = git_buf_sets(url, git_repository_workdir(repo));
}
out:
git_remote_free(remote);
git_worktree_free(wt);
return error; return error;
} }

View File

@ -1,5 +1,6 @@
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "repository.h" #include "repository.h"
#include "worktree.h"
#include "worktree_helpers.h" #include "worktree_helpers.h"
#define WORKTREE_PARENT "submodules-worktree-parent" #define WORKTREE_PARENT "submodules-worktree-parent"
@ -57,3 +58,35 @@ void test_worktree_submodule__open_discovered_submodule_worktree(void)
git_buf_free(&path); git_buf_free(&path);
git_repository_free(repo); 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);
}