aboutsummaryrefslogtreecommitdiffstats
path: root/refs
diff options
context:
space:
mode:
Diffstat (limited to 'refs')
-rw-r--r--refs/debug.c5
-rw-r--r--refs/files-backend.c198
-rw-r--r--refs/ref-cache.c2
-rw-r--r--refs/refs-internal.h6
-rw-r--r--refs/reftable-backend.c84
5 files changed, 242 insertions, 53 deletions
diff --git a/refs/debug.c b/refs/debug.c
index da300efaf3..1cb955961e 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -277,7 +277,8 @@ struct debug_reflog {
void *cb_data;
};
-static int debug_print_reflog_ent(struct object_id *old_oid,
+static int debug_print_reflog_ent(const char *refname,
+ struct object_id *old_oid,
struct object_id *new_oid,
const char *committer, timestamp_t timestamp,
int tz, const char *msg, void *cb_data)
@@ -292,7 +293,7 @@ static int debug_print_reflog_ent(struct object_id *old_oid,
if (new_oid)
oid_to_hex_r(n, new_oid);
- ret = dbg->fn(old_oid, new_oid, committer, timestamp, tz, msg,
+ ret = dbg->fn(refname, old_oid, new_oid, committer, timestamp, tz, msg,
dbg->cb_data);
trace_printf_key(&trace_refs,
"reflog_ent %s (ret %d): %s -> %s, %s %ld \"%.*s\"\n",
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 088b52c740..bb2bec3807 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -68,6 +68,12 @@
*/
#define REF_DELETED_RMDIR (1 << 9)
+/*
+ * Used to indicate that the reflog-only update has been created via
+ * `split_head_update()`.
+ */
+#define REF_LOG_VIA_SPLIT (1 << 14)
+
struct ref_lock {
char *ref_name;
struct lock_file lk;
@@ -648,6 +654,26 @@ static void unlock_ref(struct ref_lock *lock)
}
/*
+ * Check if the transaction has another update with a case-insensitive refname
+ * match.
+ *
+ * If the update is part of the transaction, we only check up to that index.
+ * Further updates are expected to call this function to match previous indices.
+ */
+static bool transaction_has_case_conflicting_update(struct ref_transaction *transaction,
+ struct ref_update *update)
+{
+ for (size_t i = 0; i < transaction->nr; i++) {
+ if (transaction->updates[i] == update)
+ break;
+
+ if (!strcasecmp(transaction->updates[i]->refname, update->refname))
+ return true;
+ }
+ return false;
+}
+
+/*
* Lock refname, without following symrefs, and set *lock_p to point
* at a newly-allocated lock object. Fill in lock->old_oid, referent,
* and type similarly to read_raw_ref().
@@ -677,16 +703,17 @@ static void unlock_ref(struct ref_lock *lock)
* - Generate informative error messages in the case of failure
*/
static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
- struct ref_update *update,
+ struct ref_transaction *transaction,
size_t update_idx,
int mustexist,
struct string_list *refnames_to_check,
- const struct string_list *extras,
struct ref_lock **lock_p,
struct strbuf *referent,
struct strbuf *err)
{
enum ref_transaction_error ret = REF_TRANSACTION_ERROR_GENERIC;
+ struct ref_update *update = transaction->updates[update_idx];
+ const struct string_list *extras = &transaction->refnames;
const char *refname = update->refname;
unsigned int *type = &update->type;
struct ref_lock *lock;
@@ -776,6 +803,24 @@ retry:
goto retry;
} else {
unable_to_lock_message(ref_file.buf, myerr, err);
+ if (myerr == EEXIST) {
+ if (ignore_case &&
+ transaction_has_case_conflicting_update(transaction, update)) {
+ /*
+ * In case-insensitive filesystems, ensure that conflicts within a
+ * given transaction are handled. Pre-existing refs on a
+ * case-insensitive system will be overridden without any issue.
+ */
+ ret = REF_TRANSACTION_ERROR_CASE_CONFLICT;
+ } else {
+ /*
+ * Pre-existing case-conflicting reference locks should also be
+ * specially categorized to avoid failing all batched updates.
+ */
+ ret = REF_TRANSACTION_ERROR_CREATE_EXISTS;
+ }
+ }
+
goto error_return;
}
}
@@ -831,6 +876,7 @@ retry:
goto error_return;
} else if (remove_dir_recursively(&ref_file,
REMOVE_DIR_EMPTY_ONLY)) {
+ ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
if (refs_verify_refname_available(
&refs->base, refname,
extras, NULL, 0, err)) {
@@ -838,14 +884,14 @@ retry:
* The error message set by
* verify_refname_available() is OK.
*/
- ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
goto error_return;
} else {
/*
- * We can't delete the directory,
- * but we also don't know of any
- * references that it should
- * contain.
+ * Directory conflicts can occur if there
+ * is an existing lock file in the directory
+ * or if the filesystem is case-insensitive
+ * and the directory contains a valid reference
+ * but conflicts with the update.
*/
strbuf_addf(err, "there is a non-empty directory '%s' "
"blocking reference '%s'",
@@ -867,8 +913,23 @@ retry:
* If the ref did not exist and we are creating it, we have to
* make sure there is no existing packed ref that conflicts
* with refname. This check is deferred so that we can batch it.
+ *
+ * For case-insensitive filesystems, we should also check for F/D
+ * conflicts between 'foo' and 'Foo/bar'. So let's lowercase
+ * the refname.
*/
- item = string_list_append(refnames_to_check, refname);
+ if (ignore_case) {
+ struct strbuf lower = STRBUF_INIT;
+
+ strbuf_addstr(&lower, refname);
+ strbuf_tolower(&lower);
+
+ item = string_list_append_nodup(refnames_to_check,
+ strbuf_detach(&lower, NULL));
+ } else {
+ item = string_list_append(refnames_to_check, refname);
+ }
+
item->util = xmalloc(sizeof(update_idx));
memcpy(item->util, &update_idx, sizeof(update_idx));
}
@@ -1467,6 +1528,15 @@ static int files_pack_refs(struct ref_store *ref_store,
return 0;
}
+static int files_optimize(struct ref_store *ref_store, struct pack_refs_opts *opts)
+{
+ /*
+ * For the "files" backend, "optimizing" is the same as "packing".
+ * So, we just call the existing worker function for packing.
+ */
+ return files_pack_refs(ref_store, opts);
+}
+
/*
* People using contrib's git-new-workdir have .git/logs/refs ->
* /some/other/path/.git/logs/refs, and that may live on another device.
@@ -2109,7 +2179,9 @@ static int files_delete_reflog(struct ref_store *ref_store,
return ret;
}
-static int show_one_reflog_ent(struct files_ref_store *refs, struct strbuf *sb,
+static int show_one_reflog_ent(struct files_ref_store *refs,
+ const char *refname,
+ struct strbuf *sb,
each_reflog_ent_fn fn, void *cb_data)
{
struct object_id ooid, noid;
@@ -2136,7 +2208,7 @@ static int show_one_reflog_ent(struct files_ref_store *refs, struct strbuf *sb,
message += 6;
else
message += 7;
- return fn(&ooid, &noid, p, timestamp, tz, message, cb_data);
+ return fn(refname, &ooid, &noid, p, timestamp, tz, message, cb_data);
}
static char *find_beginning_of_line(char *bob, char *scan)
@@ -2220,7 +2292,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1));
scanp = bp;
endp = bp + 1;
- ret = show_one_reflog_ent(refs, &sb, fn, cb_data);
+ ret = show_one_reflog_ent(refs, refname, &sb, fn, cb_data);
strbuf_reset(&sb);
if (ret)
break;
@@ -2232,7 +2304,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
* Process it, and we can end the loop.
*/
strbuf_splice(&sb, 0, 0, buf, endp - buf);
- ret = show_one_reflog_ent(refs, &sb, fn, cb_data);
+ ret = show_one_reflog_ent(refs, refname, &sb, fn, cb_data);
strbuf_reset(&sb);
break;
}
@@ -2282,7 +2354,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
return -1;
while (!ret && !strbuf_getwholeline(&sb, logfp, '\n'))
- ret = show_one_reflog_ent(refs, &sb, fn, cb_data);
+ ret = show_one_reflog_ent(refs, refname, &sb, fn, cb_data);
fclose(logfp);
strbuf_release(&sb);
return ret;
@@ -2421,9 +2493,10 @@ static enum ref_transaction_error split_head_update(struct ref_update *update,
new_update = ref_transaction_add_update(
transaction, "HEAD",
- update->flags | REF_LOG_ONLY | REF_NO_DEREF,
+ update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT,
&update->new_oid, &update->old_oid,
NULL, NULL, update->committer_info, update->msg);
+ new_update->parent_update = update;
/*
* Add "HEAD". This insertion is O(N) in the transaction
@@ -2494,7 +2567,6 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update,
* done when new_update is processed.
*/
update->flags |= REF_LOG_ONLY | REF_NO_DEREF;
- update->flags &= ~REF_HAVE_OLD;
return 0;
}
@@ -2507,11 +2579,36 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update,
*/
static enum ref_transaction_error check_old_oid(struct ref_update *update,
struct object_id *oid,
+ struct strbuf *referent,
struct strbuf *err)
{
- if (!(update->flags & REF_HAVE_OLD) ||
- oideq(oid, &update->old_oid))
+ if (update->flags & REF_LOG_ONLY ||
+ !(update->flags & REF_HAVE_OLD))
+ return 0;
+
+ if (oideq(oid, &update->old_oid)) {
+ /*
+ * Normally matching the expected old oid is enough. Either we
+ * found the ref at the expected state, or we are creating and
+ * expect the null oid (and likewise found nothing).
+ *
+ * But there is one exception for the null oid: if we found a
+ * symref pointing to nothing we'll also get the null oid. In
+ * regular recursive mode, that's good (we'll write to what the
+ * symref points to, which doesn't exist). But in no-deref
+ * mode, it means we'll clobber the symref, even though the
+ * caller asked for this to be a creation event. So flag
+ * that case to preserve the dangling symref.
+ */
+ if ((update->flags & REF_NO_DEREF) && referent->len &&
+ is_null_oid(oid)) {
+ strbuf_addf(err, "cannot lock ref '%s': "
+ "dangling symref already exists",
+ ref_update_original_update_refname(update));
+ return REF_TRANSACTION_ERROR_CREATE_EXISTS;
+ }
return 0;
+ }
if (is_null_oid(&update->old_oid)) {
strbuf_addf(err, "cannot lock ref '%s': "
@@ -2583,9 +2680,8 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
if (lock) {
lock->count++;
} else {
- ret = lock_raw_ref(refs, update, update_idx, mustexist,
- refnames_to_check, &transaction->refnames,
- &lock, &referent, err);
+ ret = lock_raw_ref(refs, transaction, update_idx, mustexist,
+ refnames_to_check, &lock, &referent, err);
if (ret) {
char *reason;
@@ -2601,7 +2697,36 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
update->backend_data = lock;
- if (update->type & REF_ISSYMREF) {
+ if (update->flags & REF_LOG_VIA_SPLIT) {
+ struct ref_lock *parent_lock;
+
+ if (!update->parent_update)
+ BUG("split update without a parent");
+
+ parent_lock = update->parent_update->backend_data;
+
+ /*
+ * Check that "HEAD" didn't racily change since we have looked
+ * it up. If it did we must refuse to write the reflog entry.
+ *
+ * Note that this does not catch all races: if "HEAD" was
+ * racily changed to point to one of the refs part of the
+ * transaction then we would miss writing the split reflog
+ * entry for "HEAD".
+ */
+ if (!(update->type & REF_ISSYMREF) ||
+ strcmp(update->parent_update->refname, referent.buf)) {
+ strbuf_addstr(err, "HEAD has been racily updated");
+ ret = REF_TRANSACTION_ERROR_GENERIC;
+ goto out;
+ }
+
+ if (update->flags & REF_HAVE_OLD) {
+ oidcpy(&lock->old_oid, &update->old_oid);
+ } else {
+ oidcpy(&lock->old_oid, &parent_lock->old_oid);
+ }
+ } else if (update->type & REF_ISSYMREF) {
if (update->flags & REF_NO_DEREF) {
/*
* We won't be reading the referent as part of
@@ -2623,7 +2748,8 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
if (update->old_target)
ret = ref_update_check_old_target(referent.buf, update, err);
else
- ret = check_old_oid(update, &lock->old_oid, err);
+ ret = check_old_oid(update, &lock->old_oid,
+ &referent, err);
if (ret)
goto out;
} else {
@@ -2655,7 +2781,8 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
ret = REF_TRANSACTION_ERROR_EXPECTED_SYMREF;
goto out;
} else {
- ret = check_old_oid(update, &lock->old_oid, err);
+ ret = check_old_oid(update, &lock->old_oid,
+ &referent, err);
if (ret) {
goto out;
}
@@ -2794,7 +2921,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
"ref_transaction_prepare");
size_t i;
int ret = 0;
- struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
+ struct string_list refnames_to_check = STRING_LIST_INIT_DUP;
char *head_ref = NULL;
int head_type;
struct files_transaction_backend_data *backend_data;
@@ -2977,6 +3104,20 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
struct ref_lock *lock,
struct strbuf *err)
{
+ struct object_id *old_oid = &lock->old_oid;
+
+ if (update->flags & REF_LOG_USE_PROVIDED_OIDS) {
+ if (!(update->flags & REF_HAVE_OLD) ||
+ !(update->flags & REF_HAVE_NEW) ||
+ !(update->flags & REF_LOG_ONLY)) {
+ strbuf_addf(err, _("trying to write reflog for '%s'"
+ "with incomplete values"), update->refname);
+ return REF_TRANSACTION_ERROR_GENERIC;
+ }
+
+ old_oid = &update->old_oid;
+ }
+
if (update->new_target) {
/*
* We want to get the resolved OID for the target, to ensure
@@ -2994,7 +3135,7 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
}
}
- if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid,
+ if (files_log_ref_write(refs, lock->ref_name, old_oid,
&update->new_oid, update->committer_info,
update->msg, update->flags, err)) {
char *old_msg = strbuf_detach(err, NULL);
@@ -3062,7 +3203,8 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
- if ((update->flags & REF_HAVE_OLD) &&
+ if (!(update->flags & REF_LOG_ONLY) &&
+ (update->flags & REF_HAVE_OLD) &&
!is_null_oid(&update->old_oid))
BUG("initial ref transaction with old_sha1 set");
@@ -3309,7 +3451,8 @@ struct expire_reflog_cb {
dry_run:1;
};
-static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
+static int expire_reflog_ent(const char *refname UNUSED,
+ struct object_id *ooid, struct object_id *noid,
const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
@@ -3855,6 +3998,7 @@ struct ref_storage_be refs_be_files = {
.transaction_abort = files_transaction_abort,
.pack_refs = files_pack_refs,
+ .optimize = files_optimize,
.rename_ref = files_rename_ref,
.copy_ref = files_copy_ref,
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index c180e0aad7..e5e5df16d8 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -539,7 +539,7 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
*/
break;
}
- } while (slash);
+ } while (slash && dir->nr);
}
return 0;
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 40c1c0f93d..4ef3bd75c6 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -447,6 +447,8 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
typedef int pack_refs_fn(struct ref_store *ref_store,
struct pack_refs_opts *opts);
+typedef int optimize_fn(struct ref_store *ref_store,
+ struct pack_refs_opts *opts);
typedef int rename_ref_fn(struct ref_store *ref_store,
const char *oldref, const char *newref,
const char *logmsg);
@@ -572,6 +574,7 @@ struct ref_storage_be {
ref_transaction_abort_fn *transaction_abort;
pack_refs_fn *pack_refs;
+ optimize_fn *optimize;
rename_ref_fn *rename_ref;
copy_ref_fn *copy_ref;
@@ -662,7 +665,8 @@ enum ref_transaction_error ref_update_check_old_target(const char *referent,
/*
* Check if the ref must exist, this means that the old_oid or
- * old_target is non NULL.
+ * old_target is non NULL. Log-only updates never require the old state to
+ * match.
*/
int ref_update_expects_existing_old_ref(struct ref_update *update);
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 8dae1e1112..9884b876c1 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1012,10 +1012,6 @@ static int prepare_transaction_update(struct write_transaction_table_arg **out,
if (!arg) {
struct reftable_addition *addition;
- ret = reftable_stack_reload(be->stack);
- if (ret)
- return ret;
-
ret = reftable_stack_new_addition(&addition, be->stack,
REFTABLE_STACK_NEW_ADDITION_RELOAD);
if (ret) {
@@ -1102,6 +1098,20 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
if (ret)
return REF_TRANSACTION_ERROR_GENERIC;
+ if (u->flags & REF_LOG_USE_PROVIDED_OIDS) {
+ if (!(u->flags & REF_HAVE_OLD) ||
+ !(u->flags & REF_HAVE_NEW) ||
+ !(u->flags & REF_LOG_ONLY)) {
+ strbuf_addf(err, _("trying to write reflog for '%s'"
+ "with incomplete values"), u->refname);
+ return REF_TRANSACTION_ERROR_GENERIC;
+ }
+
+ if (queue_transaction_update(refs, tx_data, u, &u->old_oid, err))
+ return REF_TRANSACTION_ERROR_GENERIC;
+ return 0;
+ }
+
/* Verify that the new object ID is valid. */
if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
!(u->flags & REF_SKIP_OID_VERIFICATION) &&
@@ -1186,8 +1196,6 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
if (ret > 0) {
/* The reference does not exist, but we expected it to. */
strbuf_addf(err, _("cannot lock ref '%s': "
-
-
"unable to resolve reference '%s'"),
ref_update_original_update_refname(u), u->refname);
return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
@@ -1241,13 +1249,8 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
new_update->parent_update = u;
- /*
- * Change the symbolic ref update to log only. Also, it
- * doesn't need to check its old OID value, as that will be
- * done when new_update is processed.
- */
+ /* Change the symbolic ref update to log only. */
u->flags |= REF_LOG_ONLY | REF_NO_DEREF;
- u->flags &= ~REF_HAVE_OLD;
}
}
@@ -1271,8 +1274,33 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
ret = ref_update_check_old_target(referent->buf, u, err);
if (ret)
return ret;
- } else if ((u->flags & REF_HAVE_OLD) && !oideq(&current_oid, &u->old_oid)) {
- if (is_null_oid(&u->old_oid)) {
+ } else if ((u->flags & (REF_LOG_ONLY | REF_HAVE_OLD)) == REF_HAVE_OLD) {
+ if (oideq(&current_oid, &u->old_oid)) {
+ /*
+ * Normally matching the expected old oid is enough. Either we
+ * found the ref at the expected state, or we are creating and
+ * expect the null oid (and likewise found nothing).
+ *
+ * But there is one exception for the null oid: if we found a
+ * symref pointing to nothing we'll also get the null oid. In
+ * regular recursive mode, that's good (we'll write to what the
+ * symref points to, which doesn't exist). But in no-deref
+ * mode, it means we'll clobber the symref, even though the
+ * caller asked for this to be a creation event. So flag
+ * that case to preserve the dangling symref.
+ *
+ * Everything else is OK and we can fall through to the
+ * end of the conditional chain.
+ */
+ if ((u->flags & REF_NO_DEREF) &&
+ referent->len &&
+ is_null_oid(&u->old_oid)) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "dangling symref already exists"),
+ ref_update_original_update_refname(u));
+ return REF_TRANSACTION_ERROR_CREATE_EXISTS;
+ }
+ } else if (is_null_oid(&u->old_oid)) {
strbuf_addf(err, _("cannot lock ref '%s': "
"reference already exists"),
ref_update_original_update_refname(u));
@@ -1713,6 +1741,12 @@ out:
return ret;
}
+static int reftable_be_optimize(struct ref_store *ref_store,
+ struct pack_refs_opts *opts)
+{
+ return reftable_be_pack_refs(ref_store, opts);
+}
+
struct write_create_symref_arg {
struct reftable_ref_store *refs;
struct reftable_stack *stack;
@@ -1966,7 +2000,8 @@ static int reftable_be_rename_ref(struct ref_store *ref_store,
ret = backend_for(&arg.be, refs, newrefname, &newrefname, 1);
if (ret)
goto done;
- ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg);
+ ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg,
+ REFTABLE_STACK_NEW_ADDITION_RELOAD);
done:
assert(ret != REFTABLE_API_ERROR);
@@ -1995,7 +2030,8 @@ static int reftable_be_copy_ref(struct ref_store *ref_store,
ret = backend_for(&arg.be, refs, newrefname, &newrefname, 1);
if (ret)
goto done;
- ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg);
+ ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg,
+ REFTABLE_STACK_NEW_ADDITION_RELOAD);
done:
assert(ret != REFTABLE_API_ERROR);
@@ -2147,7 +2183,7 @@ static int yield_log_record(struct reftable_ref_store *refs,
full_committer = fmt_ident(log->value.update.name, log->value.update.email,
WANT_COMMITTER_IDENT, NULL, IDENT_NO_DATE);
- return fn(&old_oid, &new_oid, full_committer,
+ return fn(log->refname, &old_oid, &new_oid, full_committer,
log->value.update.time, log->value.update.tz_offset,
log->value.update.message, cb_data);
}
@@ -2367,7 +2403,8 @@ static int reftable_be_create_reflog(struct ref_store *ref_store,
goto done;
arg.stack = be->stack;
- ret = reftable_stack_add(be->stack, &write_reflog_existence_table, &arg);
+ ret = reftable_stack_add(be->stack, &write_reflog_existence_table, &arg,
+ REFTABLE_STACK_NEW_ADDITION_RELOAD);
done:
return ret;
@@ -2438,7 +2475,8 @@ static int reftable_be_delete_reflog(struct ref_store *ref_store,
return ret;
arg.stack = be->stack;
- ret = reftable_stack_add(be->stack, &write_reflog_delete_table, &arg);
+ ret = reftable_stack_add(be->stack, &write_reflog_delete_table, &arg,
+ REFTABLE_STACK_NEW_ADDITION_RELOAD);
assert(ret != REFTABLE_API_ERROR);
return ret;
@@ -2559,15 +2597,16 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
if (ret < 0)
goto done;
- ret = reftable_stack_init_log_iterator(be->stack, &it);
+ ret = reftable_stack_new_addition(&add, be->stack,
+ REFTABLE_STACK_NEW_ADDITION_RELOAD);
if (ret < 0)
goto done;
- ret = reftable_iterator_seek_log(&it, refname);
+ ret = reftable_stack_init_log_iterator(be->stack, &it);
if (ret < 0)
goto done;
- ret = reftable_stack_new_addition(&add, be->stack, 0);
+ ret = reftable_iterator_seek_log(&it, refname);
if (ret < 0)
goto done;
@@ -2694,6 +2733,7 @@ struct ref_storage_be refs_be_reftable = {
.transaction_abort = reftable_be_transaction_abort,
.pack_refs = reftable_be_pack_refs,
+ .optimize = reftable_be_optimize,
.rename_ref = reftable_be_rename_ref,
.copy_ref = reftable_be_copy_ref,