summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/repository.h3
-rw-r--r--include/git2/submodule.h18
-rw-r--r--src/repository.c32
-rw-r--r--src/submodule.c106
-rw-r--r--tests/repo/init.c78
-rw-r--r--tests/submodule/add.c20
-rw-r--r--tests/submodule/repository_init.c40
7 files changed, 256 insertions, 41 deletions
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 18e515cb0..268782648 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -196,6 +196,8 @@ GIT_EXTERN(int) git_repository_init(
* looking the "template_path" from the options if set, or the
* `init.templatedir` global config if not, or falling back on
* "/usr/share/git-core/templates" if it exists.
+ * * GIT_REPOSITORY_INIT_RELATIVE_GITLINK - If an alternate workdir is
+ * specified, use relative paths for the gitdir and core.worktree.
*/
typedef enum {
GIT_REPOSITORY_INIT_BARE = (1u << 0),
@@ -204,6 +206,7 @@ typedef enum {
GIT_REPOSITORY_INIT_MKDIR = (1u << 3),
GIT_REPOSITORY_INIT_MKPATH = (1u << 4),
GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5),
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK = (1u << 6),
} git_repository_init_flag_t;
/**
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
index 864d1c58c..616890df6 100644
--- a/include/git2/submodule.h
+++ b/include/git2/submodule.h
@@ -471,6 +471,24 @@ GIT_EXTERN(git_submodule_recurse_t) git_submodule_set_fetch_recurse_submodules(
GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite);
/**
+ * Set up the subrepository for a submodule in preparation for clone.
+ *
+ * This function can be called to init and set up a submodule
+ * repository from a submodule in preparation to clone it from
+ * its remote.
+ *
+ * @param out Output pointer to the created git repository.
+ * @param sm The submodule to create a new subrepository from.
+ * @param use_gitlink Should the workdir contain a gitlink to
+ * the repo in .git/modules vs. repo directly in workdir.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_submodule_repo_init(
+ git_repository **out,
+ const git_submodule *sm,
+ int use_gitlink);
+
+/**
* Copy submodule remote info into submodule repo.
*
* This copies the information about the submodules URL into the checked out
diff --git a/src/repository.c b/src/repository.c
index 4f7a5feab..d86d8905a 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -994,7 +994,7 @@ static int repo_init_config(
uint32_t mode)
{
int error = 0;
- git_buf cfg_path = GIT_BUF_INIT;
+ git_buf cfg_path = GIT_BUF_INIT, worktree_path = GIT_BUF_INIT;
git_config *config = NULL;
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
@@ -1019,9 +1019,16 @@ static int repo_init_config(
if (!is_bare) {
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
- if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD))
- SET_REPO_CONFIG(string, "core.worktree", work_dir);
- else if (is_reinit) {
+ if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+ if ((error = git_buf_sets(&worktree_path, work_dir)) < 0)
+ goto cleanup;
+
+ if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK))
+ if ((error = git_path_make_relative(&worktree_path, repo_dir)) < 0)
+ goto cleanup;
+
+ SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr);
+ } else if (is_reinit) {
if (git_config_delete_entry(config, "core.worktree") < 0)
giterr_clear();
}
@@ -1038,6 +1045,7 @@ static int repo_init_config(
cleanup:
git_buf_free(&cfg_path);
+ git_buf_free(&worktree_path);
git_config_free(config);
return error;
@@ -1126,10 +1134,11 @@ static int repo_write_template(
}
static int repo_write_gitlink(
- const char *in_dir, const char *to_repo)
+ const char *in_dir, const char *to_repo, bool use_relative_path)
{
int error;
git_buf buf = GIT_BUF_INIT;
+ git_buf path_to_repo = GIT_BUF_INIT;
struct stat st;
git_path_dirname_r(&buf, to_repo);
@@ -1157,13 +1166,20 @@ static int repo_write_gitlink(
git_buf_clear(&buf);
- error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);
+ error = git_buf_sets(&path_to_repo, to_repo);
+
+ if (!error && use_relative_path)
+ error = git_path_make_relative(&path_to_repo, in_dir);
+
+ if (!error)
+ error = git_buf_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr);
if (!error)
error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
cleanup:
git_buf_free(&buf);
+ git_buf_free(&path_to_repo);
return error;
}
@@ -1207,7 +1223,7 @@ static int repo_init_structure(
if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
{
- if (repo_write_gitlink(work_dir, repo_dir) < 0)
+ if (repo_write_gitlink(work_dir, repo_dir, opts->flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK) < 0)
return -1;
}
@@ -1635,7 +1651,7 @@ int git_repository_set_workdir(
if (git_repository_config__weakptr(&config, repo) < 0)
return -1;
- error = repo_write_gitlink(path.ptr, git_repository_path(repo));
+ error = repo_write_gitlink(path.ptr, git_repository_path(repo), false);
/* passthrough error means gitlink is unnecessary */
if (error == GIT_PASSTHROUGH)
diff --git a/src/submodule.c b/src/submodule.c
index b1291df8e..ccc8ad117 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -306,6 +306,56 @@ void git_submodule_cache_free(git_repository *repo)
submodule_cache_free(cache);
}
+static int submodule_repo_init(
+ git_repository **out,
+ git_repository *parent_repo,
+ const char *path,
+ const char *url,
+ bool use_gitlink)
+{
+ int error = 0;
+ git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
+ git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_repository *subrepo = NULL;
+
+ error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT;
+ initopt.origin_url = url;
+
+ /* init submodule repository and add origin remote as needed */
+
+ /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
+ * gitlink in the sub-repo workdir directory to that repository
+ *
+ * Old style: sub-repo goes directly into repo/<name>/.git/
+ */
+ if (use_gitlink) {
+ error = git_buf_join3(
+ &repodir, '/', git_repository_path(parent_repo), "modules", path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.workdir_path = workdir.ptr;
+ initopt.flags |=
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
+
+ error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
+ } else
+ error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt);
+
+cleanup:
+ git_buf_free(&workdir);
+ git_buf_free(&repodir);
+
+ *out = subrepo;
+
+ return error;
+}
+
int git_submodule_add_setup(
git_submodule **out,
git_repository *repo,
@@ -317,7 +367,6 @@ int git_submodule_add_setup(
git_config_backend *mods = NULL;
git_submodule *sm = NULL;
git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
- git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
git_repository *subrepo = NULL;
assert(repo && url && path);
@@ -371,41 +420,14 @@ int git_submodule_add_setup(
if (error < 0)
goto cleanup;
- /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
- * gitlink in the sub-repo workdir directory to that repository
- *
- * Old style: sub-repo goes directly into repo/<name>/.git/
+ /* if the repo does not already exist, then init a new repo and add it.
+ * Otherwise, just add the existing repo.
*/
-
- initopt.flags = GIT_REPOSITORY_INIT_MKPATH |
- GIT_REPOSITORY_INIT_NO_REINIT;
- initopt.origin_url = real_url.ptr;
-
- if (git_path_exists(name.ptr) &&
- git_path_contains(&name, DOT_GIT))
- {
- /* repo appears to already exist - reinit? */
- }
- else if (use_gitlink) {
- git_buf repodir = GIT_BUF_INIT;
-
- error = git_buf_join3(
- &repodir, '/', git_repository_path(repo), "modules", path);
- if (error < 0)
+ if (!(git_path_exists(name.ptr) &&
+ git_path_contains(&name, DOT_GIT))) {
+ if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0)
goto cleanup;
-
- initopt.workdir_path = name.ptr;
- initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
-
- error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
-
- git_buf_free(&repodir);
}
- else {
- error = git_repository_init_ext(&subrepo, name.ptr, &initopt);
- }
- if (error < 0)
- goto cleanup;
/* add submodule to hash and "reload" it */
@@ -437,6 +459,23 @@ cleanup:
return error;
}
+int git_submodule_repo_init(
+ git_repository **out,
+ const git_submodule *sm,
+ int use_gitlink)
+{
+ int error;
+ git_repository *sub_repo = NULL;
+
+ assert(out && sm);
+
+ error = submodule_repo_init(&sub_repo, sm->repo, sm->path, sm->url, use_gitlink);
+
+ *out = sub_repo;
+
+ return error;
+}
+
int git_submodule_add_finalize(git_submodule *sm)
{
int error;
@@ -1897,6 +1936,7 @@ static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
*status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
}
+
static void submodule_get_wd_status(
unsigned int *status,
git_submodule *sm,
diff --git a/tests/repo/init.c b/tests/repo/init.c
index aea383c64..999afd625 100644
--- a/tests/repo/init.c
+++ b/tests/repo/init.c
@@ -367,6 +367,84 @@ void test_repo_init__extended_1(void)
cl_fixture_cleanup("root");
}
+void test_repo_init__relative_gitdir(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_config *cfg;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
+
+ opts.workdir_path = "../c_wd";
+ opts.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(git_repository_is_empty(_repo));
+
+ /* Verify that the gitlink and worktree entries are relative */
+
+ /* Verify worktree */
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../c_wd/", worktree_path);
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
+ cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
+
+ git_buf_free(&dot_git_content);
+ git_config_free(cfg);
+ cleanup_repository("root");
+}
+
+void test_repo_init__relative_gitdir_2(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_config *cfg;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
+ git_buf full_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_path_prettify(&full_path, ".", NULL));
+ cl_git_pass(git_buf_joinpath(&full_path, full_path.ptr, "root/b/c_wd"));
+
+ opts.workdir_path = full_path.ptr;
+ opts.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(git_repository_is_empty(_repo));
+
+ /* Verify that the gitlink and worktree entries are relative */
+
+ /* Verify worktree */
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../c_wd/", worktree_path);
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
+ cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
+
+ git_buf_free(&dot_git_content);
+ git_config_free(cfg);
+ cleanup_repository("root");
+}
+
#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177)
static void assert_hooks_match(
diff --git a/tests/submodule/add.c b/tests/submodule/add.c
index 9fdc7cc57..10717809e 100644
--- a/tests/submodule/add.c
+++ b/tests/submodule/add.c
@@ -2,6 +2,7 @@
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
+#include "fileops.h"
static git_repository *g_repo = NULL;
@@ -29,6 +30,10 @@ static void assert_submodule_url(const char* name, const char *url)
void test_submodule_add__url_absolute(void)
{
git_submodule *sm;
+ git_config *cfg;
+ git_repository *repo;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
g_repo = setup_fixture_submod2();
@@ -51,6 +56,21 @@ void test_submodule_add__url_absolute(void)
cl_assert(git_path_isfile("submod2/.git/modules/" "sm_libgit2" "/HEAD"));
assert_submodule_url("sm_libgit2", "https://github.com/libgit2/libgit2.git");
+ cl_git_pass(git_repository_open(&repo, "submod2/" "sm_libgit2"));
+
+ /* Verify worktree path is relative */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../../../sm_libgit2/", worktree_path);
+
+ /* Verify gitdir path is relative */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_libgit2" "/.git"));
+ cl_assert_equal_s("gitdir: ../.git/modules/sm_libgit2/", dot_git_content.ptr);
+
+ git_config_free(cfg);
+ git_repository_free(repo);
+ git_buf_free(&dot_git_content);
+
/* add a submodule not using a gitlink */
cl_git_pass(
diff --git a/tests/submodule/repository_init.c b/tests/submodule/repository_init.c
new file mode 100644
index 000000000..24b47aff8
--- /dev/null
+++ b/tests/submodule/repository_init.c
@@ -0,0 +1,40 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "fileops.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_repository_init__basic(void)
+{
+ git_submodule *sm;
+ git_repository *repo;
+ git_config *cfg;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
+
+ g_repo = setup_fixture_submod2();
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+ cl_git_pass(git_submodule_repo_init(&repo, sm, 1));
+
+ /* Verify worktree */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../../../sm_gitmodules_only/", worktree_path);
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_gitmodules_only" "/.git"));
+ cl_assert_equal_s("gitdir: ../.git/modules/sm_gitmodules_only/", dot_git_content.ptr);
+
+ cl_assert(git_path_isfile("submod2/" "sm_gitmodules_only" "/.git"));
+
+ cl_assert(git_path_isdir("submod2/.git/modules"));
+ cl_assert(git_path_isdir("submod2/.git/modules/" "sm_gitmodules_only"));
+ cl_assert(git_path_isfile("submod2/.git/modules/" "sm_gitmodules_only" "/HEAD"));
+
+ git_config_free(cfg);
+ git_repository_free(repo);
+ git_buf_free(&dot_git_content);
+}