Commit 85e7efa1 by Carlos Martín Nieto

odb: recursively load alternates

The maximum depth is 5, like in git
parent 5a36f127
......@@ -23,6 +23,8 @@
#define GIT_LOOSE_PRIORITY 2
#define GIT_PACKED_PRIORITY 1
#define GIT_ALTERNATES_MAX_DEPTH 5
typedef struct
{
git_odb_backend *backend;
......@@ -30,6 +32,8 @@ typedef struct
int is_alternate;
} backend_internal;
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
{
const char *type_str = git_object_type2string(obj_type);
......@@ -395,7 +399,7 @@ int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
return add_backend_internal(odb, backend, priority, 1);
}
static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates)
static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates, int alternate_depth)
{
git_odb_backend *loose, *packed;
......@@ -409,10 +413,10 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt
add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0)
return -1;
return 0;
return load_alternates(db, objects_dir, alternate_depth);
}
static int load_alternates(git_odb *odb, const char *objects_dir)
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth)
{
git_buf alternates_path = GIT_BUF_INIT;
git_buf alternates_buf = GIT_BUF_INIT;
......@@ -420,6 +424,11 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
const char *alternate;
int result = 0;
/* Git reports an error, we just ignore anything deeper */
if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) {
return 0;
}
if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)
return -1;
......@@ -440,14 +449,18 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
if (*alternate == '\0' || *alternate == '#')
continue;
/* relative path: build based on the current `objects` folder */
if (*alternate == '.') {
/*
* Relative path: build based on the current `objects`
* folder. However, relative paths are only allowed in
* the current repository.
*/
if (*alternate == '.' && !alternate_depth) {
if ((result = git_buf_joinpath(&alternates_path, objects_dir, alternate)) < 0)
break;
alternate = git_buf_cstr(&alternates_path);
}
if ((result = add_default_backends(odb, alternate, 1)) < 0)
if ((result = add_default_backends(odb, alternate, 1, alternate_depth + 1)) < 0)
break;
}
......@@ -468,8 +481,7 @@ int git_odb_open(git_odb **out, const char *objects_dir)
if (git_odb_new(&db) < 0)
return -1;
if (add_default_backends(db, objects_dir, 0) < 0 ||
load_alternates(db, objects_dir) < 0)
if (add_default_backends(db, objects_dir, 0, 0) < 0)
{
git_odb_free(db);
return -1;
......
#include "clar_libgit2.h"
#include "odb.h"
#include "repository.h"
static git_buf destpath, filepath;
static const char *paths[] = {
"A.git", "B.git", "C.git", "D.git", "E.git", "F.git", "G.git"
};
static git_filebuf file;
static git_repository *repo;
void test_odb_alternates__cleanup(void)
{
git_buf_free(&destpath);
git_buf_free(&filepath);
}
static void init_linked_repo(const char *path, const char *alternate)
{
git_buf_clear(&destpath);
git_buf_clear(&filepath);
cl_git_pass(git_repository_init(&repo, path, 1));
cl_git_pass(git_path_prettify(&destpath, alternate, NULL));
cl_git_pass(git_buf_joinpath(&destpath, destpath.ptr, "objects"));
cl_git_pass(git_buf_joinpath(&filepath, git_repository_path(repo), "objects/info"));
cl_git_pass(git_futils_mkdir(filepath.ptr, NULL, 0755, GIT_MKDIR_PATH));
cl_git_pass(git_buf_joinpath(&filepath, filepath.ptr , "alternates"));
cl_git_pass(git_filebuf_open(&file, git_buf_cstr(&filepath), 0));
git_filebuf_printf(&file, "%s\n", git_buf_cstr(&destpath));
cl_git_pass(git_filebuf_commit(&file, 0644));
git_repository_free(repo);
}
void test_odb_alternates__chained(void)
{
git_commit *commit;
git_oid oid;
/* Set the alternate A -> testrepo.git */
init_linked_repo(paths[0], cl_fixture("testrepo.git"));
/* Set the alternate B -> A */
init_linked_repo(paths[1], paths[0]);
/* Now load B and see if we can find an object from testrepo.git */
cl_git_pass(git_repository_open(&repo, paths[1]));
git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
git_commit_free(commit);
git_repository_free(repo);
}
void test_odb_alternates__long_chain(void)
{
git_commit *commit;
git_oid oid;
size_t i;
/* Set the alternate A -> testrepo.git */
init_linked_repo(paths[0], cl_fixture("testrepo.git"));
/* Set up the five-element chain */
for (i = 1; i < ARRAY_SIZE(paths); i++) {
init_linked_repo(paths[i], paths[i-1]);
}
/* Now load the last one and see if we can find an object from testrepo.git */
cl_git_pass(git_repository_open(&repo, paths[ARRAY_SIZE(paths)-1]));
git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
cl_git_fail(git_commit_lookup(&commit, repo, &oid));
git_repository_free(repo);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment