Commit 9282e921 by nulltoken Committed by Vicent Marti

Merge nulltoken's reference parsing code

All the commits have been squashed into a single one before refactoring
the final code, to keep everything tidy.

Individual commit messages are as follows:

Added repository reference looking up functionality placeholder.

Added basic reference database definition and caching infrastructure.

Removed useless constant.

Added GIT_EINVALIDREFNAME error and description. Added missing description for GIT_EBAREINDEX.

Added GIT_EREFCORRUPTED error and description.

Added GIT_ETOONESTEDSYMREF error and description.

Added resolving of direct and symbolic references.

Prepared the packed-refs parsing.

Added parsing of the packed-refs file content.

When no loose reference has been found, the full content of the packed-refs file is parsed. All of the new (i.e. not previously parsed as a loose reference) references are eagerly stored in the cached references storage.

The method packed_reference_file__parse() is in deer need of some refactoring. :-)

Extracted to a method the parsing of the peeled target of a tag.

Extracted to a method the parsing of a standard packed ref.

Fixed leaky removal of the cached references.

Ensured that a previously parsed packed reference isn't returned if a more up-to-date loose reference exists.

Enhanced documentation of git_repository_reference_lookup().

Moved some refs related constants from repository.c to refs.h.

Made parsing of a packed tag reference more robust.

Updated git_repository_reference_lookup() documentation.

Added some references to the test repository.

Added some tests covering tag references looking up.

Added some tests covering symbolic and head references looking up.

Added some tests covering packed references looking up.
parent f2c24713
......@@ -20,6 +20,11 @@ static struct {
{GIT_EZLIB, "The Z library failed to inflate/deflate an object's data"},
{GIT_EBUSY, "The queried object is currently busy"},
{GIT_EINVALIDPATH, "The path is invalid"},
{GIT_EBAREINDEX, "The index file is not backed up by an existing repository"},
{GIT_EINVALIDREFNAME, "The name of the reference is not valid"},
{GIT_EREFCORRUPTED, "The specified reference has its data corrupted"},
{GIT_ETOONESTEDSYMREF, "The specified symbolic reference is too deeply nested"},
{GIT_EPACKEDREFSCORRUPTED, "The pack-refs file is either corrupted of its format is not currently supported"}
};
const char *git_strerror(int num)
......
......@@ -134,7 +134,19 @@
#define GIT_EBUSY (GIT_ERROR - 13)
/** The index file is not backed up by an existing repository */
#define GIT_EBAREINDEX (GIT_ERROR -14)
#define GIT_EBAREINDEX (GIT_ERROR - 14)
/** The name of the reference is not valid */
#define GIT_EINVALIDREFNAME (GIT_ERROR - 15)
/** The specified reference has its data corrupted */
#define GIT_EREFCORRUPTED (GIT_ERROR - 16)
/** The specified symbolic reference is too deeply nested */
#define GIT_ETOONESTEDSYMREF (GIT_ERROR - 17)
/** The pack-refs file is either corrupted of its format is not currently supported */
#define GIT_EPACKEDREFSCORRUPTED (GIT_ERROR - 18)
/** The path is invalid */
#define GIT_EINVALIDPATH (GIT_ERROR - 19)
......
......@@ -215,6 +215,22 @@ GIT_EXTERN(void) git_repository_free(git_repository *repo);
*/
GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare);
/**
* Lookup a reference by its name in the repository.
*
* The generated reference is owned by the repository and
* should not be freed by the user.
*
* TODO:
* - Ensure the reference name is valid
*
* @param reference_out pointer to the looked-up reference
* @param repo the repository to look up the reference
* @param name the long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...)
* @return a reference to the reference
*/
GIT_EXTERN(int) git_repository_reference_lookup(git_reference **reference_out, git_repository *repo, const char *name);
/** @} */
GIT_END_DECL
#endif
......@@ -135,6 +135,22 @@ typedef struct git_signature {
git_time when; /** time when the action happened */
} git_signature;
/** In-memory representation of a reference. */
typedef struct git_reference git_reference;
/** In-memory representation of a reference to an object id. */
typedef struct git_reference_object_id git_reference_object_id;
/** In-memory representation of a reference which points at another reference. The target reference is embedded. */
typedef struct git_reference_symbolic git_reference_symbolic;
/** Basic type of any Git reference. */
typedef enum {
GIT_REF_ANY = -2, /** Reference can be any of the following */
GIT_REF_OBJECT_ID = 0, /** A reference which points at an object id */
GIT_REF_SYMBOLIC = 1, /** A reference which points at another reference */
} git_rtype;
/** @} */
GIT_END_DECL
......
This diff is collapsed. Click to expand it.
#ifndef INCLUDE_refs_h__
#define INCLUDE_refs_h__
#include "common.h"
#include "git2/oid.h"
#include "hashtable.h"
#define GIT_REFS_DIR "refs/"
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
#define GIT_SYMREF "ref:"
#define GIT_PACKEDREFS_FILE "packed-refs"
#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled \n"
#define MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH 100
struct git_reference {
git_rtype type;
char *name;
unsigned is_packed:1;
};
struct git_reference_object_id {
git_reference base;
git_oid id;
};
struct git_reference_symbolic {
git_reference base;
git_reference *target;
};
typedef struct {
git_hashtable *references;
unsigned is_fully_loaded:1;
unsigned have_packed_refs_been_parsed:1;
unsigned is_busy:1;
} git_reference_database;
git_reference_database *git_reference_database__alloc();
void git_reference_database__free(git_reference_database *ref_database);
int git_reference_lookup(git_reference **reference_out, git_reference_database *ref_database, const char *name, const char *path_repository, int *nesting_level);
#endif
\ No newline at end of file
......@@ -33,18 +33,16 @@
#include "blob.h"
#include "fileops.h"
#include "refs.h"
#define GIT_DIR "/.git/"
#define GIT_OBJECTS_DIR "objects/"
#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
#define GIT_REFS_DIR "refs/"
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
#define GIT_INDEX_FILE "index"
#define GIT_HEAD_FILE "HEAD"
#define GIT_SYMREF "ref: "
#define GIT_BRANCH_MASTER "master"
static const int OBJECT_TABLE_SIZE = 32;
......@@ -229,6 +227,13 @@ static git_repository *repository_alloc()
return NULL;
}
repo->ref_database = git_reference_database__alloc();
if (repo->ref_database == NULL) {
git_hashtable_free(repo->objects);
free(repo);
return NULL;
}
return repo;
}
......@@ -359,6 +364,8 @@ void git_repository_free(git_repository *repo)
git_hashtable_free(repo->objects);
git_reference_database__free(repo->ref_database);
if (repo->db != NULL)
git_odb_close(repo->db);
......@@ -579,8 +586,6 @@ static int repo_init_structure(repo_init *results)
static int repo_init_find_dir(repo_init *results, const char* path)
{
const int MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH = 66;
char temp_path[GIT_PATH_MAX];
int path_len;
int error = GIT_SUCCESS;
......@@ -628,3 +633,15 @@ cleanup:
free(results.path_repository);
return error;
}
int git_repository_reference_lookup(git_reference **reference_out, git_repository *repo, const char *name)
{
int error = GIT_SUCCESS;
int nesting_level = 0;
assert(repo && reference_out && name);
error = git_reference_lookup(reference_out, repo->ref_database, name, repo->path_repository, &nesting_level);
return error;
}
......@@ -8,6 +8,7 @@
#include "hashtable.h"
#include "index.h"
#include "refs.h"
typedef struct {
git_rawobj raw;
......@@ -27,6 +28,7 @@ struct git_repository {
git_odb *db;
git_index *index;
git_hashtable *objects;
git_reference_database *ref_database;
char *path_repository;
char *path_index;
......
......@@ -22,3 +22,15 @@ Categories
03__: Basic object writing.
04__: Parsing and loading commit data
05__: To be described
06__: To be described
07__: To be described
08__: To be described
09__: To be described
10__: Symbolic, loose and packed references reading and writing.
\ No newline at end of file
# pack-refs with: peeled
41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/heads/packed
5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/heads/packed-test
a4a7dce85cf63874e984719f4fdd239f5145052f
be3563ae3f795b2b4353bcce3a527ad0a4f7f644
4a202b346bb0fb0db7eff3cffeb3c70babbd2045
e90810b8df3e80c413d903f631643c716887138d
b25fa35b38051e4ae45d4222e795f9df2e43f1d1
7b4384978d2493e851f9cca7858815fac9b10980
#include "test_lib.h"
#include "test_helpers.h"
#include "refs.h"
static const char *loose_tag_ref_name = "refs/tags/test";
static const char *non_existing_tag_ref_name = "refs/tags/i-do-not-exist";
BEGIN_TEST(loose_tag_reference_looking_up)
git_repository *repo;
git_reference *reference;
git_object *object;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_reference_lookup(&reference, repo, loose_tag_ref_name));
must_be_true(reference->type == GIT_REF_OBJECT_ID);
must_be_true(reference->is_packed == 0);
must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0);
must_pass(git_repository_lookup(&object, repo, &((git_reference_object_id *)reference)->id, GIT_OBJ_ANY));
must_be_true(object != NULL);
must_be_true(git_object_type(object) == GIT_OBJ_TAG);
git_repository_free(repo);
END_TEST
BEGIN_TEST(non_existing_tag_reference_looking_up)
git_repository *repo;
git_reference *reference;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_fail(git_repository_reference_lookup(&reference, repo, non_existing_tag_ref_name));
git_repository_free(repo);
END_TEST
\ No newline at end of file
#include "test_lib.h"
#include "test_helpers.h"
#include "refs.h"
static const char *head_ref_name = "HEAD";
static const char *current_head_target = "refs/heads/master";
static const char *current_master_tip = "be3563ae3f795b2b4353bcce3a527ad0a4f7f644";
BEGIN_TEST(symbolic_reference_looking_up)
git_repository *repo;
git_reference *reference;
git_reference_symbolic *head_ref;
git_reference_object_id *target_ref;
git_object *object;
git_oid id;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_reference_lookup(&reference, repo, head_ref_name));
must_be_true(reference->type == GIT_REF_SYMBOLIC);
must_be_true(reference->is_packed == 0);
must_be_true(strcmp(reference->name, head_ref_name) == 0);
head_ref = (git_reference_symbolic *)reference;
must_be_true(head_ref->target != NULL);
must_be_true(head_ref->target->type == GIT_REF_OBJECT_ID); /* Current HEAD directly points to the object id reference */
must_be_true(strcmp(head_ref->target->name, current_head_target) == 0);
target_ref = (git_reference_object_id *)head_ref->target;
must_pass(git_repository_lookup(&object, repo, &target_ref->id, GIT_OBJ_ANY));
must_be_true(object != NULL);
must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);
git_oid_mkstr(&id, current_master_tip);
must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0);
git_repository_free(repo);
END_TEST
BEGIN_TEST(looking_up_head_then_master)
git_repository *repo;
git_reference *reference;
git_reference_symbolic *head_ref;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_reference_lookup(&reference, repo, head_ref_name));
head_ref = (git_reference_symbolic *)reference;
must_be_true(head_ref->target != NULL);
must_pass(git_repository_reference_lookup(&reference, repo, current_head_target));
must_be_true(head_ref->target == reference);
git_repository_free(repo);
END_TEST
BEGIN_TEST(looking_up_master_then_head)
git_repository *repo;
git_reference *reference, *master_ref;
git_reference_symbolic *head_ref;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_reference_lookup(&master_ref, repo, current_head_target));
must_pass(git_repository_reference_lookup(&reference, repo, head_ref_name));
head_ref = (git_reference_symbolic *)reference;
must_be_true(head_ref->target != NULL);
must_be_true(head_ref->target == master_ref);
git_repository_free(repo);
END_TEST
\ No newline at end of file
#include "test_lib.h"
#include "test_helpers.h"
#include "refs.h"
static const char *packed_head_name = "refs/heads/packed";
static const char *packed_test_head_name = "refs/heads/packed-test";
BEGIN_TEST(packed_reference_looking_up)
git_repository *repo;
git_reference *reference;
git_reference_object_id *packed_ref;
git_object *object;
git_oid id;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_reference_lookup(&reference, repo, packed_head_name));
must_be_true(reference->type == GIT_REF_OBJECT_ID);
must_be_true(reference->is_packed == 1);
must_be_true(strcmp(reference->name, packed_head_name) == 0);
packed_ref = (git_reference_object_id *)reference;
must_pass(git_repository_lookup(&object, repo, &packed_ref->id, GIT_OBJ_ANY));
must_be_true(object != NULL);
must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);
git_repository_free(repo);
END_TEST
BEGIN_TEST(packed_exists_but_more_recent_loose_reference_is_retrieved)
git_repository *repo;
git_reference *reference;
git_reference_object_id *packed_ref;
git_object *object;
git_oid id;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_reference_lookup(&reference, repo, packed_head_name)); /* Triggers load and eager parsing of all packed references */
must_pass(git_repository_reference_lookup(&reference, repo, packed_test_head_name));
must_be_true(reference->type == GIT_REF_OBJECT_ID);
must_be_true(reference->is_packed == 0);
must_be_true(strcmp(reference->name, packed_test_head_name) == 0);
git_repository_free(repo);
END_TEST
\ No newline at end of file
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