mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
Merge pull request #7102 from pks-gitlab/pks-refformat-extension
Introduction of the "refFormat" extension
This commit is contained in:
@@ -21,6 +21,11 @@
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/** The type of the refdb as determined by "extensions.refStorage". */
|
||||
typedef enum {
|
||||
GIT_REFDB_FILES = 1 /**< Files backend using loose and packed refs. */
|
||||
} git_refdb_t;
|
||||
|
||||
/**
|
||||
* Create a new reference database with no backends.
|
||||
*
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "odb.h"
|
||||
#include "refdb.h"
|
||||
#include "buffer.h"
|
||||
#include "commit.h"
|
||||
|
||||
@@ -377,6 +378,12 @@ typedef struct {
|
||||
*/
|
||||
git_oid_t oid_type;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Type of the reference database to use for this repository, or 0 for
|
||||
* default (currently "files").
|
||||
*/
|
||||
git_refdb_t refdb_type;
|
||||
} git_repository_init_options;
|
||||
|
||||
/** Current version for the `git_repository_init_options` structure */
|
||||
|
||||
@@ -56,10 +56,48 @@ struct git_reference_iterator {
|
||||
git_reference_iterator *iter);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
/**
|
||||
* The refdb that is to be initialized is for a worktree.
|
||||
*/
|
||||
GIT_REFDB_BACKEND_INIT_IS_WORKTREE = (1u << 0),
|
||||
|
||||
/*
|
||||
* Force-overwrite HEAD in case the refdb is already (partially)
|
||||
* initialized.
|
||||
*/
|
||||
GIT_REFDB_BACKEND_INIT_FORCE_HEAD = (1u << 1)
|
||||
} git_refdb_backend_init_flag_t;
|
||||
|
||||
/** An instance for a custom backend */
|
||||
struct git_refdb_backend {
|
||||
unsigned int version; /**< The backend API version */
|
||||
|
||||
/**
|
||||
* Create a new refdb and initialize its structures.
|
||||
*
|
||||
* A refdb implementation may provide this function; if it is not
|
||||
* provided, no data structures will be initialized for the refdb when
|
||||
* a new repository is created.
|
||||
*
|
||||
* @param path The path of the Git directory that shall be initialized.
|
||||
* @param initial_head The target that HEAD should point to. This value
|
||||
* should only be applied when there isn't yet a HEAD
|
||||
* reference, or when `GIT_REFDB_BACKEND_INIT_FORCE_HEAD`
|
||||
* is passed. Optional, if unset the backend should not set
|
||||
* up HEAD, either.
|
||||
* @param mode The mode that shall be used to create files and
|
||||
* directories. May be one of `git_repository_init_mode_t`
|
||||
* or normal Unix permission bits.
|
||||
* @param flags A combination of `git_refdb_backend_init_flag_t` flags.
|
||||
* @return `0` on success, a negative error value code.
|
||||
*/
|
||||
int GIT_CALLBACK(init)(
|
||||
git_refdb_backend *backend,
|
||||
const char *head_target,
|
||||
mode_t mode,
|
||||
uint32_t flags);
|
||||
|
||||
/**
|
||||
* Queries the refdb backend for the existence of a reference.
|
||||
*
|
||||
|
||||
@@ -39,28 +39,36 @@ int git_refdb_new(git_refdb **out, git_repository *repo)
|
||||
|
||||
int git_refdb_open(git_refdb **out, git_repository *repo)
|
||||
{
|
||||
git_refdb_backend *backend;
|
||||
git_refdb *db;
|
||||
git_refdb_backend *dir;
|
||||
int error;
|
||||
|
||||
GIT_ASSERT_ARG(out);
|
||||
GIT_ASSERT_ARG(repo);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
if (git_refdb_new(&db, repo) < 0)
|
||||
return -1;
|
||||
if ((error = git_refdb_new(&db, repo)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Add the default (filesystem) backend */
|
||||
if (git_refdb_backend_fs(&dir, repo) < 0) {
|
||||
git_refdb_free(db);
|
||||
return -1;
|
||||
switch (repo->refdb_type) {
|
||||
case GIT_REFDB_FILES:
|
||||
if ((error = git_refdb_backend_fs(&backend, repo)) < 0)
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
git_error_set(GIT_ERROR_REFERENCE, "unknown reference storage format");
|
||||
error = GIT_EINVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
db->repo = repo;
|
||||
db->backend = dir;
|
||||
|
||||
db->backend = backend;
|
||||
*out = db;
|
||||
return 0;
|
||||
out:
|
||||
if (error)
|
||||
git_refdb_free(db);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void refdb_free_backend(git_refdb *db)
|
||||
@@ -114,6 +122,16 @@ void git_refdb_free(git_refdb *db)
|
||||
GIT_REFCOUNT_DEC(db, git_refdb__free);
|
||||
}
|
||||
|
||||
int git_refdb_init(git_refdb *refdb, const char *head_target, mode_t mode, uint32_t flags)
|
||||
{
|
||||
GIT_ASSERT_ARG(refdb);
|
||||
GIT_ASSERT_ARG(refdb->backend);
|
||||
|
||||
if (!refdb->backend->init)
|
||||
return 0;
|
||||
return refdb->backend->init(refdb->backend, head_target, mode, flags);
|
||||
}
|
||||
|
||||
int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name)
|
||||
{
|
||||
GIT_ASSERT_ARG(exists);
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "git2/refdb.h"
|
||||
#include "repository.h"
|
||||
|
||||
#define GIT_INVALID_HEAD "refs/heads/.invalid"
|
||||
|
||||
struct git_refdb {
|
||||
git_refcount rc;
|
||||
git_repository *repo;
|
||||
@@ -20,6 +22,11 @@ struct git_refdb {
|
||||
|
||||
void git_refdb__free(git_refdb *db);
|
||||
|
||||
int git_refdb_init(git_refdb *refdb,
|
||||
const char *head_target,
|
||||
mode_t mode,
|
||||
uint32_t flags);
|
||||
|
||||
int git_refdb_exists(
|
||||
int *exists,
|
||||
git_refdb *refdb,
|
||||
@@ -125,4 +132,21 @@ int git_refdb_ensure_log(git_refdb *refdb, const char *refname);
|
||||
int git_refdb_lock(void **payload, git_refdb *db, const char *refname);
|
||||
int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message);
|
||||
|
||||
GIT_INLINE(const char *) git_refdb_type_name(git_refdb_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case GIT_REFDB_FILES:
|
||||
return "files";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
GIT_INLINE(git_refdb_t) git_refdb_type_fromstr(const char *name)
|
||||
{
|
||||
if (strcmp(name, "files") == 0)
|
||||
return GIT_REFDB_FILES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -344,6 +344,60 @@ static int packed_loadloose(refdb_fs_backend *backend)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int refdb_fs_backend__init(struct git_refdb_backend *_backend,
|
||||
const char *head_target,
|
||||
mode_t mode,
|
||||
uint32_t flags)
|
||||
{
|
||||
refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
|
||||
git_str path = GIT_STR_INIT, content = GIT_STR_INIT;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* As most references are per repository and not per worktree we don't
|
||||
* want to set up the typical "refs/" hierarchy in worktrees.
|
||||
*/
|
||||
if ((flags & GIT_REFDB_BACKEND_INIT_IS_WORKTREE) == 0) {
|
||||
mode_t dmode;
|
||||
|
||||
if (mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
|
||||
dmode = 0777;
|
||||
else if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
|
||||
dmode = (0775 | S_ISGID);
|
||||
else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL)
|
||||
dmode = (0777 | S_ISGID);
|
||||
else
|
||||
dmode = mode;
|
||||
|
||||
if ((error = git_str_joinpath(&path, backend->gitpath, GIT_REFS_HEADS_DIR)) < 0 ||
|
||||
(error = git_futils_mkdir(path.ptr, dmode, 0) < 0) ||
|
||||
(error = git_str_joinpath(&path, backend->gitpath, GIT_REFS_TAGS_DIR)) < 0 ||
|
||||
(error = git_futils_mkdir(path.ptr, dmode, 0) < 0))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (head_target) {
|
||||
if ((error = git_str_joinpath(&path, backend->gitpath, GIT_HEAD_FILE)) < 0)
|
||||
goto out;
|
||||
|
||||
if ((flags & GIT_REFDB_BACKEND_INIT_FORCE_HEAD) == 0 &&
|
||||
git_fs_path_exists(path.ptr))
|
||||
goto out;
|
||||
|
||||
if ((error = git_str_printf(&content, GIT_SYMREF "%s\n", head_target)) < 0 ||
|
||||
(error = git_futils_writebuffer(&content, path.ptr, 0, mode)) < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
|
||||
out:
|
||||
git_str_dispose(&content);
|
||||
git_str_dispose(&path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int refdb_fs_backend__exists(
|
||||
int *exists,
|
||||
git_refdb_backend *_backend,
|
||||
@@ -2545,6 +2599,7 @@ int git_refdb_backend_fs(
|
||||
backend->fsync = 1;
|
||||
backend->iterator_flags |= GIT_ITERATOR_DESCEND_SYMLINKS;
|
||||
|
||||
backend->parent.init = &refdb_fs_backend__init;
|
||||
backend->parent.exists = &refdb_fs_backend__exists;
|
||||
backend->parent.lookup = &refdb_fs_backend__lookup;
|
||||
backend->parent.iterator = &refdb_fs_backend__iterator;
|
||||
|
||||
@@ -45,8 +45,6 @@ typedef struct {
|
||||
static repo_template_item repo_template[] = {
|
||||
{ GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/info/' */
|
||||
{ GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/pack/' */
|
||||
{ GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/heads/' */
|
||||
{ GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/tags/' */
|
||||
{ GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE, NULL }, /* '/hooks/' */
|
||||
{ GIT_INFO_DIR, GIT_INFO_DIR_MODE, NULL }, /* '/info/' */
|
||||
{ GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <ctype.h>
|
||||
|
||||
#include "git2/object.h"
|
||||
#include "git2/sys/refdb_backend.h"
|
||||
#include "git2/sys/repository.h"
|
||||
|
||||
#include "buf.h"
|
||||
@@ -69,6 +70,7 @@ static int check_repositoryformatversion(int *version, git_config *config);
|
||||
static int check_extensions(git_config *config, int version);
|
||||
static int load_global_config(git_config **config, bool use_env);
|
||||
static int load_objectformat(git_repository *repo, git_config *config);
|
||||
static int load_refstorage_format(git_repository *repo, git_config *config);
|
||||
|
||||
#define GIT_COMMONDIR_FILE "commondir"
|
||||
#define GIT_GITDIR_FILE "gitdir"
|
||||
@@ -350,6 +352,17 @@ int git_repository_new_ext(
|
||||
repo->oid_type = opts && opts->oid_type ? opts->oid_type :
|
||||
GIT_OID_DEFAULT;
|
||||
|
||||
/*
|
||||
* This is a bit dirty, as this repository doesn't really have a refdb
|
||||
* in the first place. But we do expect that we can create an "empty"
|
||||
* ref iterator from such a repository, and things keep on working like
|
||||
* this.
|
||||
*
|
||||
* It might make sense to eventually create an "in-memory" refdb type
|
||||
* to serve this purpose.
|
||||
*/
|
||||
repo->refdb_type = GIT_REFDB_FILES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -983,20 +996,28 @@ done:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int obtain_config_and_set_oid_type(
|
||||
git_config **config_ptr,
|
||||
git_repository *repo)
|
||||
static int read_repository_format(git_repository *repo)
|
||||
{
|
||||
int error;
|
||||
git_str config_path = GIT_STR_INIT;
|
||||
git_config *config = NULL;
|
||||
int version = 0;
|
||||
|
||||
if ((error = git_repository__item_path(&config_path, repo,
|
||||
GIT_REPOSITORY_ITEM_CONFIG)) < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We'd like to have the config, but git doesn't particularly
|
||||
* care if it's not there, so we need to deal with that.
|
||||
*
|
||||
* Further note that we only read the repository's own configuration.
|
||||
* No other configuration should play a role here. Most importantly, we
|
||||
* ignore conditional includes as those may cause a cyclic dependency
|
||||
* between reading the format and evaluating the conditionals. This is
|
||||
* for example the case with "onbranch" conditions.
|
||||
*/
|
||||
|
||||
error = git_repository_config_snapshot(&config, repo);
|
||||
error = git_config_open_ondisk(&config, config_path.ptr);
|
||||
if (error < 0 && error != GIT_ENOTFOUND)
|
||||
goto out;
|
||||
|
||||
@@ -1008,14 +1029,17 @@ static int obtain_config_and_set_oid_type(
|
||||
goto out;
|
||||
|
||||
if (version > 0) {
|
||||
if ((error = load_objectformat(repo, config)) < 0)
|
||||
if ((error = load_objectformat(repo, config)) < 0 ||
|
||||
(error = load_refstorage_format(repo, config)) < 0)
|
||||
goto out;
|
||||
} else {
|
||||
repo->oid_type = GIT_OID_DEFAULT;
|
||||
repo->refdb_type = GIT_REFDB_FILES;
|
||||
}
|
||||
|
||||
out:
|
||||
*config_ptr = config;
|
||||
git_str_dispose(&config_path);
|
||||
git_config_free(config);
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -1028,7 +1052,6 @@ int git_repository_open_bare(
|
||||
git_repository *repo = NULL;
|
||||
bool is_valid;
|
||||
int error;
|
||||
git_config *config;
|
||||
|
||||
if ((error = git_fs_path_prettify_dir(&path, bare_path, NULL)) < 0 ||
|
||||
(error = is_valid_repository_path(&is_valid, &path, &common_path, 0)) < 0)
|
||||
@@ -1054,14 +1077,12 @@ int git_repository_open_bare(
|
||||
repo->is_worktree = 0;
|
||||
repo->workdir = NULL;
|
||||
|
||||
if ((error = obtain_config_and_set_oid_type(&config, repo)) < 0)
|
||||
if ((error = read_repository_format(repo)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
*repo_ptr = repo;
|
||||
|
||||
cleanup:
|
||||
git_config_free(config);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -1148,7 +1169,7 @@ int git_repository_open_ext(
|
||||
|
||||
repo->is_worktree = is_worktree;
|
||||
|
||||
error = obtain_config_and_set_oid_type(&config, repo);
|
||||
error = read_repository_format(repo);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
@@ -1158,6 +1179,10 @@ int git_repository_open_ext(
|
||||
if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) {
|
||||
repo->is_bare = 1;
|
||||
} else {
|
||||
error = git_repository_config_snapshot(&config, repo);
|
||||
if (error < 0 && error != GIT_ENOTFOUND)
|
||||
goto cleanup;
|
||||
|
||||
if (config &&
|
||||
((error = load_config_data(repo, config)) < 0 ||
|
||||
(error = load_workdir(repo, config, &paths.workdir)) < 0))
|
||||
@@ -1235,6 +1260,17 @@ int git_repository_wrap_odb(git_repository **out, git_odb *odb)
|
||||
GIT_ASSERT(git_oid_type_is_valid(odb->options.oid_type));
|
||||
repo->oid_type = odb->options.oid_type;
|
||||
|
||||
/*
|
||||
* This is a bit dirty, as this repository doesn't really have a refdb
|
||||
* in the first place. But we do expect that we can create an "empty"
|
||||
* ref iterator from such a repository, and things keep on working like
|
||||
* this.
|
||||
*
|
||||
* It might make sense to eventually create an "in-memory" refdb type
|
||||
* to serve this purpose.
|
||||
*/
|
||||
repo->refdb_type = GIT_REFDB_FILES;
|
||||
|
||||
git_repository_set_odb(repo, odb);
|
||||
*out = repo;
|
||||
|
||||
@@ -1869,7 +1905,8 @@ static const char *builtin_extensions[] = {
|
||||
"noop",
|
||||
"objectformat",
|
||||
"worktreeconfig",
|
||||
"preciousobjects"
|
||||
"preciousobjects",
|
||||
"refstorage",
|
||||
};
|
||||
|
||||
static git_vector user_extensions = { 0, git__strcmp_cb };
|
||||
@@ -2006,6 +2043,32 @@ int git_repository__set_objectformat(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_refstorage_format(git_repository *repo, git_config *config)
|
||||
{
|
||||
git_config_entry *entry = NULL;
|
||||
int error;
|
||||
|
||||
if ((error = git_config_get_entry(&entry, config, "extensions.refstorage")) < 0) {
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
repo->refdb_type = GIT_REFDB_FILES;
|
||||
git_error_clear();
|
||||
error = 0;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((repo->refdb_type = git_refdb_type_fromstr(entry->value)) == 0) {
|
||||
git_error_set(GIT_ERROR_REPOSITORY,
|
||||
"unknown refstorage format '%s'", entry->value);
|
||||
error = GIT_EINVALID;
|
||||
}
|
||||
|
||||
done:
|
||||
git_config_entry_free(entry);
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository__extensions(char ***out, size_t *out_len)
|
||||
{
|
||||
git_vector extensions;
|
||||
@@ -2098,32 +2161,6 @@ void git_repository__free_extensions(void)
|
||||
git_vector_dispose_deep(&user_extensions);
|
||||
}
|
||||
|
||||
int git_repository_create_head(const char *git_dir, const char *ref_name)
|
||||
{
|
||||
git_str ref_path = GIT_STR_INIT;
|
||||
git_filebuf ref = GIT_FILEBUF_INIT;
|
||||
const char *fmt;
|
||||
int error;
|
||||
|
||||
if ((error = git_str_joinpath(&ref_path, git_dir, GIT_HEAD_FILE)) < 0 ||
|
||||
(error = git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE)) < 0)
|
||||
goto out;
|
||||
|
||||
if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
|
||||
fmt = "ref: %s\n";
|
||||
else
|
||||
fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
|
||||
|
||||
if ((error = git_filebuf_printf(&ref, fmt, ref_name)) < 0 ||
|
||||
(error = git_filebuf_commit(&ref)) < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
git_str_dispose(&ref_path);
|
||||
git_filebuf_cleanup(&ref);
|
||||
return error;
|
||||
}
|
||||
|
||||
static bool is_chmod_supported(const char *file_path)
|
||||
{
|
||||
struct stat st1, st2;
|
||||
@@ -2317,7 +2354,8 @@ static int repo_init_config(
|
||||
const char *work_dir,
|
||||
uint32_t flags,
|
||||
uint32_t mode,
|
||||
git_oid_t oid_type)
|
||||
git_oid_t oid_type,
|
||||
git_refdb_t refdb_type)
|
||||
{
|
||||
int error = 0;
|
||||
git_str cfg_path = GIT_STR_INIT, worktree_path = GIT_STR_INIT;
|
||||
@@ -2381,6 +2419,11 @@ static int repo_init_config(
|
||||
SET_REPO_CONFIG(string, "extensions.objectformat", git_oid_type_name(oid_type));
|
||||
}
|
||||
|
||||
if (refdb_type != GIT_REFDB_FILES) {
|
||||
SET_REPO_CONFIG(int32, "core.repositoryformatversion", 1);
|
||||
SET_REPO_CONFIG(string, "extensions.refstorage", git_refdb_type_name(refdb_type));
|
||||
}
|
||||
|
||||
cleanup:
|
||||
git_str_dispose(&cfg_path);
|
||||
git_str_dispose(&worktree_path);
|
||||
@@ -2792,27 +2835,19 @@ static int repo_init_directories(
|
||||
return error;
|
||||
}
|
||||
|
||||
static int repo_init_head(const char *repo_dir, const char *given)
|
||||
static int get_initial_head(char **out, uint32_t *flags,
|
||||
git_repository_init_options *opts)
|
||||
{
|
||||
git_config *cfg = NULL;
|
||||
git_str head_path = GIT_STR_INIT, cfg_branch = GIT_STR_INIT;
|
||||
git_str cfg_branch = GIT_STR_INIT, prefixed = GIT_STR_INIT;
|
||||
const char *initial_head = NULL;
|
||||
git_config *cfg = NULL;
|
||||
int error;
|
||||
|
||||
if ((error = git_str_joinpath(&head_path, repo_dir, GIT_HEAD_FILE)) < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* A template may have set a HEAD; use that unless it's been
|
||||
* overridden by the caller's given initial head setting.
|
||||
*/
|
||||
if (git_fs_path_exists(head_path.ptr) && !given)
|
||||
goto out;
|
||||
|
||||
if (given) {
|
||||
initial_head = given;
|
||||
} else if ((error = git_config_open_default(&cfg)) >= 0 &&
|
||||
(error = git_config__get_string_buf(&cfg_branch, cfg, "init.defaultbranch")) >= 0 &&
|
||||
if (opts->initial_head) {
|
||||
initial_head = opts->initial_head;
|
||||
*flags |= GIT_REFDB_BACKEND_INIT_FORCE_HEAD;
|
||||
} else if (git_config_open_default(&cfg) >= 0 &&
|
||||
git_config__get_string_buf(&cfg_branch, cfg, "init.defaultbranch") >= 0 &&
|
||||
*cfg_branch.ptr) {
|
||||
initial_head = cfg_branch.ptr;
|
||||
}
|
||||
@@ -2820,12 +2855,22 @@ static int repo_init_head(const char *repo_dir, const char *given)
|
||||
if (!initial_head)
|
||||
initial_head = GIT_BRANCH_DEFAULT;
|
||||
|
||||
error = git_repository_create_head(repo_dir, initial_head);
|
||||
if (git__prefixcmp(initial_head, GIT_REFS_DIR) != 0) {
|
||||
git_str_printf(&prefixed, GIT_REFS_HEADS_DIR "%s", initial_head);
|
||||
initial_head = prefixed.ptr;
|
||||
}
|
||||
|
||||
if ((*out = git__strdup(initial_head)) == NULL) {
|
||||
error = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
|
||||
out:
|
||||
git_config_free(cfg);
|
||||
git_str_dispose(&head_path);
|
||||
git_str_dispose(&cfg_branch);
|
||||
git_str_dispose(&prefixed);
|
||||
git_config_free(cfg);
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -2860,10 +2905,14 @@ int git_repository_init_ext(
|
||||
git_repository_init_options *opts)
|
||||
{
|
||||
git_str repo_path = GIT_STR_INIT, wd_path = GIT_STR_INIT,
|
||||
common_path = GIT_STR_INIT;
|
||||
common_path = GIT_STR_INIT, path = GIT_STR_INIT;
|
||||
const char *wd;
|
||||
git_refdb *refdb;
|
||||
bool is_valid;
|
||||
git_oid_t oid_type = GIT_OID_DEFAULT;
|
||||
git_refdb_t refdb_type = GIT_REFDB_FILES;
|
||||
char *initial_head = NULL;
|
||||
uint32_t refdb_init_flags = 0;
|
||||
int error;
|
||||
|
||||
GIT_ASSERT_ARG(out);
|
||||
@@ -2876,6 +2925,8 @@ int git_repository_init_ext(
|
||||
if (opts->oid_type)
|
||||
oid_type = opts->oid_type;
|
||||
#endif
|
||||
if (opts->refdb_type)
|
||||
refdb_type = opts->refdb_type;
|
||||
|
||||
if ((error = repo_init_directories(&repo_path, &wd_path, given_repo, opts)) < 0)
|
||||
goto out;
|
||||
@@ -2895,20 +2946,79 @@ int git_repository_init_ext(
|
||||
|
||||
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
|
||||
|
||||
if ((error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode, oid_type)) < 0)
|
||||
if ((error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode,
|
||||
oid_type, refdb_type)) < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If we are re-initializing an existing repository we only
|
||||
* want to reset HEAD in case the user explicitly provided a
|
||||
* new target.
|
||||
*/
|
||||
if (opts->initial_head &&
|
||||
(error = get_initial_head(&initial_head, &refdb_init_flags, opts)) < 0)
|
||||
goto out;
|
||||
|
||||
/* TODO: reinitialize the templates */
|
||||
} else {
|
||||
git_str content = GIT_STR_INIT_CONST("ref: " GIT_INVALID_HEAD,
|
||||
strlen("ref: " GIT_INVALID_HEAD));
|
||||
mode_t dmode = pick_dir_mode(opts);
|
||||
|
||||
if ((error = repo_init_structure(repo_path.ptr, wd, opts)) < 0 ||
|
||||
(error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode, oid_type)) < 0 ||
|
||||
(error = repo_init_head(repo_path.ptr, opts->initial_head)) < 0)
|
||||
(error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode,
|
||||
oid_type, refdb_type)) < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Both HEAD and "refs/" must exist in a repository regardless
|
||||
* of its ref format, otherwise it won't be recognized as a Git
|
||||
* repository. As such, we create these now so that we can open
|
||||
* the repository and properly initialize its refdb.
|
||||
*/
|
||||
if ((error = git_str_joinpath(&path, repo_path.ptr, GIT_HEAD_FILE)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Only overwrite HEAD if it wasn't created by the template. */
|
||||
if (!git_fs_path_exists(path.ptr)) {
|
||||
if ((error = git_futils_writebuffer(&content, path.ptr, 0,
|
||||
GIT_REFS_FILE_MODE)) < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Ask the backend to force-overwrite the invalid HEAD
|
||||
* we have just written.
|
||||
*/
|
||||
refdb_init_flags |= GIT_REFDB_BACKEND_INIT_FORCE_HEAD;
|
||||
}
|
||||
|
||||
if ((error = git_str_joinpath(&path, repo_path.ptr, GIT_REFS_DIR)) < 0 ||
|
||||
(error = git_futils_mkdir(path.ptr, dmode, 0)) < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Note that we also compute the initial HEAD in case the file
|
||||
* already exists. This is done because the file HEAD may not
|
||||
* actually be relevant to the backend. E.g. reftables do not
|
||||
* care for this file at all, so we would end up with no HEAD
|
||||
* at all if we didn't ask the backend to write one in that
|
||||
* case.
|
||||
*
|
||||
* That being said, we don't force-overwrite HEAD so that the
|
||||
* backends can first check whether the templates have already
|
||||
* created a HEAD.
|
||||
*/
|
||||
if ((error = get_initial_head(&initial_head, &refdb_init_flags, opts)) < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((error = git_repository_open(out, repo_path.ptr)) < 0)
|
||||
goto out;
|
||||
|
||||
if ((error = git_repository_refdb__weakptr(&refdb, *out)) < 0 ||
|
||||
(error = git_refdb_init(refdb, initial_head, opts->mode, refdb_init_flags)) < 0)
|
||||
goto out;
|
||||
|
||||
if (opts->origin_url &&
|
||||
(error = repo_init_create_origin(*out, opts->origin_url)) < 0)
|
||||
goto out;
|
||||
@@ -2917,6 +3027,8 @@ out:
|
||||
git_str_dispose(&common_path);
|
||||
git_str_dispose(&repo_path);
|
||||
git_str_dispose(&wd_path);
|
||||
git_str_dispose(&path);
|
||||
git__free(initial_head);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ struct git_repository {
|
||||
is_bare:1,
|
||||
is_worktree:1;
|
||||
git_oid_t oid_type;
|
||||
git_refdb_t refdb_type;
|
||||
|
||||
unsigned int lru_counter;
|
||||
|
||||
@@ -178,7 +179,6 @@ GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
|
||||
|
||||
int git_repository_head_commit(git_commit **commit, git_repository *repo);
|
||||
int git_repository_head_tree(git_tree **tree, git_repository *repo);
|
||||
int git_repository_create_head(const char *git_dir, const char *ref_name);
|
||||
|
||||
typedef int (*git_repository_foreach_worktree_cb)(git_repository *, void *);
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
#include "worktree.h"
|
||||
|
||||
#include "buf.h"
|
||||
#include "refdb.h"
|
||||
#include "repository.h"
|
||||
#include "path.h"
|
||||
|
||||
#include "git2/branch.h"
|
||||
#include "git2/commit.h"
|
||||
#include "git2/worktree.h"
|
||||
#include "git2/sys/refdb_backend.h"
|
||||
|
||||
static bool is_worktree_dir(const char *dir)
|
||||
{
|
||||
@@ -305,12 +307,15 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
|
||||
const char *name, const char *worktree,
|
||||
const git_worktree_add_options *opts)
|
||||
{
|
||||
git_str invalid_head_content = GIT_STR_INIT_CONST("ref: " GIT_INVALID_HEAD,
|
||||
strlen("ref: " GIT_INVALID_HEAD));
|
||||
git_str gitdir = GIT_STR_INIT, wddir = GIT_STR_INIT, buf = GIT_STR_INIT;
|
||||
git_reference *ref = NULL, *head = NULL;
|
||||
git_reference *ref = NULL, *head = NULL, *wt_head = NULL;
|
||||
git_commit *commit = NULL;
|
||||
git_repository *wt = NULL;
|
||||
git_checkout_options coopts;
|
||||
git_worktree_add_options wtopts = GIT_WORKTREE_ADD_OPTIONS_INIT;
|
||||
git_refdb *refdb;
|
||||
int err;
|
||||
|
||||
GIT_ERROR_CHECK_VERSION(
|
||||
@@ -402,12 +407,26 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
|
||||
|| (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Set worktree's HEAD */
|
||||
if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)
|
||||
/*
|
||||
* Create the ref skeleton that is required to make a directory look
|
||||
* like a Git repository. These are required regardless of the ref
|
||||
* format used.
|
||||
*/
|
||||
if ((err = git_str_joinpath(&buf, gitdir.ptr, GIT_HEAD_FILE)) < 0 ||
|
||||
(err = git_futils_writebuffer(&invalid_head_content, buf.ptr, 0, GIT_REFS_FILE_MODE)) < 0 ||
|
||||
(err = git_str_joinpath(&buf, gitdir.ptr, GIT_REFS_DIR)) < 0 ||
|
||||
(err = git_futils_mkdir(buf.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
|
||||
goto out;
|
||||
|
||||
if ((err = git_repository_open(&wt, wddir.ptr)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Initialize the worktree refdb and set up HEAD. */
|
||||
if ((err = git_repository_refdb__weakptr(&refdb, wt)) < 0 ||
|
||||
(err = git_refdb_init(refdb, git_reference_name(ref), 0777,
|
||||
GIT_REFDB_BACKEND_INIT_IS_WORKTREE | GIT_REFDB_BACKEND_INIT_FORCE_HEAD)) < 0)
|
||||
goto out;
|
||||
|
||||
/* Checkout worktree's HEAD */
|
||||
if ((err = git_checkout_head(wt, &coopts)) < 0)
|
||||
goto out;
|
||||
@@ -422,6 +441,7 @@ out:
|
||||
git_str_dispose(&buf);
|
||||
git_reference_free(ref);
|
||||
git_reference_free(head);
|
||||
git_reference_free(wt_head);
|
||||
git_commit_free(commit);
|
||||
git_repository_free(wt);
|
||||
|
||||
|
||||
@@ -112,7 +112,9 @@ void test_config_conditionals__invalid_conditional_fails(void)
|
||||
|
||||
static void set_head(git_repository *repo, const char *name)
|
||||
{
|
||||
cl_git_pass(git_repository_create_head(git_repository_path(repo), name));
|
||||
struct git_reference *ref;
|
||||
cl_git_pass(git_reference_symbolic_create(&ref, repo, GIT_HEAD_FILE, name, 1, NULL));
|
||||
git_reference_free(ref);
|
||||
}
|
||||
|
||||
void test_config_conditionals__onbranch(void)
|
||||
@@ -123,12 +125,12 @@ void test_config_conditionals__onbranch(void)
|
||||
assert_condition_includes("onbranch", "master/", false);
|
||||
assert_condition_includes("onbranch", "foo", false);
|
||||
|
||||
set_head(_repo, "foo");
|
||||
set_head(_repo, "refs/heads/foo");
|
||||
assert_condition_includes("onbranch", "master", false);
|
||||
assert_condition_includes("onbranch", "foo", true);
|
||||
assert_condition_includes("onbranch", "f*o", true);
|
||||
|
||||
set_head(_repo, "dir/ref");
|
||||
set_head(_repo, "refs/heads/dir/ref");
|
||||
assert_condition_includes("onbranch", "dir/ref", true);
|
||||
assert_condition_includes("onbranch", "dir/", true);
|
||||
assert_condition_includes("onbranch", "dir/*", true);
|
||||
@@ -137,7 +139,7 @@ void test_config_conditionals__onbranch(void)
|
||||
assert_condition_includes("onbranch", "dir", false);
|
||||
assert_condition_includes("onbranch", "dir*", false);
|
||||
|
||||
set_head(_repo, "dir/subdir/ref");
|
||||
set_head(_repo, "refs/heads/dir/subdir/ref");
|
||||
assert_condition_includes("onbranch", "dir/subdir/", true);
|
||||
assert_condition_includes("onbranch", "dir/subdir/*", true);
|
||||
assert_condition_includes("onbranch", "dir/subdir/ref", true);
|
||||
|
||||
@@ -34,11 +34,12 @@ void test_core_opts__extensions_query(void)
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
|
||||
|
||||
cl_assert_equal_sz(out.count, 4);
|
||||
cl_assert_equal_sz(out.count, 5);
|
||||
cl_assert_equal_s("noop", out.strings[0]);
|
||||
cl_assert_equal_s("objectformat", out.strings[1]);
|
||||
cl_assert_equal_s("preciousobjects", out.strings[2]);
|
||||
cl_assert_equal_s("worktreeconfig", out.strings[3]);
|
||||
cl_assert_equal_s("refstorage", out.strings[3]);
|
||||
cl_assert_equal_s("worktreeconfig", out.strings[4]);
|
||||
|
||||
git_strarray_dispose(&out);
|
||||
}
|
||||
@@ -51,12 +52,13 @@ void test_core_opts__extensions_add(void)
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
|
||||
|
||||
cl_assert_equal_sz(out.count, 5);
|
||||
cl_assert_equal_sz(out.count, 6);
|
||||
cl_assert_equal_s("foo", out.strings[0]);
|
||||
cl_assert_equal_s("noop", out.strings[1]);
|
||||
cl_assert_equal_s("objectformat", out.strings[2]);
|
||||
cl_assert_equal_s("preciousobjects", out.strings[3]);
|
||||
cl_assert_equal_s("worktreeconfig", out.strings[4]);
|
||||
cl_assert_equal_s("refstorage", out.strings[4]);
|
||||
cl_assert_equal_s("worktreeconfig", out.strings[5]);
|
||||
|
||||
git_strarray_dispose(&out);
|
||||
}
|
||||
@@ -69,12 +71,13 @@ void test_core_opts__extensions_remove(void)
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
|
||||
|
||||
cl_assert_equal_sz(out.count, 5);
|
||||
cl_assert_equal_sz(out.count, 6);
|
||||
cl_assert_equal_s("bar", out.strings[0]);
|
||||
cl_assert_equal_s("baz", out.strings[1]);
|
||||
cl_assert_equal_s("objectformat", out.strings[2]);
|
||||
cl_assert_equal_s("preciousobjects", out.strings[3]);
|
||||
cl_assert_equal_s("worktreeconfig", out.strings[4]);
|
||||
cl_assert_equal_s("refstorage", out.strings[4]);
|
||||
cl_assert_equal_s("worktreeconfig", out.strings[5]);
|
||||
|
||||
git_strarray_dispose(&out);
|
||||
}
|
||||
@@ -87,13 +90,14 @@ void test_core_opts__extensions_uniq(void)
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
|
||||
|
||||
cl_assert_equal_sz(out.count, 6);
|
||||
cl_assert_equal_sz(out.count, 7);
|
||||
cl_assert_equal_s("bar", out.strings[0]);
|
||||
cl_assert_equal_s("foo", out.strings[1]);
|
||||
cl_assert_equal_s("noop", out.strings[2]);
|
||||
cl_assert_equal_s("objectformat", out.strings[3]);
|
||||
cl_assert_equal_s("preciousobjects", out.strings[4]);
|
||||
cl_assert_equal_s("worktreeconfig", out.strings[5]);
|
||||
cl_assert_equal_s("refstorage", out.strings[5]);
|
||||
cl_assert_equal_s("worktreeconfig", out.strings[6]);
|
||||
|
||||
git_strarray_dispose(&out);
|
||||
}
|
||||
|
||||
@@ -874,3 +874,26 @@ void test_repo_open__can_reset_safe_directory_list(void)
|
||||
git_str_dispose(&config_filename);
|
||||
git_str_dispose(&config_data);
|
||||
}
|
||||
|
||||
void test_repo_open__refstorage_extension(void)
|
||||
{
|
||||
git_repository *repo, *tmp;
|
||||
git_config *config;
|
||||
|
||||
repo = cl_git_sandbox_init("empty_bare.git");
|
||||
|
||||
cl_git_pass(git_repository_open(&repo, "empty_bare.git"));
|
||||
cl_git_pass(git_repository_config(&config, repo));
|
||||
cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1));
|
||||
cl_git_pass(git_config_set_string(config, "extensions.refStorage", "files"));
|
||||
|
||||
cl_git_pass(git_repository_open(&tmp, "empty_bare.git"));
|
||||
git_repository_free(tmp);
|
||||
|
||||
cl_git_pass(git_config_set_string(config, "extensions.refStorage", "garbage"));
|
||||
cl_git_fail_with(GIT_EINVALID, git_repository_open(&tmp, "empty_bare.git"));
|
||||
git_repository_free(tmp);
|
||||
|
||||
git_config_free(config);
|
||||
git_repository_free(repo);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user