mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-31 07:22:24 +00:00
repository: introduce commondir variable
The commondir variable stores the path to the common directory. The common directory is used to store objects and references shared across multiple repositories. A current use case is the newly introduced `git worktree` feature, which sets up a separate working copy, where the backing git object store and references are pointed to by the common directory.
This commit is contained in:
parent
807d57e7df
commit
c09fd54e2e
@ -392,6 +392,17 @@ GIT_EXTERN(const char *) git_repository_path(git_repository *repo);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
|
GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the path of the shared common directory for this repository
|
||||||
|
*
|
||||||
|
* If the repository is bare is not a worktree, the git directory
|
||||||
|
* path is returned.
|
||||||
|
*
|
||||||
|
* @param repo A repository object
|
||||||
|
* @return the path to the common dir
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const char *) git_repository_commondir(git_repository *repo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the path to the working directory for this repository
|
* Set the path to the working directory for this repository
|
||||||
*
|
*
|
||||||
|
@ -38,6 +38,8 @@ GIT__USE_STRMAP
|
|||||||
|
|
||||||
static int check_repositoryformatversion(git_config *config);
|
static int check_repositoryformatversion(git_config *config);
|
||||||
|
|
||||||
|
#define GIT_COMMONDIR_FILE "commondir"
|
||||||
|
|
||||||
#define GIT_FILE_CONTENT_PREFIX "gitdir:"
|
#define GIT_FILE_CONTENT_PREFIX "gitdir:"
|
||||||
|
|
||||||
#define GIT_BRANCH_MASTER "master"
|
#define GIT_BRANCH_MASTER "master"
|
||||||
@ -143,6 +145,7 @@ void git_repository_free(git_repository *repo)
|
|||||||
|
|
||||||
git__free(repo->path_gitlink);
|
git__free(repo->path_gitlink);
|
||||||
git__free(repo->path_repository);
|
git__free(repo->path_repository);
|
||||||
|
git__free(repo->commondir);
|
||||||
git__free(repo->workdir);
|
git__free(repo->workdir);
|
||||||
git__free(repo->namespace);
|
git__free(repo->namespace);
|
||||||
git__free(repo->ident_name);
|
git__free(repo->ident_name);
|
||||||
@ -157,17 +160,41 @@ void git_repository_free(git_repository *repo)
|
|||||||
*
|
*
|
||||||
* Open a repository object from its path
|
* Open a repository object from its path
|
||||||
*/
|
*/
|
||||||
static bool valid_repository_path(git_buf *repository_path)
|
static bool valid_repository_path(git_buf *repository_path, git_buf *common_path)
|
||||||
{
|
{
|
||||||
/* Check OBJECTS_DIR first, since it will generate the longest path name */
|
/* Check if we have a separate commondir (e.g. we have a
|
||||||
if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
|
* worktree) */
|
||||||
return false;
|
if (git_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) {
|
||||||
|
git_buf common_link = GIT_BUF_INIT;
|
||||||
|
git_buf_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE);
|
||||||
|
|
||||||
|
git_futils_readbuffer(&common_link, common_link.ptr);
|
||||||
|
git_buf_rtrim(&common_link);
|
||||||
|
|
||||||
|
if (git_path_is_relative(common_link.ptr)) {
|
||||||
|
git_buf_joinpath(common_path, repository_path->ptr, common_link.ptr);
|
||||||
|
} else {
|
||||||
|
git_buf_swap(common_path, &common_link);
|
||||||
|
}
|
||||||
|
|
||||||
|
git_buf_free(&common_link);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
git_buf_set(common_path, repository_path->ptr, repository_path->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure the commondir path always has a trailing * slash */
|
||||||
|
if (git_buf_rfind(common_path, '/') != (ssize_t)common_path->size - 1)
|
||||||
|
git_buf_putc(common_path, '/');
|
||||||
|
|
||||||
/* Ensure HEAD file exists */
|
/* Ensure HEAD file exists */
|
||||||
if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
|
if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (git_path_contains_dir(repository_path, GIT_REFS_DIR) == false)
|
/* Check files in common dir */
|
||||||
|
if (git_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false)
|
||||||
|
return false;
|
||||||
|
if (git_path_contains_dir(common_path, GIT_REFS_DIR) == false)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -356,6 +383,7 @@ static int find_repo(
|
|||||||
git_buf *repo_path,
|
git_buf *repo_path,
|
||||||
git_buf *parent_path,
|
git_buf *parent_path,
|
||||||
git_buf *link_path,
|
git_buf *link_path,
|
||||||
|
git_buf *common_path,
|
||||||
const char *start_path,
|
const char *start_path,
|
||||||
uint32_t flags,
|
uint32_t flags,
|
||||||
const char *ceiling_dirs)
|
const char *ceiling_dirs)
|
||||||
@ -363,6 +391,7 @@ static int find_repo(
|
|||||||
int error;
|
int error;
|
||||||
git_buf path = GIT_BUF_INIT;
|
git_buf path = GIT_BUF_INIT;
|
||||||
git_buf repo_link = GIT_BUF_INIT;
|
git_buf repo_link = GIT_BUF_INIT;
|
||||||
|
git_buf common_link = GIT_BUF_INIT;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
dev_t initial_device = 0;
|
dev_t initial_device = 0;
|
||||||
int min_iterations;
|
int min_iterations;
|
||||||
@ -409,9 +438,13 @@ static int find_repo(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (S_ISDIR(st.st_mode)) {
|
if (S_ISDIR(st.st_mode)) {
|
||||||
if (valid_repository_path(&path)) {
|
if (valid_repository_path(&path, &common_link)) {
|
||||||
git_path_to_dir(&path);
|
git_path_to_dir(&path);
|
||||||
git_buf_set(repo_path, path.ptr, path.size);
|
git_buf_set(repo_path, path.ptr, path.size);
|
||||||
|
|
||||||
|
if (common_path)
|
||||||
|
git_buf_swap(&common_link, common_path);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -419,11 +452,13 @@ static int find_repo(
|
|||||||
error = read_gitfile(&repo_link, path.ptr);
|
error = read_gitfile(&repo_link, path.ptr);
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
break;
|
break;
|
||||||
if (valid_repository_path(&repo_link)) {
|
if (valid_repository_path(&repo_link, &common_link)) {
|
||||||
git_buf_swap(repo_path, &repo_link);
|
git_buf_swap(repo_path, &repo_link);
|
||||||
|
|
||||||
if (link_path)
|
if (link_path)
|
||||||
error = git_buf_put(link_path, path.ptr, path.size);
|
error = git_buf_put(link_path, path.ptr, path.size);
|
||||||
|
if (common_path)
|
||||||
|
git_buf_swap(&common_link, common_path);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -470,6 +505,7 @@ static int find_repo(
|
|||||||
|
|
||||||
git_buf_free(&path);
|
git_buf_free(&path);
|
||||||
git_buf_free(&repo_link);
|
git_buf_free(&repo_link);
|
||||||
|
git_buf_free(&common_link);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,14 +514,15 @@ int git_repository_open_bare(
|
|||||||
const char *bare_path)
|
const char *bare_path)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
git_buf path = GIT_BUF_INIT;
|
git_buf path = GIT_BUF_INIT, common_path = GIT_BUF_INIT;
|
||||||
git_repository *repo = NULL;
|
git_repository *repo = NULL;
|
||||||
|
|
||||||
if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0)
|
if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (!valid_repository_path(&path)) {
|
if (!valid_repository_path(&path, &common_path)) {
|
||||||
git_buf_free(&path);
|
git_buf_free(&path);
|
||||||
|
git_buf_free(&common_path);
|
||||||
giterr_set(GITERR_REPOSITORY, "path is not a repository: %s", bare_path);
|
giterr_set(GITERR_REPOSITORY, "path is not a repository: %s", bare_path);
|
||||||
return GIT_ENOTFOUND;
|
return GIT_ENOTFOUND;
|
||||||
}
|
}
|
||||||
@ -495,6 +532,8 @@ int git_repository_open_bare(
|
|||||||
|
|
||||||
repo->path_repository = git_buf_detach(&path);
|
repo->path_repository = git_buf_detach(&path);
|
||||||
GITERR_CHECK_ALLOC(repo->path_repository);
|
GITERR_CHECK_ALLOC(repo->path_repository);
|
||||||
|
repo->commondir = git_buf_detach(&common_path);
|
||||||
|
GITERR_CHECK_ALLOC(repo->commondir);
|
||||||
|
|
||||||
/* of course we're bare! */
|
/* of course we're bare! */
|
||||||
repo->is_bare = 1;
|
repo->is_bare = 1;
|
||||||
@ -681,7 +720,7 @@ int git_repository_open_ext(
|
|||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT,
|
git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT,
|
||||||
link_path = GIT_BUF_INIT;
|
link_path = GIT_BUF_INIT, common_path = GIT_BUF_INIT;
|
||||||
git_repository *repo;
|
git_repository *repo;
|
||||||
git_config *config = NULL;
|
git_config *config = NULL;
|
||||||
|
|
||||||
@ -692,7 +731,7 @@ int git_repository_open_ext(
|
|||||||
*repo_ptr = NULL;
|
*repo_ptr = NULL;
|
||||||
|
|
||||||
error = find_repo(
|
error = find_repo(
|
||||||
&path, &parent, &link_path, start_path, flags, ceiling_dirs);
|
&path, &parent, &link_path, &common_path, start_path, flags, ceiling_dirs);
|
||||||
|
|
||||||
if (error < 0 || !repo_ptr)
|
if (error < 0 || !repo_ptr)
|
||||||
return error;
|
return error;
|
||||||
@ -707,6 +746,10 @@ int git_repository_open_ext(
|
|||||||
repo->path_gitlink = git_buf_detach(&link_path);
|
repo->path_gitlink = git_buf_detach(&link_path);
|
||||||
GITERR_CHECK_ALLOC(repo->path_gitlink);
|
GITERR_CHECK_ALLOC(repo->path_gitlink);
|
||||||
}
|
}
|
||||||
|
if (common_path.size) {
|
||||||
|
repo->commondir = git_buf_detach(&common_path);
|
||||||
|
GITERR_CHECK_ALLOC(repo->commondir);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We'd like to have the config, but git doesn't particularly
|
* We'd like to have the config, but git doesn't particularly
|
||||||
@ -773,7 +816,7 @@ int git_repository_discover(
|
|||||||
|
|
||||||
git_buf_sanitize(out);
|
git_buf_sanitize(out);
|
||||||
|
|
||||||
return find_repo(out, NULL, NULL, start_path, flags, ceiling_dirs);
|
return find_repo(out, NULL, NULL, NULL, start_path, flags, ceiling_dirs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_config(
|
static int load_config(
|
||||||
@ -928,7 +971,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
|
|||||||
git_buf odb_path = GIT_BUF_INIT;
|
git_buf odb_path = GIT_BUF_INIT;
|
||||||
git_odb *odb;
|
git_odb *odb;
|
||||||
|
|
||||||
if ((error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR)) < 0)
|
if ((error = git_buf_joinpath(&odb_path, repo->commondir, GIT_OBJECTS_DIR)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = git_odb_open(&odb, odb_path.ptr);
|
error = git_odb_open(&odb, odb_path.ptr);
|
||||||
@ -1856,7 +1899,8 @@ int git_repository_init_ext(
|
|||||||
git_repository_init_options *opts)
|
git_repository_init_options *opts)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT;
|
git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT,
|
||||||
|
common_path = GIT_BUF_INIT;
|
||||||
const char *wd;
|
const char *wd;
|
||||||
|
|
||||||
assert(out && given_repo && opts);
|
assert(out && given_repo && opts);
|
||||||
@ -1868,7 +1912,7 @@ int git_repository_init_ext(
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_buf_cstr(&wd_path);
|
wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_buf_cstr(&wd_path);
|
||||||
if (valid_repository_path(&repo_path)) {
|
if (valid_repository_path(&repo_path, &common_path)) {
|
||||||
|
|
||||||
if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
|
if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
|
||||||
giterr_set(GITERR_REPOSITORY,
|
giterr_set(GITERR_REPOSITORY,
|
||||||
@ -1901,6 +1945,7 @@ int git_repository_init_ext(
|
|||||||
error = repo_init_create_origin(*out, opts->origin_url);
|
error = repo_init_create_origin(*out, opts->origin_url);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
git_buf_free(&common_path);
|
||||||
git_buf_free(&repo_path);
|
git_buf_free(&repo_path);
|
||||||
git_buf_free(&wd_path);
|
git_buf_free(&wd_path);
|
||||||
|
|
||||||
@ -2023,6 +2068,12 @@ const char *git_repository_workdir(git_repository *repo)
|
|||||||
return repo->workdir;
|
return repo->workdir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *git_repository_commondir(git_repository *repo)
|
||||||
|
{
|
||||||
|
assert(repo);
|
||||||
|
return repo->commondir;
|
||||||
|
}
|
||||||
|
|
||||||
int git_repository_set_workdir(
|
int git_repository_set_workdir(
|
||||||
git_repository *repo, const char *workdir, int update_gitlink)
|
git_repository *repo, const char *workdir, int update_gitlink)
|
||||||
{
|
{
|
||||||
|
@ -128,6 +128,7 @@ struct git_repository {
|
|||||||
|
|
||||||
char *path_repository;
|
char *path_repository;
|
||||||
char *path_gitlink;
|
char *path_gitlink;
|
||||||
|
char *commondir;
|
||||||
char *workdir;
|
char *workdir;
|
||||||
char *namespace;
|
char *namespace;
|
||||||
|
|
||||||
|
60
tests/worktree/open.c
Normal file
60
tests/worktree/open.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#include "clar_libgit2.h"
|
||||||
|
#include "worktree_helpers.h"
|
||||||
|
|
||||||
|
#define WORKTREE_PARENT "submodules-worktree-parent"
|
||||||
|
#define WORKTREE_CHILD "submodules-worktree-child"
|
||||||
|
|
||||||
|
void test_worktree_open__repository(void)
|
||||||
|
{
|
||||||
|
worktree_fixture fixture =
|
||||||
|
WORKTREE_FIXTURE_INIT("testrepo", "testrepo-worktree");
|
||||||
|
setup_fixture_worktree(&fixture);
|
||||||
|
|
||||||
|
cl_assert(git_repository_path(fixture.worktree) != NULL);
|
||||||
|
cl_assert(git_repository_workdir(fixture.worktree) != NULL);
|
||||||
|
|
||||||
|
cleanup_fixture_worktree(&fixture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_worktree_open__repository_with_nonexistent_parent(void)
|
||||||
|
{
|
||||||
|
git_repository *repo;
|
||||||
|
|
||||||
|
cl_fixture_sandbox("testrepo-worktree");
|
||||||
|
cl_git_pass(p_chdir("testrepo-worktree"));
|
||||||
|
cl_git_pass(cl_rename(".gitted", ".git"));
|
||||||
|
cl_git_pass(p_chdir(".."));
|
||||||
|
|
||||||
|
cl_git_fail(git_repository_open(&repo, "testrepo-worktree"));
|
||||||
|
|
||||||
|
cl_fixture_cleanup("testrepo-worktree");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_worktree_open__submodule_worktree_parent(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);
|
||||||
|
|
||||||
|
cleanup_fixture_worktree(&fixture);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
cleanup_fixture_worktree(&child_fixture);
|
||||||
|
cleanup_fixture_worktree(&parent_fixture);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user