From 92f95a170c76ffde80010cbbd6b03c0bf8dded1d Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sun, 12 May 2013 14:16:13 +0200 Subject: [PATCH 01/12] refs: Centralize reference creation logic --- src/refs.c | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/refs.c b/src/refs.c index 472a79890..7b4730cd8 100644 --- a/src/refs.c +++ b/src/refs.c @@ -335,6 +335,8 @@ static int reference__create( git_reference *ref = NULL; int error = 0; + assert(repo && name); + if (ref_out) *ref_out = NULL; @@ -347,10 +349,29 @@ static int reference__create( return error; if (oid != NULL) { + git_odb *odb; + assert(symbolic == NULL); - ref = git_reference__alloc(normalized, oid, NULL); + + /* Sanity check the reference being created - target must exist. */ + if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) + return error; + + if (!git_odb_exists(odb, oid)) { + giterr_set(GITERR_REFERENCE, + "Target OID for the reference doesn't exist on the repository"); + return -1; + } + + ref = git_reference__alloc(name, oid, NULL); } else { - ref = git_reference__alloc_symbolic(normalized, symbolic); + char normalized_target[GIT_REFNAME_MAX]; + + if ((error = git_reference__normalize_name_lax( + normalized_target, sizeof(normalized_target), symbolic)) < 0) + return error; + + ref = git_reference__alloc_symbolic(name, normalized_target); } GITERR_CHECK_ALLOC(ref); @@ -375,20 +396,7 @@ int git_reference_create( const git_oid *oid, int force) { - git_odb *odb; - int error = 0; - - assert(repo && name && oid); - - /* Sanity check the reference being created - target must exist. */ - if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) - return error; - - if (!git_odb_exists(odb, oid)) { - giterr_set(GITERR_REFERENCE, - "Target OID for the reference doesn't exist on the repository"); - return -1; - } + assert(oid); return reference__create(ref_out, repo, name, oid, NULL, force); } @@ -400,16 +408,9 @@ int git_reference_symbolic_create( const char *target, int force) { - char normalized[GIT_REFNAME_MAX]; - int error = 0; + assert(target); - assert(repo && name && target); - - if ((error = git_reference__normalize_name_lax( - normalized, sizeof(normalized), target)) < 0) - return error; - - return reference__create(ref_out, repo, name, NULL, normalized, force); + return reference__create(ref_out, repo, name, NULL, target, force); } int git_reference_set_target( From bba25f39a29c7913bab97fa6e8ac2ccb78ea33b6 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Mon, 13 May 2013 16:21:09 +0200 Subject: [PATCH 02/12] refs: Introduce git_reference_create_with_log() --- include/git2/refs.h | 43 ++++++++++++++++++++++++++++ src/refs.c | 58 ++++++++++++++++++++++++++++++++++++-- tests/refs/createwithlog.c | 52 ++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 tests/refs/createwithlog.c diff --git a/include/git2/refs.h b/include/git2/refs.h index 4041947f6..049b7fa72 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -130,6 +130,49 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor */ GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force); +/** + * Create a new direct reference and update the reflog with a given + * message. + * + * A direct reference (also called an object id reference) refers directly + * to a specific object id (a.k.a. OID or SHA) in the repository. The id + * permanently refers to the object (although the reference itself can be + * moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0" + * refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977. + * + * The direct reference will be created in the repository and written to + * the disk. The generated reference object must be freed by the user. + * + * Valid reference names must follow one of two patterns: + * + * 1. Top-level names must contain only capital letters and underscores, + * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). + * 2. Names prefixed with "refs/" can be almost anything. You must avoid + * the characters '~', '^', ':', '\\', '?', '[', and '*', and the + * sequences ".." and "@{" which have special meaning to revparse. + * + * This function will return an error if a reference already exists with the + * given name unless `force` is true, in which case it will be overwritten. + * + * @param out Pointer to the newly created reference + * @param repo Repository where that reference will live + * @param name The name of the reference + * @param id The object id pointed to by the reference. + * @param force Overwrite existing references + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message that has to be appended + * to the reflog + * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code + */ +GIT_EXTERN(int) git_reference_create_with_log( + git_reference **out, + git_repository *repo, + const char *name, + const git_oid *id, + int force, + const git_signature *signature, + const char *log_message); + /** * Get the OID pointed to by a direct reference. * diff --git a/src/refs.c b/src/refs.c index 7b4730cd8..e00041581 100644 --- a/src/refs.c +++ b/src/refs.c @@ -322,13 +322,44 @@ const char *git_reference_symbolic_target(const git_reference *ref) return ref->target.symbolic; } +static int feed_reflog( + const git_reference *ref, + const git_signature *signature, + const char *log_message) +{ + + git_reflog *reflog = NULL; + git_oid peeled_ref_oid; + int error; + + if ((error = git_reflog_read(&reflog, ref)) < 0) + goto cleanup; + + if ((error = git_reference_name_to_id(&peeled_ref_oid, + git_reference_owner(ref), git_reference_name(ref))) < 0) + goto cleanup; + + if ((error = git_reflog_append(reflog, &peeled_ref_oid, + signature, log_message)) < 0) + goto cleanup; + + error = git_reflog_write(reflog); + +cleanup: + git_reflog_free(reflog); + + return 0; +} + static int reference__create( git_reference **ref_out, git_repository *repo, const char *name, const git_oid *oid, const char *symbolic, - int force) + int force, + const git_signature *signature, + const char *log_message) { char normalized[GIT_REFNAME_MAX]; git_refdb *refdb; @@ -336,6 +367,7 @@ static int reference__create( int error = 0; assert(repo && name); + assert(!((signature == NULL) ^ (log_message == NULL))); if (ref_out) *ref_out = NULL; @@ -381,6 +413,11 @@ static int reference__create( return error; } + if (log_message && (error = feed_reflog(ref, signature, log_message)) < 0) { + git_reference_free(ref); + return error; + } + if (ref_out == NULL) git_reference_free(ref); else @@ -398,7 +435,22 @@ int git_reference_create( { assert(oid); - return reference__create(ref_out, repo, name, oid, NULL, force); + return reference__create(ref_out, repo, name, oid, NULL, force, NULL, NULL); +} + +int git_reference_create_with_log( + git_reference **ref_out, + git_repository *repo, + const char *name, + const git_oid *oid, + int force, + const git_signature *signature, + const char *log_message) +{ + assert(oid && signature && log_message); + + return reference__create( + ref_out, repo, name, oid, NULL, force, signature, log_message); } int git_reference_symbolic_create( @@ -410,7 +462,7 @@ int git_reference_symbolic_create( { assert(target); - return reference__create(ref_out, repo, name, NULL, target, force); + return reference__create(ref_out, repo, name, NULL, target, force, NULL, NULL); } int git_reference_set_target( diff --git a/tests/refs/createwithlog.c b/tests/refs/createwithlog.c new file mode 100644 index 000000000..10e10cec8 --- /dev/null +++ b/tests/refs/createwithlog.c @@ -0,0 +1,52 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" +#include "ref_helpers.h" + +static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; +static const char *current_head_target = "refs/heads/master"; + +static git_repository *g_repo; + +void test_refs_createwithlog__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_createwithlog__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_createwithlog__creating_a_direct_reference_adds_a_reflog_entry(void) +{ + git_reference *reference; + git_oid id; + git_signature *signature; + git_reflog *reflog; + const git_reflog_entry *entry; + + const char *name = "refs/heads/new-head"; + const char *message = "You've been logged, mate!"; + + git_oid_fromstr(&id, current_master_tip); + + cl_git_pass(git_signature_now(&signature, "foo", "foo@bar")); + + cl_git_pass( + git_reference_create_with_log(&reference, g_repo, name, &id, 0, signature, message)); + + cl_git_pass(git_reflog_read(&reflog, reference)); + cl_assert_equal_sz(1, git_reflog_entrycount(reflog)); + + entry = git_reflog_entry_byindex(reflog, 0); + cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); + cl_assert(git_oid_cmp(&id, &entry->oid_cur) == 0); + cl_assert_equal_s(message, entry->msg); + + git_reflog_free(reflog); + git_reference_free(reference); + git_signature_free(signature); +} From 56ad3782e08cd1b2d26eee4014e77fac7a6c2414 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Mon, 13 May 2013 17:44:39 +0200 Subject: [PATCH 03/12] refs: Introduce git_reference_symbolic_create_with_log() --- include/git2/refs.h | 42 ++++++++++++++++++++++++++++++++++++++ src/refs.c | 15 ++++++++++++++ tests/refs/createwithlog.c | 31 ++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/include/git2/refs.h b/include/git2/refs.h index 049b7fa72..e47077354 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -98,6 +98,48 @@ GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, co */ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force); +/** + * Create a new symbolic reference and update the reflog with a given + * message. + * + * A symbolic reference is a reference name that refers to another + * reference name. If the other name moves, the symbolic name will move, + * too. As a simple example, the "HEAD" reference might refer to + * "refs/heads/master" while on the "master" branch of a repository. + * + * The symbolic reference will be created in the repository and written to + * the disk. The generated reference object must be freed by the user. + * + * Valid reference names must follow one of two patterns: + * + * 1. Top-level names must contain only capital letters and underscores, + * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). + * 2. Names prefixed with "refs/" can be almost anything. You must avoid + * the characters '~', '^', ':', '\\', '?', '[', and '*', and the + * sequences ".." and "@{" which have special meaning to revparse. + * + * This function will return an error if a reference already exists with the + * given name unless `force` is true, in which case it will be overwritten. + * + * @param out Pointer to the newly created reference + * @param repo Repository where that reference will live + * @param name The name of the reference + * @param target The target of the reference + * @param force Overwrite existing references + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message that has to be appended + * to the reflog + * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code + */ +GIT_EXTERN(int) git_reference_symbolic_create_with_log( + git_reference **out, + git_repository *repo, + const char *name, + const char *target, + int force, + const git_signature *signature, + const char *log_message); + /** * Create a new direct reference. * diff --git a/src/refs.c b/src/refs.c index e00041581..7c97c1627 100644 --- a/src/refs.c +++ b/src/refs.c @@ -465,6 +465,21 @@ int git_reference_symbolic_create( return reference__create(ref_out, repo, name, NULL, target, force, NULL, NULL); } +int git_reference_symbolic_create_with_log( + git_reference **ref_out, + git_repository *repo, + const char *name, + const char *target, + int force, + const git_signature *signature, + const char* log_message) +{ + assert(target && signature && log_message); + + return reference__create( + ref_out, repo, name, NULL, target, force, signature, log_message); +} + int git_reference_set_target( git_reference **out, git_reference *ref, diff --git a/tests/refs/createwithlog.c b/tests/refs/createwithlog.c index 10e10cec8..34ab8067f 100644 --- a/tests/refs/createwithlog.c +++ b/tests/refs/createwithlog.c @@ -50,3 +50,34 @@ void test_refs_createwithlog__creating_a_direct_reference_adds_a_reflog_entry(vo git_reference_free(reference); git_signature_free(signature); } + +void test_refs_createwithlog__creating_a_symbolic_reference_adds_a_reflog_entry(void) +{ + git_reference *reference; + git_oid id; + git_signature *signature; + git_reflog *reflog; + const git_reflog_entry *entry; + + const char *name = "ANOTHER_HEAD_TRACKER"; + const char *message = "You've been logged, mate!"; + + git_oid_fromstr(&id, current_master_tip); + + cl_git_pass(git_signature_now(&signature, "foo", "foo@bar")); + + cl_git_pass(git_reference_symbolic_create_with_log(&reference, g_repo, + name, current_head_target, 0, signature, message)); + + cl_git_pass(git_reflog_read(&reflog, reference)); + cl_assert_equal_sz(1, git_reflog_entrycount(reflog)); + + entry = git_reflog_entry_byindex(reflog, 0); + cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); + cl_assert(git_oid_cmp(&id, &entry->oid_cur) == 0); + cl_assert_equal_s(message, entry->msg); + + git_reflog_free(reflog); + git_reference_free(reference); + git_signature_free(signature); +} From 14ab0e100e9474c5d1fda37fb0f4eec9ee0e9d13 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Tue, 14 May 2013 16:07:33 +0200 Subject: [PATCH 04/12] refs: Introduce git_reference_set_target_with_log() --- include/git2/refs.h | 23 +++++++++++++++ src/refs.c | 36 ++++++++++++++++++++--- tests/refs/settargetwithlog.c | 55 +++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 tests/refs/settargetwithlog.c diff --git a/include/git2/refs.h b/include/git2/refs.h index e47077354..172fdd284 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -334,6 +334,29 @@ GIT_EXTERN(int) git_reference_set_target( git_reference *ref, const git_oid *id); +/** + * Create a new reference with the same name as the given reference but a + * different OID target and update the reflog with a given message. + * + * The reference must be a direct reference, otherwise this will fail. + * + * The new reference will be written to disk, overwriting the given reference. + * + * @param out Pointer to the newly created reference + * @param ref The reference + * @param id The new target OID for the reference + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message that has to be appended + * to the reflog + * @return 0 or an error code + */ +GIT_EXTERN(int) git_reference_set_target_with_log( + git_reference **out, + git_reference *ref, + const git_oid *id, + const git_signature *signature, + const char *log_message); + /** * Rename an existing reference. * diff --git a/src/refs.c b/src/refs.c index 7c97c1627..75a7e1b95 100644 --- a/src/refs.c +++ b/src/refs.c @@ -480,21 +480,49 @@ int git_reference_symbolic_create_with_log( ref_out, repo, name, NULL, target, force, signature, log_message); } +static int ensure_is_an_updatable_direct_reference(git_reference *ref) +{ + if (ref->type == GIT_REF_OID) + return 0; + + giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference"); + return -1; +} + int git_reference_set_target( git_reference **out, git_reference *ref, const git_oid *id) { + int error; + assert(out && ref && id); - if (ref->type != GIT_REF_OID) { - giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference"); - return -1; - } + if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0) + return error; return git_reference_create(out, ref->db->repo, ref->name, id, 1); } +int git_reference_set_target_with_log( + git_reference **out, + git_reference *ref, + const git_oid *id, + const git_signature *signature, + const char *log_message) +{ + int error; + + assert(out && ref && id); + assert(signature && log_message); + + if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0) + return error; + + return git_reference_create_with_log( + out, ref->db->repo, ref->name, id, 1, signature, log_message); +} + int git_reference_symbolic_set_target( git_reference **out, git_reference *ref, diff --git a/tests/refs/settargetwithlog.c b/tests/refs/settargetwithlog.c new file mode 100644 index 000000000..7bdd6d50b --- /dev/null +++ b/tests/refs/settargetwithlog.c @@ -0,0 +1,55 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" +#include "ref_helpers.h" + +static const char *br2_tip = "a4a7dce85cf63874e984719f4fdd239f5145052f"; +static const char *master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; +static const char *br2_name = "refs/heads/br2"; + +static git_repository *g_repo; + +void test_refs_settargetwithlog__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_refs_settargetwithlog__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_settargetwithlog__updating_a_direct_reference_adds_a_reflog_entry(void) +{ + git_reference *reference, *reference_out; + git_oid current_id, target_id; + git_signature *signature; + git_reflog *reflog; + const git_reflog_entry *entry; + + const char *message = "You've been logged, mate!"; + + git_oid_fromstr(¤t_id, br2_tip); + git_oid_fromstr(&target_id, master_tip); + + cl_git_pass(git_reference_lookup(&reference, g_repo, br2_name)); + + cl_git_pass(git_signature_now(&signature, "foo", "foo@bar")); + + cl_git_pass(git_reference_set_target_with_log( + &reference_out, reference, &target_id, signature, message)); + + cl_git_pass(git_reflog_read(&reflog, reference_out)); + + entry = git_reflog_entry_byindex(reflog, 0); + cl_assert(git_oid_cmp(¤t_id, &entry->oid_old) == 0); + cl_assert(git_oid_cmp(&target_id, &entry->oid_cur) == 0); + cl_assert_equal_s(message, entry->msg); + + git_reflog_free(reflog); + git_reference_free(reference_out); + git_reference_free(reference); + git_signature_free(signature); +} From ca84e058505a25b8f789ee1298a83e818b297ecc Mon Sep 17 00:00:00 2001 From: nulltoken Date: Tue, 14 May 2013 16:40:09 +0200 Subject: [PATCH 05/12] refs: Introduce git_reference_symbolic_set_target_with_log() --- include/git2/refs.h | 25 ++++++++++++++++++++++ src/refs.c | 37 ++++++++++++++++++++++++++++----- tests/refs/settargetwithlog.c | 39 +++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 5 deletions(-) diff --git a/include/git2/refs.h b/include/git2/refs.h index 172fdd284..2dc137692 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -317,6 +317,31 @@ GIT_EXTERN(int) git_reference_symbolic_set_target( git_reference *ref, const char *target); +/** + * Create a new reference with the same name as the given reference but a + * different symbolic target and update the reflog with a given message. + * + * The reference must be a symbolic reference, otherwise this will fail. + * + * The new reference will be written to disk, overwriting the given reference. + * + * The target name will be checked for validity. + * See `git_reference_create_symbolic()` for rules about valid names. + * + * @param out Pointer to the newly created reference + * @param ref The reference + * @param target The new target for the reference + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message that has to be appended + * @return 0 on success, EINVALIDSPEC or an error code + */ +GIT_EXTERN(int) git_reference_symbolic_set_target_with_log( + git_reference **out, + git_reference *ref, + const char *target, + const git_signature *signature, + const char *log_message); + /** * Create a new reference with the same name as the given reference but a * different OID target. The reference must be a direct reference, otherwise diff --git a/src/refs.c b/src/refs.c index 75a7e1b95..eff6b3356 100644 --- a/src/refs.c +++ b/src/refs.c @@ -523,22 +523,49 @@ int git_reference_set_target_with_log( out, ref->db->repo, ref->name, id, 1, signature, log_message); } +static int ensure_is_an_updatable_symbolic_reference(git_reference *ref) +{ + if (ref->type == GIT_REF_SYMBOLIC) + return 0; + + giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference"); + return -1; +} + int git_reference_symbolic_set_target( git_reference **out, git_reference *ref, const char *target) { + int error; + assert(out && ref && target); - if (ref->type != GIT_REF_SYMBOLIC) { - giterr_set(GITERR_REFERENCE, - "Cannot set symbolic target on a direct reference"); - return -1; - } + if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0) + return error; return git_reference_symbolic_create(out, ref->db->repo, ref->name, target, 1); } +int git_reference_symbolic_set_target_with_log( + git_reference **out, + git_reference *ref, + const char *target, + const git_signature *signature, + const char *log_message) +{ + int error; + + assert(out && ref && target); + assert(signature && log_message); + + if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0) + return error; + + return git_reference_symbolic_create_with_log( + out, ref->db->repo, ref->name, target, 1, signature, log_message); +} + int git_reference_rename( git_reference **out, git_reference *ref, diff --git a/tests/refs/settargetwithlog.c b/tests/refs/settargetwithlog.c index 7bdd6d50b..e1f73db56 100644 --- a/tests/refs/settargetwithlog.c +++ b/tests/refs/settargetwithlog.c @@ -53,3 +53,42 @@ void test_refs_settargetwithlog__updating_a_direct_reference_adds_a_reflog_entry git_reference_free(reference); git_signature_free(signature); } + +void test_refs_settargetwithlog__updating_a_symbolic_reference_adds_a_reflog_entry(void) +{ + git_reference *reference, *reference_out; + git_oid id; + git_signature *signature; + git_reflog *reflog; + const git_reflog_entry *entry; + + const char *name = "HEAD"; + const char *message = "You've been logged, mate!"; + + git_oid_fromstr(&id, br2_tip); + + cl_git_pass(git_signature_now(&signature, "foo", "foo@bar")); + + cl_git_pass(git_reference_lookup(&reference, g_repo, name)); + + cl_assert_equal_s( + "refs/heads/master", git_reference_symbolic_target(reference)); + + cl_git_pass(git_reference_symbolic_set_target_with_log(&reference_out, + reference, br2_name, signature, message)); + + cl_assert_equal_s( + br2_name, git_reference_symbolic_target(reference_out)); + + cl_git_pass(git_reflog_read(&reflog, reference)); + + entry = git_reflog_entry_byindex(reflog, 0); + cl_assert(git_oid_streq(&entry->oid_old, master_tip) == 0); + cl_assert(git_oid_streq(&entry->oid_cur, br2_tip) == 0); + cl_assert_equal_s(message, entry->msg); + + git_reflog_free(reflog); + git_reference_free(reference_out); + git_reference_free(reference); + git_signature_free(signature); +} From a6b508080cf73df7139c951feebb2281b9027752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 30 Oct 2013 17:24:36 +0100 Subject: [PATCH 06/12] refs: adjust to the new reflog API --- src/refs.c | 18 +++--------------- tests/refs/createwithlog.c | 4 ++-- tests/refs/settargetwithlog.c | 4 ++-- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/refs.c b/src/refs.c index eff6b3356..d3d072cef 100644 --- a/src/refs.c +++ b/src/refs.c @@ -328,27 +328,15 @@ static int feed_reflog( const char *log_message) { - git_reflog *reflog = NULL; git_oid peeled_ref_oid; int error; - if ((error = git_reflog_read(&reflog, ref)) < 0) - goto cleanup; - if ((error = git_reference_name_to_id(&peeled_ref_oid, git_reference_owner(ref), git_reference_name(ref))) < 0) - goto cleanup; + return error; - if ((error = git_reflog_append(reflog, &peeled_ref_oid, - signature, log_message)) < 0) - goto cleanup; - - error = git_reflog_write(reflog); - -cleanup: - git_reflog_free(reflog); - - return 0; + return git_reflog_append_to(git_reference_owner(ref), git_reference_name(ref), + &peeled_ref_oid, signature, log_message); } static int reference__create( diff --git a/tests/refs/createwithlog.c b/tests/refs/createwithlog.c index 34ab8067f..ff36ffdcd 100644 --- a/tests/refs/createwithlog.c +++ b/tests/refs/createwithlog.c @@ -38,7 +38,7 @@ void test_refs_createwithlog__creating_a_direct_reference_adds_a_reflog_entry(vo cl_git_pass( git_reference_create_with_log(&reference, g_repo, name, &id, 0, signature, message)); - cl_git_pass(git_reflog_read(&reflog, reference)); + cl_git_pass(git_reflog_read(&reflog, g_repo, name)); cl_assert_equal_sz(1, git_reflog_entrycount(reflog)); entry = git_reflog_entry_byindex(reflog, 0); @@ -69,7 +69,7 @@ void test_refs_createwithlog__creating_a_symbolic_reference_adds_a_reflog_entry( cl_git_pass(git_reference_symbolic_create_with_log(&reference, g_repo, name, current_head_target, 0, signature, message)); - cl_git_pass(git_reflog_read(&reflog, reference)); + cl_git_pass(git_reflog_read(&reflog, g_repo, name)); cl_assert_equal_sz(1, git_reflog_entrycount(reflog)); entry = git_reflog_entry_byindex(reflog, 0); diff --git a/tests/refs/settargetwithlog.c b/tests/refs/settargetwithlog.c index e1f73db56..99377dad7 100644 --- a/tests/refs/settargetwithlog.c +++ b/tests/refs/settargetwithlog.c @@ -41,7 +41,7 @@ void test_refs_settargetwithlog__updating_a_direct_reference_adds_a_reflog_entry cl_git_pass(git_reference_set_target_with_log( &reference_out, reference, &target_id, signature, message)); - cl_git_pass(git_reflog_read(&reflog, reference_out)); + cl_git_pass(git_reflog_read(&reflog, g_repo, br2_name)); entry = git_reflog_entry_byindex(reflog, 0); cl_assert(git_oid_cmp(¤t_id, &entry->oid_old) == 0); @@ -80,7 +80,7 @@ void test_refs_settargetwithlog__updating_a_symbolic_reference_adds_a_reflog_ent cl_assert_equal_s( br2_name, git_reference_symbolic_target(reference_out)); - cl_git_pass(git_reflog_read(&reflog, reference)); + cl_git_pass(git_reflog_read(&reflog, g_repo, name)); entry = git_reflog_entry_byindex(reflog, 0); cl_assert(git_oid_streq(&entry->oid_old, master_tip) == 0); From 110df89317b56267b956574216d6611637860446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 13 Nov 2013 13:36:37 +0100 Subject: [PATCH 07/12] refdb: add a `message` parameter for appending to the log This is as yet unused. --- include/git2/sys/refdb_backend.h | 6 +++-- src/refdb.c | 9 ++++--- src/refdb.h | 5 ++-- src/refdb_fs.c | 46 ++++++++++++++++++++++---------- src/refs.c | 4 +-- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 9cf5073fb..d5611d01e 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -93,11 +93,13 @@ struct git_refdb_backend { * must provide this function. */ int (*write)(git_refdb_backend *backend, - const git_reference *ref, int force); + const git_reference *ref, int force, + const char *message); int (*rename)( git_reference **out, git_refdb_backend *backend, - const char *old_name, const char *new_name, int force); + const char *old_name, const char *new_name, int force, + const char *message); /** * Deletes the given reference from the refdb. A refdb implementation diff --git a/src/refdb.c b/src/refdb.c index adb58806e..8f002ebcc 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -167,14 +167,14 @@ void git_refdb_iterator_free(git_reference_iterator *iter) iter->free(iter); } -int git_refdb_write(git_refdb *db, git_reference *ref, int force) +int git_refdb_write(git_refdb *db, git_reference *ref, int force, const char *message) { assert(db && db->backend); GIT_REFCOUNT_INC(db); ref->db = db; - return db->backend->write(db->backend, ref, force); + return db->backend->write(db->backend, ref, force, message); } int git_refdb_rename( @@ -182,12 +182,13 @@ int git_refdb_rename( git_refdb *db, const char *old_name, const char *new_name, - int force) + int force, + const char *message) { int error; assert(db && db->backend); - error = db->backend->rename(out, db->backend, old_name, new_name, force); + error = db->backend->rename(out, db->backend, old_name, new_name, force, message); if (error < 0) return error; diff --git a/src/refdb.h b/src/refdb.h index 0ee60d911..4dea20bd9 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -33,14 +33,15 @@ int git_refdb_rename( git_refdb *db, const char *old_name, const char *new_name, - int force); + int force, + const char *message); int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob); int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter); int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); -int git_refdb_write(git_refdb *refdb, git_reference *ref, int force); +int git_refdb_write(git_refdb *refdb, git_reference *ref, int force, const char *message); int git_refdb_delete(git_refdb *refdb, const char *ref_name); int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 62d5c1047..6eb6ec418 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -910,7 +910,8 @@ fail: static int refdb_fs_backend__write( git_refdb_backend *_backend, const git_reference *ref, - int force) + int force, + const char *message) { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; int error; @@ -974,7 +975,8 @@ static int refdb_fs_backend__rename( git_refdb_backend *_backend, const char *old_name, const char *new_name, - int force) + int force, + const char *message) { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; git_reference *old, *new; @@ -1264,6 +1266,32 @@ static int serialize_reflog_entry( return git_buf_oom(buf); } +static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname) +{ + git_repository *repo; + git_buf log_path = GIT_BUF_INIT; + int error; + + repo = backend->repo; + + if (retrieve_reflog_path(&log_path, repo, refname) < 0) + return -1; + + if (!git_path_isfile(git_buf_cstr(&log_path))) { + giterr_set(GITERR_INVALID, + "Log file for reference '%s' doesn't exist.", refname); + error = -1; + goto cleanup; + } + + error = git_filebuf_open(file, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE); + +cleanup: + git_buf_free(&log_path); + + return error; +} + static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog) { int error = -1; @@ -1271,7 +1299,6 @@ static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflo git_reflog_entry *entry; git_repository *repo; refdb_fs_backend *backend; - git_buf log_path = GIT_BUF_INIT; git_buf log = GIT_BUF_INIT; git_filebuf fbuf = GIT_FILEBUF_INIT; @@ -1280,18 +1307,9 @@ static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflo backend = (refdb_fs_backend *) _backend; repo = backend->repo; - if (retrieve_reflog_path(&log_path, repo, reflog->ref_name) < 0) + if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0) return -1; - if (!git_path_isfile(git_buf_cstr(&log_path))) { - giterr_set(GITERR_INVALID, - "Log file for reference '%s' doesn't exist.", reflog->ref_name); - goto cleanup; - } - - if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE)) < 0) - goto cleanup; - git_vector_foreach(&reflog->entries, i, entry) { if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0) goto cleanup; @@ -1308,7 +1326,7 @@ cleanup: success: git_buf_free(&log); - git_buf_free(&log_path); + return error; } diff --git a/src/refs.c b/src/refs.c index d3d072cef..dbbdda045 100644 --- a/src/refs.c +++ b/src/refs.c @@ -396,7 +396,7 @@ static int reference__create( GITERR_CHECK_ALLOC(ref); - if ((error = git_refdb_write(refdb, ref, force)) < 0) { + if ((error = git_refdb_write(refdb, ref, force, log_message)) < 0) { git_reference_free(ref); return error; } @@ -579,7 +579,7 @@ int git_reference_rename( should_head_be_updated = (error > 0); - if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force)) < 0) + if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force, NULL)) < 0) return error; /* Update HEAD it was poiting to the reference being renamed. */ From a57dd3b7a46c9a2f87f203f1ab372fa28e10e571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 13 Nov 2013 18:15:20 +0100 Subject: [PATCH 08/12] reflog: integrate into the ref writing Whenever a reference is created or updated, we need to write to the reflog regardless of whether the user gave us a message, so we shouldn't leave that to the ref frontend, but integrate it into the backend. This also eliminates the race between ref update and writing to the reflog, as we protect the reflog with the ref lock. As an additional benefit, this reflog append on the backend happens by appending to the file instead of parsing and rewriting it. --- include/git2/sys/refdb_backend.h | 4 +- src/refdb.c | 7 +- src/refdb.h | 3 +- src/refdb_fs.c | 104 ++++++++++++++++++++++-- src/refs.c | 132 +++++++++++++------------------ src/stash.c | 21 ++--- tests/refs/createwithlog.c | 32 -------- tests/refs/reflog/reflog.c | 9 ++- tests/refs/settargetwithlog.c | 39 --------- 9 files changed, 173 insertions(+), 178 deletions(-) diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index d5611d01e..131e1b5d0 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -94,12 +94,12 @@ struct git_refdb_backend { */ int (*write)(git_refdb_backend *backend, const git_reference *ref, int force, - const char *message); + const git_signature *who, const char *message); int (*rename)( git_reference **out, git_refdb_backend *backend, const char *old_name, const char *new_name, int force, - const char *message); + const git_signature *who, const char *message); /** * Deletes the given reference from the refdb. A refdb implementation diff --git a/src/refdb.c b/src/refdb.c index 8f002ebcc..bc8c2b366 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -167,14 +167,14 @@ void git_refdb_iterator_free(git_reference_iterator *iter) iter->free(iter); } -int git_refdb_write(git_refdb *db, git_reference *ref, int force, const char *message) +int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message) { assert(db && db->backend); GIT_REFCOUNT_INC(db); ref->db = db; - return db->backend->write(db->backend, ref, force, message); + return db->backend->write(db->backend, ref, force, who, message); } int git_refdb_rename( @@ -183,12 +183,13 @@ int git_refdb_rename( const char *old_name, const char *new_name, int force, + const git_signature *who, const char *message) { int error; assert(db && db->backend); - error = db->backend->rename(out, db->backend, old_name, new_name, force, message); + error = db->backend->rename(out, db->backend, old_name, new_name, force, who, message); if (error < 0) return error; diff --git a/src/refdb.h b/src/refdb.h index 4dea20bd9..215ae17c5 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -34,6 +34,7 @@ int git_refdb_rename( const char *old_name, const char *new_name, int force, + const git_signature *who, const char *message); int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob); @@ -41,7 +42,7 @@ int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter); int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); -int git_refdb_write(git_refdb *refdb, git_reference *ref, int force, const char *message); +int git_refdb_write(git_refdb *refdb, git_reference *ref, int force, const git_signature *who, const char *message); int git_refdb_delete(git_refdb *refdb, const char *ref_name); int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 6eb6ec418..2f7b43401 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -688,9 +688,8 @@ static int reference_path_available( return 0; } -static int loose_write(refdb_fs_backend *backend, const git_reference *ref) +static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const git_reference *ref) { - git_filebuf file = GIT_FILEBUF_INIT; git_buf ref_path = GIT_BUF_INIT; /* Remove a possibly existing empty directory hierarchy @@ -702,25 +701,29 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref) if (git_buf_joinpath(&ref_path, backend->path, ref->name) < 0) return -1; - if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) { + if (git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) { git_buf_free(&ref_path); return -1; } git_buf_free(&ref_path); + return 0; +} +static int loose_commit(git_filebuf *file, const git_reference *ref) +{ if (ref->type == GIT_REF_OID) { char oid[GIT_OID_HEXSZ + 1]; git_oid_nfmt(oid, sizeof(oid), &ref->target.oid); - git_filebuf_printf(&file, "%s\n", oid); + git_filebuf_printf(file, "%s\n", oid); } else if (ref->type == GIT_REF_SYMBOLIC) { - git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic); + git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic); } else { assert(0); /* don't let this happen */ } - return git_filebuf_commit(&file); + return git_filebuf_commit(file); } /* @@ -907,13 +910,17 @@ fail: return -1; } +static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_signature *author, const char *message); + static int refdb_fs_backend__write( git_refdb_backend *_backend, const git_reference *ref, int force, + const git_signature *who, const char *message) { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; + git_filebuf file = GIT_FILEBUF_INIT; int error; assert(backend); @@ -922,7 +929,16 @@ static int refdb_fs_backend__write( if (error < 0) return error; - return loose_write(backend, ref); + /* We need to perform the reflog append under the ref's lock */ + if ((error = loose_lock(&file, backend, ref)) < 0) + return error; + + if ((error = reflog_append(backend, ref, who, message)) < 0) { + git_filebuf_cleanup(&file); + return error; + } + + return loose_commit(&file, ref); } static int refdb_fs_backend__delete( @@ -970,16 +986,20 @@ static int refdb_fs_backend__delete( return packed_write(backend); } +static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name); + static int refdb_fs_backend__rename( git_reference **out, git_refdb_backend *_backend, const char *old_name, const char *new_name, int force, + const git_signature *who, const char *message) { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; git_reference *old, *new; + git_filebuf file = GIT_FILEBUF_INIT; int error; assert(backend); @@ -1000,7 +1020,28 @@ static int refdb_fs_backend__rename( return -1; } - if ((error = loose_write(backend, new)) < 0 || out == NULL) { + if ((error = loose_lock(&file, backend, new)) < 0) { + git_reference_free(new); + return error; + } + + /* Try to rename the refog; it's ok if the old doesn't exist */ + error = refdb_reflog_fs__rename(_backend, old_name, new_name); + if (((error == 0) || (error == GIT_ENOTFOUND)) && + ((error = reflog_append(backend, new, who, message)) < 0)) { + git_reference_free(new); + git_filebuf_cleanup(&file); + return error; + } + + if (error < 0) { + git_reference_free(new); + git_filebuf_cleanup(&file); + return error; + } + + + if ((error = loose_commit(&file, new)) < 0 || out == NULL) { git_reference_free(new); return error; } @@ -1330,6 +1371,48 @@ success: return error; } +/* Append to the reflog, must be called under reference lock */ +static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message) +{ + int error; + git_oid old_id, new_id; + git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; + git_repository *repo = backend->repo; + + /* Creation of symbolic references doesn't get a reflog entry */ + if (ref->type == GIT_REF_SYMBOLIC) + return 0; + + error = git_reference_name_to_id(&old_id, repo, ref->name); + if (error == GIT_ENOTFOUND) { + memset(&old_id, 0, sizeof(git_oid)); + error = 0; + } + if (error < 0) + return error; + + git_oid_cpy(&new_id, git_reference_target(ref)); + + if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0) + goto cleanup; + + if ((error = retrieve_reflog_path(&path, repo, ref->name)) < 0) + goto cleanup; + + if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) && + (error != GIT_EEXISTS)) { + goto cleanup; + } + + error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE); + +cleanup: + git_buf_free(&buf); + git_buf_free(&path); + + return error; +} + static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name) { int error = 0, fd; @@ -1358,6 +1441,11 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_ if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0) return -1; + if (!git_path_exists(git_buf_cstr(&old_path))) { + error = GIT_ENOTFOUND; + goto cleanup; + } + /* * Move the reflog to a temporary place. This two-phase renaming is required * in order to cope with funny renaming use cases when one tries to move a reference diff --git a/src/refs.c b/src/refs.c index dbbdda045..598d6873c 100644 --- a/src/refs.c +++ b/src/refs.c @@ -21,6 +21,7 @@ #include #include #include +#include GIT__USE_STRMAP; @@ -322,23 +323,6 @@ const char *git_reference_symbolic_target(const git_reference *ref) return ref->target.symbolic; } -static int feed_reflog( - const git_reference *ref, - const git_signature *signature, - const char *log_message) -{ - - git_oid peeled_ref_oid; - int error; - - if ((error = git_reference_name_to_id(&peeled_ref_oid, - git_reference_owner(ref), git_reference_name(ref))) < 0) - return error; - - return git_reflog_append_to(git_reference_owner(ref), git_reference_name(ref), - &peeled_ref_oid, signature, log_message); -} - static int reference__create( git_reference **ref_out, git_repository *repo, @@ -355,7 +339,7 @@ static int reference__create( int error = 0; assert(repo && name); - assert(!((signature == NULL) ^ (log_message == NULL))); + assert(symbolic || signature); if (ref_out) *ref_out = NULL; @@ -396,12 +380,7 @@ static int reference__create( GITERR_CHECK_ALLOC(ref); - if ((error = git_refdb_write(refdb, ref, force, log_message)) < 0) { - git_reference_free(ref); - return error; - } - - if (log_message && (error = feed_reflog(ref, signature, log_message)) < 0) { + if ((error = git_refdb_write(refdb, ref, force, signature, log_message)) < 0) { git_reference_free(ref); return error; } @@ -421,9 +400,22 @@ int git_reference_create( const git_oid *oid, int force) { + git_signature *who; + int error; + assert(oid); - return reference__create(ref_out, repo, name, oid, NULL, force, NULL, NULL); + /* Should we return an error if there is no default? */ + if (((error = git_signature_default(&who, repo)) < 0) && + ((error = git_signature_now(&who, "unknown", "unknown")) < 0)) { + return error; + } + + error = reference__create(ref_out, repo, name, oid, NULL, force, who, NULL); + + git_signature_free(who); + + return error; } int git_reference_create_with_log( @@ -449,25 +441,9 @@ int git_reference_symbolic_create( int force) { assert(target); - return reference__create(ref_out, repo, name, NULL, target, force, NULL, NULL); } -int git_reference_symbolic_create_with_log( - git_reference **ref_out, - git_repository *repo, - const char *name, - const char *target, - int force, - const git_signature *signature, - const char* log_message) -{ - assert(target && signature && log_message); - - return reference__create( - ref_out, repo, name, NULL, target, force, signature, log_message); -} - static int ensure_is_an_updatable_direct_reference(git_reference *ref) { if (ref->type == GIT_REF_OID) @@ -535,36 +511,15 @@ int git_reference_symbolic_set_target( return git_reference_symbolic_create(out, ref->db->repo, ref->name, target, 1); } -int git_reference_symbolic_set_target_with_log( - git_reference **out, - git_reference *ref, - const char *target, - const git_signature *signature, - const char *log_message) -{ - int error; - - assert(out && ref && target); - assert(signature && log_message); - - if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0) - return error; - - return git_reference_symbolic_create_with_log( - out, ref->db->repo, ref->name, target, 1, signature, log_message); -} - -int git_reference_rename( - git_reference **out, - git_reference *ref, - const char *new_name, - int force) +static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force, + const git_signature *signature, const char *message) { unsigned int normalization_flags; char normalized[GIT_REFNAME_MAX]; bool should_head_be_updated = false; int error = 0; - int reference_has_log; + + assert(ref && new_name && signature); normalization_flags = ref->type == GIT_REF_SYMBOLIC ? GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL; @@ -573,13 +528,14 @@ int git_reference_rename( normalized, sizeof(normalized), new_name, normalization_flags)) < 0) return error; + /* Check if we have to update HEAD. */ if ((error = git_branch_is_head(ref)) < 0) return error; should_head_be_updated = (error > 0); - if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force, NULL)) < 0) + if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force, signature, message)) < 0) return error; /* Update HEAD it was poiting to the reference being renamed. */ @@ -589,17 +545,43 @@ int git_reference_rename( return error; } - /* Rename the reflog file, if it exists. */ - reference_has_log = git_reference_has_log(ref); - if (reference_has_log < 0) - return reference_has_log; - - if (reference_has_log && (error = git_reflog_rename(git_reference_owner(ref), git_reference_name(ref), new_name)) < 0) - return error; - return 0; } + +int git_reference_rename( + git_reference **out, + git_reference *ref, + const char *new_name, + int force) +{ + git_signature *who; + int error; + + /* Should we return an error if there is no default? */ + if (((error = git_signature_default(&who, ref->db->repo)) < 0) && + ((error = git_signature_now(&who, "unknown", "unknown")) < 0)) { + return error; + } + + error = reference__rename(out, ref, new_name, force, who, NULL); + + git_signature_free(who); + + return error; +} + +int git_reference_rename_with_log( + git_reference **out, + git_reference *ref, + const char *new_name, + int force, + const git_signature *who, + const char * message) +{ + return reference__rename(out, ref, new_name, force, who, message); +} + int git_reference_resolve(git_reference **ref_out, const git_reference *ref) { switch (git_reference_type(ref)) { diff --git a/src/stash.c b/src/stash.c index 083c2a4cd..66b1cd7c5 100644 --- a/src/stash.c +++ b/src/stash.c @@ -412,25 +412,12 @@ static int update_reflog( const char *message) { git_reference *stash; - git_reflog *reflog = NULL; int error; - if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0) - goto cleanup; + error = git_reference_create_with_log(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1, stasher, message); git_reference_free(stash); - if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE) < 0)) - goto cleanup; - - if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0) - goto cleanup; - - if ((error = git_reflog_write(reflog)) < 0) - goto cleanup; - -cleanup: - git_reflog_free(reflog); return error; } @@ -636,7 +623,11 @@ int git_stash_drop( entry = git_reflog_entry_byindex(reflog, 0); git_reference_free(stash); - error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1); + if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1) < 0)) + goto cleanup; + + /* We need to undo the writing that we just did */ + error = git_reflog_write(reflog); } cleanup: diff --git a/tests/refs/createwithlog.c b/tests/refs/createwithlog.c index ff36ffdcd..0fe81df91 100644 --- a/tests/refs/createwithlog.c +++ b/tests/refs/createwithlog.c @@ -6,7 +6,6 @@ #include "ref_helpers.h" static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; -static const char *current_head_target = "refs/heads/master"; static git_repository *g_repo; @@ -50,34 +49,3 @@ void test_refs_createwithlog__creating_a_direct_reference_adds_a_reflog_entry(vo git_reference_free(reference); git_signature_free(signature); } - -void test_refs_createwithlog__creating_a_symbolic_reference_adds_a_reflog_entry(void) -{ - git_reference *reference; - git_oid id; - git_signature *signature; - git_reflog *reflog; - const git_reflog_entry *entry; - - const char *name = "ANOTHER_HEAD_TRACKER"; - const char *message = "You've been logged, mate!"; - - git_oid_fromstr(&id, current_master_tip); - - cl_git_pass(git_signature_now(&signature, "foo", "foo@bar")); - - cl_git_pass(git_reference_symbolic_create_with_log(&reference, g_repo, - name, current_head_target, 0, signature, message)); - - cl_git_pass(git_reflog_read(&reflog, g_repo, name)); - cl_assert_equal_sz(1, git_reflog_entrycount(reflog)); - - entry = git_reflog_entry_byindex(reflog, 0); - cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); - cl_assert(git_oid_cmp(&id, &entry->oid_cur) == 0); - cl_assert_equal_s(message, entry->msg); - - git_reflog_free(reflog); - git_reference_free(reference); - git_signature_free(signature); -} diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index bcd224270..60085791c 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -49,17 +49,20 @@ static void assert_appends(const git_signature *committer, const git_oid *oid) /* Read and parse the reflog for this branch */ cl_git_pass(git_reflog_read(&reflog, repo2, new_ref)); - cl_assert_equal_i(2, (int)git_reflog_entrycount(reflog)); + cl_assert_equal_i(3, (int)git_reflog_entrycount(reflog)); + + /* The first one was the creation of the branch */ + entry = git_reflog_entry_byindex(reflog, 2); + cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); entry = git_reflog_entry_byindex(reflog, 1); assert_signature(committer, entry->committer); - cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); + cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0); cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0); cl_assert(entry->msg == NULL); entry = git_reflog_entry_byindex(reflog, 0); assert_signature(committer, entry->committer); - cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0); cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0); cl_assert_equal_s(commit_msg, entry->msg); diff --git a/tests/refs/settargetwithlog.c b/tests/refs/settargetwithlog.c index 99377dad7..cfa1c99d5 100644 --- a/tests/refs/settargetwithlog.c +++ b/tests/refs/settargetwithlog.c @@ -53,42 +53,3 @@ void test_refs_settargetwithlog__updating_a_direct_reference_adds_a_reflog_entry git_reference_free(reference); git_signature_free(signature); } - -void test_refs_settargetwithlog__updating_a_symbolic_reference_adds_a_reflog_entry(void) -{ - git_reference *reference, *reference_out; - git_oid id; - git_signature *signature; - git_reflog *reflog; - const git_reflog_entry *entry; - - const char *name = "HEAD"; - const char *message = "You've been logged, mate!"; - - git_oid_fromstr(&id, br2_tip); - - cl_git_pass(git_signature_now(&signature, "foo", "foo@bar")); - - cl_git_pass(git_reference_lookup(&reference, g_repo, name)); - - cl_assert_equal_s( - "refs/heads/master", git_reference_symbolic_target(reference)); - - cl_git_pass(git_reference_symbolic_set_target_with_log(&reference_out, - reference, br2_name, signature, message)); - - cl_assert_equal_s( - br2_name, git_reference_symbolic_target(reference_out)); - - cl_git_pass(git_reflog_read(&reflog, g_repo, name)); - - entry = git_reflog_entry_byindex(reflog, 0); - cl_assert(git_oid_streq(&entry->oid_old, master_tip) == 0); - cl_assert(git_oid_streq(&entry->oid_cur, br2_tip) == 0); - cl_assert_equal_s(message, entry->msg); - - git_reflog_free(reflog); - git_reference_free(reference_out); - git_reference_free(reference); - git_signature_free(signature); -} From 13c9e44af9424e1fc478693f0d64fbf7082c9665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 14 Nov 2013 19:41:09 +0100 Subject: [PATCH 09/12] reflog: remove git_reflog_append_to() This was a convenience method for the refs front-end to do the reflog writing. This is now done in the backend and it has no more reason for being. --- include/git2/reflog.h | 19 +------------------ src/reflog.c | 19 ------------------- tests/refs/reflog/reflog.c | 23 ----------------------- 3 files changed, 1 insertion(+), 60 deletions(-) diff --git a/include/git2/reflog.h b/include/git2/reflog.h index 2d1b6eeaa..df06e1b8e 100644 --- a/include/git2/reflog.h +++ b/include/git2/reflog.h @@ -47,7 +47,7 @@ GIT_EXTERN(int) git_reflog_read(git_reflog **out, git_repository *repo, const c GIT_EXTERN(int) git_reflog_write(git_reflog *reflog); /** - * Add a new entry to the reflog. + * Add a new entry to the in-memory reflog. * * `msg` is optional and can be NULL. * @@ -59,23 +59,6 @@ GIT_EXTERN(int) git_reflog_write(git_reflog *reflog); */ GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg); -/** - * Add a new entry to the named reflog. - * - * This utility function loads the named reflog, appends to it and - * writes it back out to the backend. - * - * `msg` is optional and can be NULL. - * - * @param repo the repository to act on - * @param name the reflog's name - * @param id the OID the reference is now pointing to - * @param committer the signature of the committer - * @param msg the reflog message - * @return 0 or an error code - */ -GIT_EXTERN(int) git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id, const git_signature *committer, const char *msg); - /** * Rename a reflog * diff --git a/src/reflog.c b/src/reflog.c index cebb87d86..9b2b201bf 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -230,22 +230,3 @@ int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry) return 0; } - -int git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id, - const git_signature *committer, const char *msg) -{ - int error; - git_reflog *reflog; - - if ((error = git_reflog_read(&reflog, repo, name)) < 0) - return error; - - if ((error = git_reflog_append(reflog, id, committer, msg)) < 0) - goto cleanup; - - error = git_reflog_write(reflog); - -cleanup: - git_reflog_free(reflog); - return error; -} diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index 60085791c..9ac15d556 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -100,29 +100,6 @@ void test_refs_reflog_reflog__append_then_read(void) git_signature_free(committer); } -void test_refs_reflog_reflog__append_to_then_read(void) -{ - /* write a reflog for a given reference and ensure it can be read back */ - git_reference *ref; - git_oid oid; - git_signature *committer; - - /* Create a new branch pointing at the HEAD */ - git_oid_fromstr(&oid, current_master_tip); - cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0)); - git_reference_free(ref); - - cl_git_pass(git_signature_now(&committer, "foo", "foo@bar")); - - cl_git_fail(git_reflog_append_to(g_repo, new_ref, &oid, committer, "no inner\nnewline")); - cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, NULL)); - cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, commit_msg "\n")); - - assert_appends(committer, &oid); - - git_signature_free(committer); -} - void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void) { git_reference *master, *new_master; From 6f13a30565fc3891c0df161945097b316945fca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 17 Nov 2013 23:26:49 +0100 Subject: [PATCH 10/12] reflog: write to the reflog following git's rules git-core only writes to the reflogs of HEAD, refs/heads/ and, refs/notes/ or if there is already a reflog in place. Adjust our code to follow these semantics. --- src/refdb_fs.c | 36 +++++++++++++++++++++++++++++++++--- src/refs.h | 1 + tests/refs/reflog/reflog.c | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 2f7b43401..9f23de248 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -911,6 +911,22 @@ fail: } static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_signature *author, const char *message); +static int has_reflog(git_repository *repo, const char *name); + +/* We only write if it's under heads/, remotes/ or notes/ or if it already has a log */ +static bool should_write_reflog(git_repository *repo, const char *name) +{ + if (has_reflog(repo, name)) + return 1; + + if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR) || + !git__strcmp(name, GIT_HEAD_FILE) || + !git__prefixcmp(name, GIT_REFS_REMOTES_DIR) || + !git__prefixcmp(name, GIT_REFS_NOTES_DIR)) + return 1; + + return 0; +} static int refdb_fs_backend__write( git_refdb_backend *_backend, @@ -933,7 +949,8 @@ static int refdb_fs_backend__write( if ((error = loose_lock(&file, backend, ref)) < 0) return error; - if ((error = reflog_append(backend, ref, who, message)) < 0) { + if (should_write_reflog(backend->repo, ref->name) && + (error = reflog_append(backend, ref, who, message)) < 0) { git_filebuf_cleanup(&file); return error; } @@ -1228,6 +1245,21 @@ GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name); } +static int has_reflog(git_repository *repo, const char *name) +{ + int ret = 0; + git_buf path = GIT_BUF_INIT; + + if (retrieve_reflog_path(&path, repo, name) < 0) + goto cleanup; + + ret = git_path_isfile(git_buf_cstr(&path)); + +cleanup: + git_buf_free(&path); + return ret; +} + static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name) { int error = -1; @@ -1338,7 +1370,6 @@ static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflo int error = -1; unsigned int i; git_reflog_entry *entry; - git_repository *repo; refdb_fs_backend *backend; git_buf log = GIT_BUF_INIT; git_filebuf fbuf = GIT_FILEBUF_INIT; @@ -1346,7 +1377,6 @@ static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflo assert(_backend && reflog); backend = (refdb_fs_backend *) _backend; - repo = backend->repo; if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0) return -1; diff --git a/src/refs.h b/src/refs.h index 80c7703fc..4d5b6dacb 100644 --- a/src/refs.h +++ b/src/refs.h @@ -19,6 +19,7 @@ #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/" #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/" #define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/" +#define GIT_REFS_NOTES_DIR GIT_REFS_DIR "notes/" #define GIT_REFS_DIR_MODE 0777 #define GIT_REFS_FILE_MODE 0666 diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index 9ac15d556..6c9375dc7 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -187,3 +187,39 @@ void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC cl_assert_equal_i(GIT_EINVALIDSPEC, git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id")); } + +void test_refs_reflog_reflog__write_only_std_locations(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, current_master_tip); + + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/foo", &id, 1)); + git_reference_free(ref); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1)); + git_reference_free(ref); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/notes/foo", &id, 1)); + git_reference_free(ref); + + assert_has_reflog(true, "refs/heads/foo"); + assert_has_reflog(false, "refs/tags/foo"); + assert_has_reflog(true, "refs/notes/foo"); + +} + +void test_refs_reflog_reflog__write_when_explicitly_active(void) +{ + git_reference *ref; + git_oid id; + git_buf path = GIT_BUF_INIT; + + git_oid_fromstr(&id, current_master_tip); + cl_git_pass(git_buf_join(&path, '/', git_repository_path(g_repo), "logs/refs/tags/foo")); + cl_git_pass(git_futils_mkpath2file(git_buf_cstr(&path), 0777)); + cl_git_mkfile(git_buf_cstr(&path), ""); + + cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1)); + git_reference_free(ref); + assert_has_reflog(true, "refs/tags/foo"); +} From 8d5ec9106afbca346a8def84ef20de0a7ba2240a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 23 Nov 2013 14:13:01 +0100 Subject: [PATCH 11/12] refs: expose a way to ensure a ref has a log Sometimes (e.g. stash) we want to make sure that a log will be written, even if it's not in one of the standard locations. Let's make that easier. --- include/git2/refs.h | 12 ++++++++++++ include/git2/sys/refdb_backend.h | 6 ++++++ src/refdb.c | 7 +++++++ src/refdb.h | 3 +++ src/refdb_fs.c | 21 ++++++++++++++++++++- src/refs.c | 13 +++++++++++++ src/stash.c | 3 +++ tests/refs/reflog/reflog.c | 5 +---- 8 files changed, 65 insertions(+), 5 deletions(-) diff --git a/include/git2/refs.h b/include/git2/refs.h index 2dc137692..31bf997fe 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -555,6 +555,18 @@ GIT_EXTERN(int) git_reference_foreach_glob( */ GIT_EXTERN(int) git_reference_has_log(git_reference *ref); +/** + * Ensure there is a reflog for a particular reference. + * + * Make sure that successive updates to the reference will append to + * its log. + * + * @param repo the repository + * @param refname the reference's name + * @return 0 or an error code. + */ +GIT_EXTERN(int) git_reference_ensure_log(git_repository *repo, const char *refname); + /** * Check if a reference is a local branch. * diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 131e1b5d0..1485ed73a 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -116,6 +116,12 @@ struct git_refdb_backend { */ int (*compress)(git_refdb_backend *backend); + /** + * Make sure a particular reference will have a reflog which + * will be appended to on writes. + */ + int (*ensure_log)(git_refdb_backend *backend, const char *refname); + /** * Frees any resources held by the refdb. A refdb implementation may * provide this function; if it is not provided, nothing will be done. diff --git a/src/refdb.c b/src/refdb.c index bc8c2b366..4f3169f88 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -221,3 +221,10 @@ int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name) return 0; } + +int git_refdb_ensure_log(git_refdb *db, const char *refname) +{ + assert(db && refname); + + return db->backend->ensure_log(db->backend, refname); +} diff --git a/src/refdb.h b/src/refdb.h index 215ae17c5..12c15cb9f 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -48,5 +48,8 @@ int git_refdb_delete(git_refdb *refdb, const char *ref_name); int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name); int git_refdb_reflog_write(git_reflog *reflog); +int git_refdb_ensure_log(git_refdb *refdb, const char *refname); + + #endif diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 9f23de248..2cdea8274 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -1233,7 +1233,7 @@ static int create_new_reflog_file(const char *filepath) return error; if ((fd = p_open(filepath, - O_WRONLY | O_CREAT | O_TRUNC, + O_WRONLY | O_CREAT, GIT_REFLOG_FILE_MODE)) < 0) return -1; @@ -1245,6 +1245,24 @@ GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name); } +static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name) +{ + refdb_fs_backend *backend; + git_repository *repo; + git_buf path = GIT_BUF_INIT; + int error; + + assert(_backend && name); + + backend = (refdb_fs_backend *) _backend; + repo = backend->repo; + + if ((error = retrieve_reflog_path(&path, repo, name)) < 0) + return error; + + return create_new_reflog_file(git_buf_cstr(&path)); +} + static int has_reflog(git_repository *repo, const char *name) { int ret = 0; @@ -1590,6 +1608,7 @@ int git_refdb_backend_fs( backend->parent.del = &refdb_fs_backend__delete; backend->parent.rename = &refdb_fs_backend__rename; backend->parent.compress = &refdb_fs_backend__compress; + backend->parent.ensure_log = &refdb_reflog_fs__ensure_log; backend->parent.free = &refdb_fs_backend__free; backend->parent.reflog_read = &refdb_reflog_fs__read; backend->parent.reflog_write = &refdb_reflog_fs__write; diff --git a/src/refs.c b/src/refs.c index 598d6873c..902a17cfc 100644 --- a/src/refs.c +++ b/src/refs.c @@ -1068,6 +1068,19 @@ int git_reference_has_log( return result; } +int git_reference_ensure_log(git_repository *repo, const char *refname) +{ + int error; + git_refdb *refdb; + + assert(repo && refname); + + if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) + return error; + + return git_refdb_ensure_log(refdb, refname); +} + int git_reference__is_branch(const char *ref_name) { return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0; diff --git a/src/stash.c b/src/stash.c index 66b1cd7c5..7d7bf78c3 100644 --- a/src/stash.c +++ b/src/stash.c @@ -414,6 +414,9 @@ static int update_reflog( git_reference *stash; int error; + if ((error = git_reference_ensure_log(repo, GIT_REFS_STASH_FILE)) < 0) + return error; + error = git_reference_create_with_log(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1, stasher, message); git_reference_free(stash); diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index 6c9375dc7..9f414f21d 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -212,12 +212,9 @@ void test_refs_reflog_reflog__write_when_explicitly_active(void) { git_reference *ref; git_oid id; - git_buf path = GIT_BUF_INIT; git_oid_fromstr(&id, current_master_tip); - cl_git_pass(git_buf_join(&path, '/', git_repository_path(g_repo), "logs/refs/tags/foo")); - cl_git_pass(git_futils_mkpath2file(git_buf_cstr(&path), 0777)); - cl_git_mkfile(git_buf_cstr(&path), ""); + git_reference_ensure_log(g_repo, "refs/tags/foo"); cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1)); git_reference_free(ref); From f21051297cc698644ea0dc9c7122ec944dba2863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 23 Nov 2013 14:39:53 +0100 Subject: [PATCH 12/12] refs: expose has_log() on the backend The frontend used to look at the file directly, but that's obviously not the right thing to do. Expose it on the backend and use that function instead. --- include/git2/refs.h | 6 +++--- include/git2/sys/refdb_backend.h | 5 +++++ src/refdb.c | 7 +++++++ src/refdb.h | 1 + src/refdb_fs.c | 12 ++++++++++++ src/refs.c | 19 +++++++------------ tests/refs/reflog/reflog.c | 8 +------- 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/include/git2/refs.h b/include/git2/refs.h index 31bf997fe..f88f448f0 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -548,12 +548,12 @@ GIT_EXTERN(int) git_reference_foreach_glob( /** * Check if a reflog exists for the specified reference. * - * @param ref A git reference - * + * @param repo the repository + * @param refname the reference's name * @return 0 when no reflog can be found, 1 when it exists; * otherwise an error code. */ -GIT_EXTERN(int) git_reference_has_log(git_reference *ref); +GIT_EXTERN(int) git_reference_has_log(git_repository *repo, const char *refname); /** * Ensure there is a reflog for a particular reference. diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 1485ed73a..5bbd4ba4c 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -116,6 +116,11 @@ struct git_refdb_backend { */ int (*compress)(git_refdb_backend *backend); + /** + * Query whether a particular reference has a log (may be empty) + */ + int (*has_log)(git_refdb_backend *backend, const char *refname); + /** * Make sure a particular reference will have a reflog which * will be appended to on writes. diff --git a/src/refdb.c b/src/refdb.c index 4f3169f88..411423d57 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -222,6 +222,13 @@ int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name) return 0; } +int git_refdb_has_log(git_refdb *db, const char *refname) +{ + assert(db && refname); + + return db->backend->has_log(db->backend, refname); +} + int git_refdb_ensure_log(git_refdb *db, const char *refname) { assert(db && refname); diff --git a/src/refdb.h b/src/refdb.h index 12c15cb9f..91eecb782 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -48,6 +48,7 @@ int git_refdb_delete(git_refdb *refdb, const char *ref_name); int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name); int git_refdb_reflog_write(git_reflog *reflog); +int git_refdb_has_log(git_refdb *db, const char *refname); int git_refdb_ensure_log(git_refdb *refdb, const char *refname); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 2cdea8274..e9ce648e1 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -1278,6 +1278,17 @@ cleanup: return ret; } +static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name) +{ + refdb_fs_backend *backend; + + assert(_backend && name); + + backend = (refdb_fs_backend *) _backend; + + return has_reflog(backend->repo, name); +} + static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name) { int error = -1; @@ -1608,6 +1619,7 @@ int git_refdb_backend_fs( backend->parent.del = &refdb_fs_backend__delete; backend->parent.rename = &refdb_fs_backend__rename; backend->parent.compress = &refdb_fs_backend__compress; + backend->parent.has_log = &refdb_reflog_fs__has_log; backend->parent.ensure_log = &refdb_reflog_fs__ensure_log; backend->parent.free = &refdb_fs_backend__free; backend->parent.reflog_read = &refdb_reflog_fs__read; diff --git a/src/refs.c b/src/refs.c index 902a17cfc..519770d2d 100644 --- a/src/refs.c +++ b/src/refs.c @@ -1050,22 +1050,17 @@ int git_reference__update_terminal( return reference__update_terminal(repo, ref_name, oid, 0); } -int git_reference_has_log( - git_reference *ref) +int git_reference_has_log(git_repository *repo, const char *refname) { - git_buf path = GIT_BUF_INIT; - int result; + int error; + git_refdb *refdb; - assert(ref); + assert(repo && refname); - if (git_buf_join_n(&path, '/', 3, ref->db->repo->path_repository, - GIT_REFLOG_DIR, ref->name) < 0) - return -1; + if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) + return error; - result = git_path_isfile(git_buf_cstr(&path)); - git_buf_free(&path); - - return result; + return git_refdb_has_log(refdb, refname); } int git_reference_ensure_log(git_repository *repo, const char *refname) diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c index 9f414f21d..a1c5adaf4 100644 --- a/tests/refs/reflog/reflog.c +++ b/tests/refs/reflog/reflog.c @@ -127,13 +127,7 @@ void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void) static void assert_has_reflog(bool expected_result, const char *name) { - git_reference *ref; - - cl_git_pass(git_reference_lookup(&ref, g_repo, name)); - - cl_assert_equal_i(expected_result, git_reference_has_log(ref)); - - git_reference_free(ref); + cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name)); } void test_refs_reflog_reflog__reference_has_reflog(void)