mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-29 05:44:08 +00:00
Introduce option to use relative paths for repository work directory
Teach git_repository_init_ext to use relative paths for the gitlink to the work directory. This is used when creating a sub repository where the sub repository resides in the parent repository's .git directory.
This commit is contained in:
parent
0ee9f31c3b
commit
bc737620dd
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -470,6 +470,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.
|
||||
*
|
||||
|
@ -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)
|
||||
|
106
src/submodule.c
106
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,
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
40
tests/submodule/repository_init.c
Normal file
40
tests/submodule/repository_init.c
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user