refdb: extract function to check whether to append HEAD to the reflog The logic to determine whether a reflog entry should be for the HEAD reference is non-trivial. Currently, the only user of this is the filesystem-based refdb, but with the advent of the reftable refdb we're going to add a second user that's interested in having the same behaviour. Let's pull out a new function that checks whether a given reference should cause a entry to be written to the HEAD reflog as a preparatory step.
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 154 155 156 157 158 159
diff --git a/src/refdb.c b/src/refdb.c
index 19b9887..764d2c1 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -268,6 +268,55 @@ int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *
return 0;
}
+int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref)
+{
+ git_reference *head = NULL, *peeled = NULL;
+ const char *name;
+ int error;
+
+ *out = 0;
+
+ if (ref->type == GIT_REFERENCE_SYMBOLIC) {
+ error = 0;
+ goto out;
+ }
+
+ if ((error = git_refdb_lookup(&head, db, GIT_HEAD_FILE)) < 0)
+ goto out;
+
+ if (git_reference_type(head) == GIT_REFERENCE_DIRECT)
+ goto out;
+
+ /* Go down the symref chain until we find the branch */
+ while (git_reference_type(head) == GIT_REFERENCE_SYMBOLIC) {
+ if ((error = git_refdb_lookup(&peeled, db, git_reference_symbolic_target(head))) < 0)
+ break;
+
+ git_reference_free(head);
+ head = peeled;
+ peeled = NULL;
+ }
+
+ if (error < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto out;
+ error = 0;
+ name = git_reference_symbolic_target(head);
+ } else {
+ name = git_reference_name(head);
+ }
+
+ if (strcmp(name, ref->name))
+ goto out;
+
+ *out = 1;
+
+out:
+ git_reference_free(peeled);
+ git_reference_free(head);
+ return error;
+}
+
int git_refdb_has_log(git_refdb *db, const char *refname)
{
assert(db && refname);
diff --git a/src/refdb.h b/src/refdb.h
index 7578f66..05c0e1c 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -78,6 +78,22 @@ int git_refdb_reflog_write(git_reflog *reflog);
*/
int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *ref);
+/**
+ * Determine whether a reflog entry should be created for HEAD if creating one
+ * for the given reference
+ *
+ * In case the given reference is being pointed to by HEAD, then creating a
+ * reflog entry for this reference also requires us to create a corresponding
+ * reflog entry for HEAD. This function can be used to determine that scenario.
+ *
+ * @param out pointer to which the result will be written, `1` means a reflog
+ * entry should be written, `0` means none should be written.
+ * @param db The refdb to decide this for.
+ * @param ref The reference one wants to check.
+ * @return `0` on success, a negative error code otherwise.
+ */
+int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref);
+
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 eeddac3..7e04819 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -1181,54 +1181,28 @@ out:
*/
static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
{
- int error;
+ git_reference *head = NULL;
+ git_refdb *refdb = NULL;
+ int error, write_reflog;
git_oid old_id;
- git_reference *tmp = NULL, *head = NULL, *peeled = NULL;
- const char *name;
- if (ref->type == GIT_REFERENCE_SYMBOLIC)
- return 0;
+ if ((error = git_repository_refdb(&refdb, backend->repo)) < 0 ||
+ (error = git_refdb_should_write_head_reflog(&write_reflog, refdb, ref)) < 0)
+ goto out;
+ if (!write_reflog)
+ goto out;
/* if we can't resolve, we use {0}*40 as old id */
if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0)
memset(&old_id, 0, sizeof(old_id));
- if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0)
- return error;
-
- if (git_reference_type(head) == GIT_REFERENCE_DIRECT)
- goto cleanup;
-
- if ((error = git_reference_lookup(&tmp, backend->repo, GIT_HEAD_FILE)) < 0)
- goto cleanup;
-
- /* Go down the symref chain until we find the branch */
- while (git_reference_type(tmp) == GIT_REFERENCE_SYMBOLIC) {
- error = git_reference_lookup(&peeled, backend->repo, git_reference_symbolic_target(tmp));
- if (error < 0)
- break;
-
- git_reference_free(tmp);
- tmp = peeled;
- }
-
- if (error == GIT_ENOTFOUND) {
- error = 0;
- name = git_reference_symbolic_target(tmp);
- } else if (error < 0) {
- goto cleanup;
- } else {
- name = git_reference_name(tmp);
- }
-
- if (strcmp(name, ref->name))
- goto cleanup;
-
- error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message);
+ if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0 ||
+ (error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message)) < 0)
+ goto out;
-cleanup:
- git_reference_free(tmp);
+out:
git_reference_free(head);
+ git_refdb_free(refdb);
return error;
}