Commit 2f8a8ab2 by Vicent Marti

Refactor reference parsing code

Several changes have been committed to allow the user to create
in-memory references and write back to disk. Peeling of symbolic
references has been made explicit. Added getter and setter methods for
all attributes on a reference. Added corresponding documentation.

Signed-off-by: Vicent Marti <tanoku@gmail.com>
parent 9282e921
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "git2/repository.h" #include "git2/repository.h"
#include "git2/revwalk.h" #include "git2/revwalk.h"
#include "git2/refs.h"
#include "git2/object.h" #include "git2/object.h"
#include "git2/blob.h" #include "git2/blob.h"
......
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDE_git_refs_h__
#define INCLUDE_git_refs_h__
#include "common.h"
#include "types.h"
/**
* @file git2/refs.h
* @brief Git reference management routines
* @defgroup git_reference Git reference management routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Create a new reference.
*
* The reference will be empty and exclusively
* in-memory until it is filled with the setter
* methods and written back to disk using
* `git_reference_write`.
*
* @param ref_out Pointer to the newly created reference
* @param repo Repository where that reference exists
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_reference_new(git_reference **ref_out, git_repository *repo);
/**
* Get the OID pointed to by a reference.
*
* Only available if the reference is direct (i.e. not symbolic)
*
* @param ref The reference
* @return a pointer to the oid if available, NULL otherwise
*/
GIT_EXTERN(const git_oid *) git_reference_oid(git_reference *ref);
/**
* Get full name to the reference pointed by this reference
*
* Only available if the reference is symbolic
*
* @param ref The reference
* @return a pointer to the name if available, NULL otherwise
*/
GIT_EXTERN(const char *) git_reference_target(git_reference *ref);
/**
* Get the type of a reference
*
* Either direct (GIT_REF_OID) or symbolic (GIT_REF_SYMBOLIC)
*
* @param ref The reference
* @return the type
*/
GIT_EXTERN(git_rtype) git_reference_type(git_reference *ref);
/**
* Get the full name of a reference
*
* @param ref The reference
* @return the full name for the ref
*/
GIT_EXTERN(const char *) git_reference_name(git_reference *ref);
/**
* Resolve a symbolic reference
*
* Thie method iteratively peels a symbolic reference
* until it resolves to a direct reference to an OID.
*
* If a direct reference is passed as an argument,
* that reference is returned immediately
*
* @param resolved_ref Pointer to the peeled reference
* @param ref The reference
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_reference_resolve(git_reference **resolved_ref, git_reference *ref);
/**
* Write a reference back to disk.
*
* The reference must have a valid name and a valid target
* (either direct or symbolic).
*
* If the reference has been loaded from disk and no changes
* have been made, no action will take place.
*
* The writing to disk is atomic.
*
* @param ref The reference
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_reference_write(git_reference *ref);
/**
* Get the repository where a reference resides
*
* @param ref The reference
* @return a pointer to the repo
*/
GIT_EXTERN(git_repository *) git_reference_owner(git_reference *ref);
/**
* Set the name of a reference.
*
* This marks the reference as modified; changes
* won't take effect until it is manually written back
* to disk.
*
* @param ref The reference
* @param name The new name for the reference
*/
GIT_EXTERN(void) git_reference_set_name(git_reference *ref, const char *name);
/**
* Set the target reference of a reference.
*
* This converts the reference into a symbolic
* reference.
*
* This marks the reference as modified; changes
* won't take effect until it is manually written back
* to disk.
*
* @param ref The reference
* @param target The new target for the reference
*/
GIT_EXTERN(void) git_reference_set_target(git_reference *ref, const char *target);
/**
* Set the OID target of a reference.
*
* This converts the reference into a direct
* reference.
*
* This marks the reference as modified; changes
* won't take effect until it is manually written back
* to disk.
*
* @param ref The reference
* @param target The new target OID for the reference
*/
GIT_EXTERN(void) git_reference_set_oid(git_reference *ref, const git_oid *id);
/** @} */
GIT_END_DECL
#endif
...@@ -229,7 +229,7 @@ GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, ...@@ -229,7 +229,7 @@ GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path,
* @param name the long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...) * @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 * @return a reference to the reference
*/ */
GIT_EXTERN(int) git_repository_reference_lookup(git_reference **reference_out, git_repository *repo, const char *name); GIT_EXTERN(int) git_repository_lookup_ref(git_reference **reference_out, git_repository *repo, const char *name);
/** @} */ /** @} */
GIT_END_DECL GIT_END_DECL
......
...@@ -138,17 +138,11 @@ typedef struct git_signature { ...@@ -138,17 +138,11 @@ typedef struct git_signature {
/** In-memory representation of a reference. */ /** In-memory representation of a reference. */
typedef struct git_reference git_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. */ /** Basic type of any Git reference. */
typedef enum { typedef enum {
GIT_REF_ANY = -2, /** Reference can be any of the following */ GIT_REF_INVALID = -1, /** Invalid reference */
GIT_REF_OBJECT_ID = 0, /** A reference which points at an object id */ GIT_REF_OID = 1, /** A reference which points at an object id */
GIT_REF_SYMBOLIC = 1, /** A reference which points at another reference */ GIT_REF_SYMBOLIC = 2, /** A reference which points at another reference */
} git_rtype; } git_rtype;
/** @} */ /** @} */
......
...@@ -9,40 +9,32 @@ ...@@ -9,40 +9,32 @@
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/" #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/" #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
#define GIT_SYMREF "ref:" #define GIT_SYMREF "ref: "
#define GIT_PACKEDREFS_FILE "packed-refs" #define GIT_PACKEDREFS_FILE "packed-refs"
#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled \n" #define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled \n"
#define MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH 100 #define MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH 100
struct git_reference { struct git_reference {
git_repository *owner;
git_rtype type; git_rtype type;
char *name; char *name;
unsigned is_packed:1; unsigned packed:1,
}; modified:1;
struct git_reference_object_id {
git_reference base;
git_oid id; union {
char *ref;
git_oid oid;
} target;
}; };
struct git_reference_symbolic { typedef struct {
git_reference base; git_hashtable *cache;
unsigned pack_loaded:1;
} git_refcache;
git_reference *target; void git_repository__refcache_free(git_refcache *refs);
}; int git_repository__refcache_init(git_refcache *refs);
typedef struct { #endif
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
...@@ -227,8 +227,7 @@ static git_repository *repository_alloc() ...@@ -227,8 +227,7 @@ static git_repository *repository_alloc()
return NULL; return NULL;
} }
repo->ref_database = git_reference_database__alloc(); if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) {
if (repo->ref_database == NULL) {
git_hashtable_free(repo->objects); git_hashtable_free(repo->objects);
free(repo); free(repo);
return NULL; return NULL;
...@@ -364,7 +363,7 @@ void git_repository_free(git_repository *repo) ...@@ -364,7 +363,7 @@ void git_repository_free(git_repository *repo)
git_hashtable_free(repo->objects); git_hashtable_free(repo->objects);
git_reference_database__free(repo->ref_database); git_repository__refcache_free(&repo->references);
if (repo->db != NULL) if (repo->db != NULL)
git_odb_close(repo->db); git_odb_close(repo->db);
...@@ -633,15 +632,3 @@ cleanup: ...@@ -633,15 +632,3 @@ cleanup:
free(results.path_repository); free(results.path_repository);
return error; 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;
}
...@@ -28,7 +28,8 @@ struct git_repository { ...@@ -28,7 +28,8 @@ struct git_repository {
git_odb *db; git_odb *db;
git_index *index; git_index *index;
git_hashtable *objects; git_hashtable *objects;
git_reference_database *ref_database;
git_refcache references;
char *path_repository; char *path_repository;
char *path_index; char *path_index;
......
...@@ -12,18 +12,15 @@ BEGIN_TEST(loose_tag_reference_looking_up) ...@@ -12,18 +12,15 @@ BEGIN_TEST(loose_tag_reference_looking_up)
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_lookup_ref(&reference, repo, loose_tag_ref_name));
must_pass(git_repository_reference_lookup(&reference, repo, loose_tag_ref_name)); must_be_true(reference->type == GIT_REF_OID);
must_be_true(reference->type == GIT_REF_OBJECT_ID); must_be_true(reference->packed == 0);
must_be_true(reference->is_packed == 0);
must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0); must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0);
must_pass(git_repository_lookup(&object, repo, git_reference_oid(reference), GIT_OBJ_ANY));
must_pass(git_repository_lookup(&object, repo, &((git_reference_object_id *)reference)->id, GIT_OBJ_ANY));
must_be_true(object != NULL); must_be_true(object != NULL);
must_be_true(git_object_type(object) == GIT_OBJ_TAG); must_be_true(git_object_type(object) == GIT_OBJ_TAG);
git_repository_free(repo); git_repository_free(repo);
END_TEST END_TEST
...@@ -32,8 +29,7 @@ BEGIN_TEST(non_existing_tag_reference_looking_up) ...@@ -32,8 +29,7 @@ BEGIN_TEST(non_existing_tag_reference_looking_up)
git_reference *reference; git_reference *reference;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_fail(git_repository_lookup_ref(&reference, repo, non_existing_tag_ref_name));
must_fail(git_repository_reference_lookup(&reference, repo, non_existing_tag_ref_name));
git_repository_free(repo); git_repository_free(repo);
END_TEST END_TEST
\ No newline at end of file
...@@ -8,54 +8,37 @@ static const char *current_master_tip = "be3563ae3f795b2b4353bcce3a527ad0a4f7f64 ...@@ -8,54 +8,37 @@ static const char *current_master_tip = "be3563ae3f795b2b4353bcce3a527ad0a4f7f64
BEGIN_TEST(symbolic_reference_looking_up) BEGIN_TEST(symbolic_reference_looking_up)
git_repository *repo; git_repository *repo;
git_reference *reference; git_reference *reference, *resolved_ref;
git_reference_symbolic *head_ref;
git_reference_object_id *target_ref;
git_object *object; git_object *object;
git_oid id; git_oid id;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_lookup_ref(&reference, repo, head_ref_name));
must_pass(git_repository_reference_lookup(&reference, repo, head_ref_name));
must_be_true(reference->type == GIT_REF_SYMBOLIC); must_be_true(reference->type == GIT_REF_SYMBOLIC);
must_be_true(reference->is_packed == 0); must_be_true(reference->packed == 0);
must_be_true(strcmp(reference->name, head_ref_name) == 0); must_be_true(strcmp(reference->name, head_ref_name) == 0);
head_ref = (git_reference_symbolic *)reference; must_pass(git_reference_resolve(&resolved_ref, reference));
must_be_true(head_ref->target != NULL); must_be_true(resolved_ref->type == GIT_REF_OID);
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, git_reference_oid(resolved_ref), GIT_OBJ_ANY));
must_pass(git_repository_lookup(&object, repo, &target_ref->id, GIT_OBJ_ANY));
must_be_true(object != NULL); must_be_true(object != NULL);
must_be_true(git_object_type(object) == GIT_OBJ_COMMIT); must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);
git_oid_mkstr(&id, current_master_tip); git_oid_mkstr(&id, current_master_tip);
must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0); must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0);
git_repository_free(repo); git_repository_free(repo);
END_TEST END_TEST
BEGIN_TEST(looking_up_head_then_master) BEGIN_TEST(looking_up_head_then_master)
git_repository *repo; git_repository *repo;
git_reference *reference; git_reference *reference;
git_reference_symbolic *head_ref;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_lookup_ref(&reference, repo, head_ref_name));
must_pass(git_repository_lookup_ref(&reference, 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_pass(git_repository_reference_lookup(&reference, repo, current_head_target));
must_be_true(head_ref->target == reference);
git_repository_free(repo); git_repository_free(repo);
END_TEST END_TEST
...@@ -63,19 +46,10 @@ END_TEST ...@@ -63,19 +46,10 @@ END_TEST
BEGIN_TEST(looking_up_master_then_head) BEGIN_TEST(looking_up_master_then_head)
git_repository *repo; git_repository *repo;
git_reference *reference, *master_ref; git_reference *reference, *master_ref;
git_reference_symbolic *head_ref;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_lookup_ref(&master_ref, repo, current_head_target));
must_pass(git_repository_lookup_ref(&reference, repo, head_ref_name));
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); git_repository_free(repo);
END_TEST END_TEST
\ No newline at end of file
...@@ -8,45 +8,32 @@ static const char *packed_test_head_name = "refs/heads/packed-test"; ...@@ -8,45 +8,32 @@ static const char *packed_test_head_name = "refs/heads/packed-test";
BEGIN_TEST(packed_reference_looking_up) BEGIN_TEST(packed_reference_looking_up)
git_repository *repo; git_repository *repo;
git_reference *reference; git_reference *reference;
git_reference_object_id *packed_ref;
git_object *object; git_object *object;
git_oid id;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_lookup_ref(&reference, repo, packed_head_name));
must_pass(git_repository_reference_lookup(&reference, repo, packed_head_name)); must_be_true(reference->type == GIT_REF_OID);
must_be_true(reference->type == GIT_REF_OBJECT_ID); must_be_true(reference->packed == 1);
must_be_true(reference->is_packed == 1);
must_be_true(strcmp(reference->name, packed_head_name) == 0); must_be_true(strcmp(reference->name, packed_head_name) == 0);
packed_ref = (git_reference_object_id *)reference; must_pass(git_repository_lookup(&object, repo, git_reference_oid(reference), GIT_OBJ_ANY));
must_pass(git_repository_lookup(&object, repo, &packed_ref->id, GIT_OBJ_ANY));
must_be_true(object != NULL); must_be_true(object != NULL);
must_be_true(git_object_type(object) == GIT_OBJ_COMMIT); must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);
git_repository_free(repo); git_repository_free(repo);
END_TEST END_TEST
BEGIN_TEST(packed_exists_but_more_recent_loose_reference_is_retrieved) BEGIN_TEST(packed_exists_but_more_recent_loose_reference_is_retrieved)
git_repository *repo; git_repository *repo;
git_reference *reference; 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_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_lookup_ref(&reference, repo, packed_head_name));
must_pass(git_repository_lookup_ref(&reference, repo, packed_test_head_name));
must_pass(git_repository_reference_lookup(&reference, repo, packed_head_name)); /* Triggers load and eager parsing of all packed references */ must_be_true(reference->type == GIT_REF_OID);
must_be_true(reference->packed == 0);
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); must_be_true(strcmp(reference->name, packed_test_head_name) == 0);
git_repository_free(repo); git_repository_free(repo);
END_TEST 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