Commit f8e8c8d4 by Vicent Martí

Merge pull request #472 from libgit2/new-references

References! References! References!
parents d3104fa0 62dd6d16
......@@ -23,8 +23,7 @@ GIT_BEGIN_DECL
/**
* Lookup a reference by its name in a repository.
*
* The generated reference is owned by the repository and
* should not be freed by the user.
* The generated reference must be freed by the user.
*
* @param reference_out pointer to the looked-up reference
* @param repo the repository to look up the reference
......@@ -39,8 +38,7 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_reposito
* The reference will be created in the repository and written
* to the disk.
*
* This reference is owned by the repository and shall not
* be free'd by the user.
* The generated reference must be freed by the user.
*
* If `force` is true and there already exists a reference
* with the same name, it will be overwritten.
......@@ -60,8 +58,7 @@ GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repos
* The reference will be created in the repository and written
* to the disk.
*
* This reference is owned by the repository and shall not
* be free'd by the user.
* The generated reference must be freed by the user.
*
* If `force` is true and there already exists a reference
* with the same name, it will be overwritten.
......@@ -119,8 +116,13 @@ GIT_EXTERN(const char *) git_reference_name(git_reference *ref);
* Thie method iteratively peels a symbolic reference
* until it resolves to a direct reference to an OID.
*
* The peeled reference is returned in the `resolved_ref`
* argument, and must be freed manually once it's no longer
* needed.
*
* If a direct reference is passed as an argument,
* that reference is returned immediately
* a copy of that reference is returned. This copy must
* be manually freed too.
*
* @param resolved_ref Pointer to the peeled reference
* @param ref The reference
......@@ -173,9 +175,19 @@ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
* The new name will be checked for validity and may be
* modified into a normalized form.
*
* The refernece will be immediately renamed in-memory
* The given git_reference will be updated in place.
*
* The reference will be immediately renamed in-memory
* and on disk.
*
* If the `force` flag is not enabled, and there's already
* a reference with the given name, the renaming will fail.
*
* @param ref The reference to rename
* @param new_name The new name for the reference
* @param force Overwrite an existing reference
* @return GIT_SUCCESS or an error code
*
*/
GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, int force);
......@@ -187,6 +199,8 @@ GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, i
* The reference will be immediately removed on disk and from
* memory. The given reference pointer will no longer be valid.
*
* @param ref The reference to remove
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_reference_delete(git_reference *ref);
......@@ -200,9 +214,6 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
* Once the `packed-refs` file has been written properly,
* the loose references will be removed from disk.
*
* WARNING: calling this method may invalidate any existing
* references previously loaded on the cache.
*
* @param repo Repository where the loose refs will be packed
* @return GIT_SUCCESS or an error code
*/
......@@ -253,6 +264,41 @@ GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo,
*/
GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload);
/**
* Check if a reference has been loaded from a packfile
*
* @param ref A git reference
* @return 0 in case it's not packed; 1 otherwise
*/
GIT_EXTERN(int) git_reference_is_packed(git_reference *ref);
/**
* Reload a reference from disk
*
* Reference pointers may become outdated if the Git
* repository is accessed simultaneously by other clients
* whilt the library is open.
*
* This method forces a reload of the reference from disk,
* to ensure that the provided information is still
* reliable.
*
* If the reload fails (e.g. the reference no longer exists
* on disk, or has become corrupted), an error code will be
* returned and the reference pointer will be invalidated.
*
* @param ref The reference to reload
* @return GIT_SUCCESS on success, or an error code
*/
GIT_EXTERN(int) git_reference_reload(git_reference *ref);
/**
* Free the given reference
*
* @param ref git_reference
*/
GIT_EXTERN(void) git_reference_free(git_reference *ref);
/** @} */
GIT_END_DECL
#endif
......@@ -137,15 +137,18 @@ int git_commit_create(
if (error == GIT_SUCCESS && update_ref != NULL) {
git_reference *head;
git_reference *target;
error = git_reference_lookup(&head, repo, update_ref);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to create commit");
error = git_reference_resolve(&head, head);
error = git_reference_resolve(&target, head);
if (error < GIT_SUCCESS) {
if (error != GIT_ENOTFOUND)
if (error != GIT_ENOTFOUND) {
git_reference_free(head);
return git__rethrow(error, "Failed to create commit");
}
/*
* The target of the reference was not found. This can happen
* just after a repository has been initialized (the master
......@@ -153,10 +156,19 @@ int git_commit_create(
* point to) or after an orphan checkout, so if the target
* branch doesn't exist yet, create it and return.
*/
return git_reference_create_oid(&head, repo, git_reference_target(head), oid, 1);
error = git_reference_create_oid(&target, repo, git_reference_target(head), oid, 1);
git_reference_free(head);
if (error == GIT_SUCCESS)
git_reference_free(target);
return error;
}
error = git_reference_set_oid(head, oid);
error = git_reference_set_oid(target, oid);
git_reference_free(head);
git_reference_free(target);
}
if (error < GIT_SUCCESS)
......
......@@ -215,23 +215,37 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
const git_oid *oid;
if ((error = git_reference_resolve(&r, ref)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to write reflog. Cannot resolve reference `%s`", ref->name);
return git__rethrow(error,
"Failed to write reflog. Cannot resolve reference `%s`", ref->name);
oid = git_reference_oid(r);
if (oid == NULL)
return git__throw(GIT_ERROR, "Failed to write reflog. Cannot resolve reference `%s`", r->name);
if (oid == NULL) {
git_reference_free(r);
return git__throw(GIT_ERROR,
"Failed to write reflog. Cannot resolve reference `%s`", r->name);
}
git_oid_to_string(new, GIT_OID_HEXSZ+1, oid);
git_path_join_n(log_path, 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
git_path_join_n(log_path, 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
git_reference_free(r);
if (git_futils_exists(log_path)) {
if ((error = git_futils_mkpath2file(log_path, GIT_REFLOG_DIR_MODE)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to write reflog. Cannot create reflog directory");
error = git_futils_mkpath2file(log_path, GIT_REFLOG_DIR_MODE);
if (error < GIT_SUCCESS)
return git__rethrow(error,
"Failed to write reflog. Cannot create reflog directory");
} else if (git_futils_isfile(log_path)) {
return git__throw(GIT_ERROR, "Failed to write reflog. `%s` is directory", log_path);
} else if (oid_old == NULL)
return git__throw(GIT_ERROR, "Failed to write reflog. Old OID cannot be NULL for existing reference");
return git__throw(GIT_ERROR,
"Failed to write reflog. `%s` is directory", log_path);
} else if (oid_old == NULL) {
return git__throw(GIT_ERROR,
"Failed to write reflog. Old OID cannot be NULL for existing reference");
}
if (oid_old)
git_oid_to_string(old, GIT_OID_HEXSZ+1, oid_old);
......
......@@ -33,21 +33,23 @@
#define GIT_REFNAME_MAX 1024
struct git_reference {
unsigned int flags;
git_repository *owner;
char *name;
unsigned int type;
time_t mtime;
union {
git_oid oid;
char *symbolic;
} target;
};
typedef struct {
git_hashtable *packfile;
git_hashtable *loose_cache;
time_t packfile_time;
} git_refcache;
void git_repository__refcache_free(git_refcache *refs);
int git_repository__refcache_init(git_refcache *refs);
int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name);
int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);
......
......@@ -172,11 +172,6 @@ static git_repository *repository_alloc(void)
return NULL;
}
if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) {
git__free(repo);
return NULL;
}
return repo;
}
......@@ -603,8 +598,14 @@ static int repo_init_reinit(const char *repository_path, int is_bare)
static int repo_init_createhead(git_repository *repo)
{
int error;
git_reference *head_reference;
return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE, 0);
error = git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE, 0);
git_reference_free(head_reference);
return error;
}
static int repo_init_structure(const char *git_dir, int is_bare)
......@@ -715,10 +716,15 @@ int git_repository_head_detached(git_repository *repo)
if (error < GIT_SUCCESS)
return error;
if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
git_reference_free(ref);
return 0;
}
error = git_odb_read_header(&_size, &type, repo->db, git_reference_oid(ref));
git_reference_free(ref);
if (error < GIT_SUCCESS)
return error;
......@@ -730,7 +736,7 @@ int git_repository_head_detached(git_repository *repo)
int git_repository_head(git_reference **head_out, git_repository *repo)
{
git_reference *ref;
git_reference *ref, *resolved_ref;
int error;
*head_out = NULL;
......@@ -739,11 +745,15 @@ int git_repository_head(git_reference **head_out, git_repository *repo)
if (error < GIT_SUCCESS)
return git__rethrow(GIT_ENOTAREPO, "Failed to locate the HEAD");
error = git_reference_resolve(&ref, ref);
if (error < GIT_SUCCESS)
error = git_reference_resolve(&resolved_ref, ref);
if (error < GIT_SUCCESS) {
git_reference_free(ref);
return git__rethrow(error, "Failed to resolve the HEAD");
}
git_reference_free(ref);
*head_out = ref;
*head_out = resolved_ref;
return GIT_SUCCESS;
}
......@@ -754,25 +764,36 @@ int git_repository_head_orphan(git_repository *repo)
error = git_repository_head(&ref, repo);
if (error == GIT_SUCCESS)
git_reference_free(ref);
return error == GIT_ENOTFOUND ? 1 : error;
}
int git_repository_is_empty(git_repository *repo)
{
git_reference *head, *branch;
git_reference *head = NULL, *branch = NULL;
int error;
error = git_reference_lookup(&head, repo, "HEAD");
if (error < GIT_SUCCESS)
return git__throw(error, "Corrupted repository. HEAD does not exist");
if (git_reference_type(head) != GIT_REF_SYMBOLIC)
if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
git_reference_free(head);
return 0;
}
if (strcmp(git_reference_target(head), "refs/heads/master") != 0)
if (strcmp(git_reference_target(head), "refs/heads/master") != 0) {
git_reference_free(head);
return 0;
}
error = git_reference_resolve(&branch, head);
git_reference_free(head);
git_reference_free(branch);
return error == GIT_ENOTFOUND ? 1 : error;
}
......
......@@ -153,6 +153,8 @@ static int retrieve_tag_reference(git_reference **tag_reference_out, char *ref_n
git_reference *tag_ref;
int error;
*tag_reference_out = NULL;
git_path_join(ref_name_out, GIT_REFS_TAGS_DIR, tag_name);
error = git_reference_lookup(&tag_ref, repo, ref_name_out);
if (error < GIT_SUCCESS)
......@@ -224,6 +226,7 @@ static int git_tag_create__internal(
break;
default:
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
......@@ -232,6 +235,7 @@ static int git_tag_create__internal(
if (new_ref != NULL) {
if (!allow_ref_overwrite) {
git_oid_cpy(oid, git_reference_oid(new_ref));
git_reference_free(new_ref);
return git__throw(GIT_EEXISTS, "Tag already exists");
} else {
should_update_ref = 1;
......@@ -239,8 +243,10 @@ static int git_tag_create__internal(
}
if (create_tag_annotation) {
if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS)
if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS) {
git_reference_free(new_ref);
return error;
}
} else
git_oid_cpy(oid, git_object_id(target));
......@@ -249,6 +255,8 @@ static int git_tag_create__internal(
else
error = git_reference_set_oid(new_ref, oid);
git_reference_free(new_ref);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag");
}
......@@ -281,7 +289,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
git_odb_stream *stream;
git_odb_object *target_obj;
git_reference *new_ref;
git_reference *new_ref = NULL;
char ref_name[GIT_REFNAME_MAX];
assert(oid && buffer);
......@@ -309,6 +317,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
break;
default:
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
......@@ -317,6 +326,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
if (new_ref != NULL) {
if (!allow_ref_overwrite) {
git_oid_cpy(oid, git_reference_oid(new_ref));
git_reference_free(new_ref);
return git__throw(GIT_EEXISTS, "Tag already exists");
} else {
should_update_ref = 1;
......@@ -324,22 +334,28 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
}
/* write the buffer */
if ((error = git_odb_open_wstream(&stream, repo->db, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS)
if ((error = git_odb_open_wstream(&stream, repo->db, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS) {
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
stream->write(stream, buffer, strlen(buffer));
error = stream->finalize_write(oid, stream);
stream->free(stream);
if (error < GIT_SUCCESS)
if (error < GIT_SUCCESS) {
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
if (!should_update_ref)
error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0);
else
error = git_reference_set_oid(new_ref, oid);
git_reference_free(new_ref);
git_signature_free(tag.tagger);
git__free(tag.tag_name);
git__free(tag.message);
......
......@@ -54,7 +54,7 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
{
const char peeled[] = "^{}";
git_remote_head *head;
git_reference *ref;
git_reference *ref, *resolved_ref;
git_object *obj = NULL;
int error = GIT_SUCCESS, peel_len, ret;
......@@ -72,7 +72,7 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
if (error < GIT_SUCCESS)
goto out;
error = git_reference_resolve(&ref, ref);
error = git_reference_resolve(&resolved_ref, ref);
if (error < GIT_SUCCESS)
goto out;
......@@ -111,6 +111,9 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
goto out;
out:
git_reference_free(ref);
git_reference_free(resolved_ref);
git_object_close(obj);
if (error < GIT_SUCCESS) {
git__free(head->name);
......
......@@ -766,6 +766,8 @@ BEGIN_TEST(root0, "create a root commit")
git__free(head_old);
git_commit_close(commit);
git_repository_free(repo);
git_reference_free(head);
END_TEST
BEGIN_SUITE(commit)
......
......@@ -197,7 +197,6 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again")
git_tag_close(tag);
git_repository_free(repo);
END_TEST
BEGIN_TEST(write2, "Attempt to write a tag bearing the same name than an already existing tag")
......@@ -266,6 +265,7 @@ BEGIN_TEST(write3, "Replace an already existing tag")
close_temp_repo(repo);
git_reference_free(ref_tag);
END_TEST
BEGIN_TEST(write4, "write a lightweight tag to the repository and read it again")
......@@ -296,6 +296,8 @@ BEGIN_TEST(write4, "write a lightweight tag to the repository and read it again"
must_pass(git_tag_delete(repo, "light-tag"));
git_repository_free(repo);
git_reference_free(ref_tag);
END_TEST
BEGIN_TEST(write5, "Attempt to write a lightweight tag bearing the same name than an already existing tag")
......@@ -334,6 +336,8 @@ BEGIN_TEST(delete0, "Delete an already existing tag")
must_fail(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b"));
close_temp_repo(repo);
git_reference_free(ref_tag);
END_TEST
BEGIN_SUITE(tag)
......
......@@ -286,6 +286,8 @@ BEGIN_TEST(detached0, "test if HEAD is detached")
must_be_true(git_repository_head_detached(repo) == 0);
git_repository_free(repo);
git_reference_free(ref);
END_TEST
BEGIN_TEST(orphan0, "test if HEAD is orphan")
......@@ -305,6 +307,8 @@ BEGIN_TEST(orphan0, "test if HEAD is orphan")
must_be_true(git_repository_head_orphan(repo) == 0);
git_repository_free(repo);
git_reference_free(ref);
END_TEST
#define DISCOVER_FOLDER TEMP_REPO_FOLDER "discover.git"
......
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