Commit e02478b19b1084775c0aa1f7c658958810d29752

Patrick Steinhardt 2020-06-05T08:17:03

refdb: extract function to check whether a reflog should be written The logic to determine whether a reflog should be written 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 reflog to be written as a preparatory step.

diff --git a/src/refdb.c b/src/refdb.c
index fbbf519..19b9887 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -231,6 +231,43 @@ int git_refdb_reflog_read(git_reflog **out, git_refdb *db,  const char *name)
 	return 0;
 }
 
+int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *ref)
+{
+	int error, logall;
+
+	error = git_repository__configmap_lookup(&logall, db->repo, GIT_CONFIGMAP_LOGALLREFUPDATES);
+	if (error < 0)
+		return error;
+
+	/* Defaults to the opposite of the repo being bare */
+	if (logall == GIT_LOGALLREFUPDATES_UNSET)
+		logall = !git_repository_is_bare(db->repo);
+
+	*out = 0;
+	switch (logall) {
+	case GIT_LOGALLREFUPDATES_FALSE:
+		*out = 0;
+		break;
+
+	case GIT_LOGALLREFUPDATES_TRUE:
+		/* Only write if it already has a log,
+		 * or if it's under heads/, remotes/ or notes/
+		 */
+		*out = git_refdb_has_log(db, ref->name) ||
+			!git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) ||
+			!git__strcmp(ref->name, GIT_HEAD_FILE) ||
+			!git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) ||
+			!git__prefixcmp(ref->name, GIT_REFS_NOTES_DIR);
+		break;
+
+	case GIT_LOGALLREFUPDATES_ALWAYS:
+		*out = 1;
+		break;
+	}
+
+	return 0;
+}
+
 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 2d4ec75..7578f66 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -50,6 +50,34 @@ int git_refdb_delete(git_refdb *refdb, const char *ref_name, const git_oid *old_
 int git_refdb_reflog_read(git_reflog **out, git_refdb *db,  const char *name);
 int git_refdb_reflog_write(git_reflog *reflog);
 
+/**
+ * Determine whether a reflog entry should be created for the given reference.
+ *
+ * Whether or not writing to a reference should create a reflog entry is
+ * dependent on a number of things. Most importantly, there's the
+ * "core.logAllRefUpdates" setting that controls in which situations a
+ * reference should get a corresponding reflog entry. The following values for
+ * it are understood:
+ *
+ *     - "false": Do not log reference updates.
+ *
+ *     - "true": Log normal reference updates. This will write entries for
+ *               references in "refs/heads", "refs/remotes", "refs/notes" and
+ *               "HEAD" or if the reference already has a log entry.
+ *
+ *     - "always": Always create a reflog entry.
+ *
+ * If unset, the value will default to "true" for non-bare repositories and
+ * "false" for bare ones.
+ *
+ * @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_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 28ea474..eeddac3 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -1128,44 +1128,6 @@ cleanup:
 }
 
 static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message);
-static int has_reflog(git_repository *repo, const char *name);
-
-static int should_write_reflog(int *write, git_repository *repo, const char *name)
-{
-	int error, logall;
-
-	error = git_repository__configmap_lookup(&logall, repo, GIT_CONFIGMAP_LOGALLREFUPDATES);
-	if (error < 0)
-		return error;
-
-	/* Defaults to the opposite of the repo being bare */
-	if (logall == GIT_LOGALLREFUPDATES_UNSET)
-		logall = !git_repository_is_bare(repo);
-
-	*write = 0;
-	switch (logall) {
-	case GIT_LOGALLREFUPDATES_FALSE:
-		*write = 0;
-		break;
-
-	case GIT_LOGALLREFUPDATES_TRUE:
-		/* Only write if it already has a log,
-		 * or if it's under heads/, remotes/ or notes/
-		 */
-		*write = has_reflog(repo, name) ||
-			!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);
-		break;
-
-	case GIT_LOGALLREFUPDATES_ALWAYS:
-		*write = 1;
-		break;
-	}
-
-	return 0;
-}
 
 static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
 	const git_oid *old_id, const char *old_target)
@@ -1335,7 +1297,10 @@ static int refdb_fs_backend__write_tail(
 	}
 
 	if (update_reflog) {
-		if ((error = should_write_reflog(&should_write, backend->repo, ref->name)) < 0)
+		git_refdb *refdb;
+
+		if ((error = git_repository_refdb__weakptr(&refdb, backend->repo)) < 0 ||
+		    (error = git_refdb_should_write_reflog(&should_write, refdb, ref)) < 0)
 			goto on_error;
 
 		if (should_write) {