refs: Introduce git_reference_set_target_with_log()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
diff --git a/include/git2/refs.h b/include/git2/refs.h
index e470773..172fdd2 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -335,6 +335,29 @@ GIT_EXTERN(int) git_reference_set_target(
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.
*
* This method works for both direct and symbolic references.
diff --git a/src/refs.c b/src/refs.c
index 7c97c16..75a7e1b 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 0000000..7bdd6d5
--- /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);
+}