Merge branch 'ps/refs-onbranch-fixes'

Reference backend configuration has been updated to load lazily to
avoid recursive calls during repository initialization when 'onbranch'
configuration conditions are evaluated. This has also fixed a memory
leak and allowed the unused `chdir_notify_reparent()` machinery to be
dropped.

* ps/refs-onbranch-fixes:
  refs: protect against chicken-and-egg recursion
  refs/reftable: lazy-load configuration to fix chicken-and-egg
  reftable: split up write options
  refs/files: lazy-load configuration to fix chicken-and-egg
  refs: move parsing of "core.logAllRefUpdates" back into ref stores
  repository: free main reference database
  chdir-notify: drop unused `chdir_notify_reparent()`
  refs: unregister reference stores from "chdir_notify"
  setup: don't apply "GIT_REFERENCE_BACKEND" without a repository
  setup: stop applying repository format twice
  setup: inline `check_and_apply_repository_format()`
main
Junio C Hamano 2026-07-06 15:50:24 -07:00
commit 3442a0fb79
29 changed files with 561 additions and 389 deletions

View File

@ -952,10 +952,13 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
const char *old_desc, *reflog_msg;
if (opts->new_branch) {
if (opts->new_orphan_branch) {
enum log_refs_config log_all_ref_updates =
repo_settings_get_log_all_ref_updates(the_repository);
enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
const char *value;
char *refname;

if (!repo_config_get_string_tmp(the_repository, "core.logallrefupdates", &value))
log_all_ref_updates = refs_parse_log_all_ref_updates_config(value);

refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
if (opts->new_branch_log &&
!should_autocreate_reflog(log_all_ref_updates, refname)) {

View File

@ -43,32 +43,6 @@ void chdir_notify_unregister(const char *name, chdir_notify_callback cb,
}
}

static void reparent_cb(const char *name,
const char *old_cwd,
const char *new_cwd,
void *data)
{
char **path = data;
char *tmp = *path;

if (!tmp)
return;

*path = reparent_relative_path(old_cwd, new_cwd, tmp);
free(tmp);

if (name) {
trace_printf_key(&trace_setup_key,
"setup: reparent %s to '%s'",
name, *path);
}
}

void chdir_notify_reparent(const char *name, char **path)
{
chdir_notify_register(name, reparent_cb, path);
}

int chdir_notify(const char *new_cwd)
{
struct strbuf old_cwd = STRBUF_INIT;

View File

@ -19,10 +19,7 @@
* chdir_notify_register("description", foo, data);
*
* In practice most callers will want to move a relative path to the new root;
* they can use the reparent_relative_path() helper for that. If that's all
* you're doing, you can also use the convenience function:
*
* chdir_notify_reparent("description", &my_path);
* they can use the reparent_relative_path() helper for that.
*
* Whenever a chdir event occurs, that will update my_path (if it's relative)
* to adjust for the new cwd by freeing any existing string and allocating a
@ -43,7 +40,6 @@ typedef void (*chdir_notify_callback)(const char *name,
void chdir_notify_register(const char *name, chdir_notify_callback cb, void *data);
void chdir_notify_unregister(const char *name, chdir_notify_callback cb,
void *data);
void chdir_notify_reparent(const char *name, char **path);

/*
*

17
refs.c
View File

@ -1053,6 +1053,15 @@ static char *normalize_reflog_message(const char *msg)
return strbuf_detach(&sb, NULL);
}

enum log_refs_config refs_parse_log_all_ref_updates_config(const char *value)
{
if (value && !strcasecmp(value, "always"))
return LOG_REFS_ALWAYS;
else if (git_config_bool("core.logallrefupdates", value))
return LOG_REFS_NORMAL;
return LOG_REFS_NONE;
}

int should_autocreate_reflog(enum log_refs_config log_all_ref_updates,
const char *refname)
{
@ -2327,7 +2336,6 @@ static struct ref_store *ref_store_init(struct repository *repo,
struct ref_store *refs;
struct ref_store_init_options opts = {
.access_flags = flags,
.log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo),
};

be = find_ref_storage_backend(format);
@ -2351,15 +2359,22 @@ void ref_store_release(struct ref_store *ref_store)

struct ref_store *get_main_ref_store(struct repository *r)
{
static bool initializing;

if (r->refs_private)
return r->refs_private;

if (!r->gitdir)
BUG("attempting to get main_ref_store outside of repository");
if (initializing)
BUG("initialization of main ref store is recursing");

initializing = true;
r->refs_private = ref_store_init(r, r->ref_storage_format,
r->gitdir, REF_STORE_ALL_CAPS);
r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);
initializing = false;

return r->refs_private;
}


9
refs.h
View File

@ -146,6 +146,15 @@ enum ref_transaction_error refs_verify_refname_available(struct ref_store *refs,

int refs_ref_exists(struct ref_store *refs, const char *refname);

enum log_refs_config {
LOG_REFS_UNSET = -1,
LOG_REFS_NONE = 0,
LOG_REFS_NORMAL,
LOG_REFS_ALWAYS
};

enum log_refs_config refs_parse_log_all_ref_updates_config(const char *value);

int should_autocreate_reflog(enum log_refs_config log_all_ref_updates,
const char *refname);


View File

@ -84,12 +84,21 @@ struct files_ref_store {
unsigned int store_flags;

char *gitcommondir;
enum log_refs_config log_all_ref_updates;
int prefer_symlink_refs;

struct ref_cache *loose;

struct ref_store *packed_ref_store;

/*
* Options used when writing references. These are parsed from the
* config lazily on first use via `files_ref_store_write_options()` so
* that we don't have to access the configuration when initializing the
* ref store. Do not access these fields directly, but use the accessor
* instead.
*/
struct files_ref_store_write_options {
enum log_refs_config log_all_ref_updates;
int prefer_symlink_refs;
bool initialized;
} write_opts_lazy_loaded;
};

static void clear_loose_ref_cache(struct files_ref_store *refs)
@ -100,6 +109,52 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
}
}

static void files_ref_store_reparent(const char *name UNUSED,
const char *old_cwd,
const char *new_cwd,
void *payload)
{
struct files_ref_store *refs = payload;
char *tmp;

tmp = reparent_relative_path(old_cwd, new_cwd, refs->base.gitdir);
free(refs->base.gitdir);
refs->base.gitdir = tmp;

tmp = reparent_relative_path(old_cwd, new_cwd, refs->gitcommondir);
free(refs->gitcommondir);
refs->gitcommondir = tmp;
}

static int files_ref_store_config(const char *var, const char *value,
const struct config_context *ctx UNUSED,
void *payload)
{
struct files_ref_store_write_options *opts = payload;

if (!strcmp(var, "core.prefersymlinkrefs")) {
opts->prefer_symlink_refs = git_config_bool(var, value);
} else if (!strcmp(var, "core.logallrefupdates")) {
opts->log_all_ref_updates = refs_parse_log_all_ref_updates_config(value);
}

return 0;
}

static const struct files_ref_store_write_options *files_ref_store_write_options(struct files_ref_store *refs)
{
struct files_ref_store_write_options *opts = &refs->write_opts_lazy_loaded;

if (opts->initialized)
return opts;

opts->log_all_ref_updates = LOG_REFS_UNSET;
repo_config(refs->base.repo, files_ref_store_config, opts);

opts->initialized = true;
return opts;
}

/*
* Create a new submodule ref cache and add it to the internal
* set of caches.
@ -124,13 +179,8 @@ static struct ref_store *files_ref_store_init(struct repository *repo,
refs->packed_ref_store =
packed_ref_store_init(repo, NULL, refs->gitcommondir, opts);
refs->store_flags = opts->access_flags;
refs->log_all_ref_updates = opts->log_all_ref_updates;

repo_config_get_bool(repo, "core.prefersymlinkrefs", &refs->prefer_symlink_refs);

chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
chdir_notify_reparent("files-backend $GIT_COMMONDIR",
&refs->gitcommondir);
chdir_notify_register(NULL, files_ref_store_reparent, refs);

strbuf_release(&refdir);

@ -182,6 +232,7 @@ static void files_ref_store_release(struct ref_store *ref_store)
free(refs->gitcommondir);
ref_store_release(refs->packed_ref_store);
free(refs->packed_ref_store);
chdir_notify_unregister(NULL, files_ref_store_reparent, refs);
}

static void files_reflog_path(struct files_ref_store *refs,
@ -1860,7 +1911,7 @@ static int log_ref_setup(struct files_ref_store *refs,
const char *refname, int force_create,
int *logfd, struct strbuf *err)
{
enum log_refs_config log_refs_cfg = refs->log_all_ref_updates;
enum log_refs_config log_refs_cfg = files_ref_store_write_options(refs)->log_all_ref_updates;
struct strbuf logfile_sb = STRBUF_INIT;
char *logfile;

@ -3271,6 +3322,7 @@ static int files_transaction_finish(struct ref_store *ref_store,
{
struct files_ref_store *refs =
files_downcast(ref_store, 0, "ref_transaction_finish");
const struct files_ref_store_write_options *write_opts = files_ref_store_write_options(refs);
size_t i;
int ret = 0;
struct strbuf sb = STRBUF_INIT;
@ -3310,7 +3362,7 @@ static int files_transaction_finish(struct ref_store *ref_store,
* We try creating a symlink, if that succeeds we continue to the
* next update. If not, we try and create a regular symref.
*/
if (update->new_target && refs->prefer_symlink_refs)
if (update->new_target && write_opts->prefer_symlink_refs)
/*
* By using the `NOT_CONSTANT()` trick, we can avoid
* errors by `clang`'s `-Wunreachable` logic that would

View File

@ -211,6 +211,19 @@ static size_t snapshot_hexsz(const struct snapshot *snapshot)
return snapshot->refs->base.repo->hash_algo->hexsz;
}

static void packed_ref_store_reparent(const char *name UNUSED,
const char *old_cwd,
const char *new_cwd,
void *payload)
{
struct packed_ref_store *refs = payload;
char *tmp;

tmp = reparent_relative_path(old_cwd, new_cwd, refs->path);
free(refs->path);
refs->path = tmp;
}

/*
* Since packed-refs is only stored in the common dir, don't parse the
* payload and rely on the files-backend to set 'gitdir' correctly.
@ -229,7 +242,7 @@ struct ref_store *packed_ref_store_init(struct repository *repo,

strbuf_addf(&sb, "%s/packed-refs", gitdir);
refs->path = strbuf_detach(&sb, NULL);
chdir_notify_reparent("packed-refs", &refs->path);
chdir_notify_register(NULL, packed_ref_store_reparent, refs);
return ref_store;
}

@ -274,6 +287,7 @@ static void packed_ref_store_release(struct ref_store *ref_store)
clear_snapshot(refs);
rollback_lock_file(&refs->lock);
delete_tempfile(&refs->tempfile);
chdir_notify_unregister(NULL, packed_ref_store_reparent, refs);
free(refs->path);
}


View File

@ -406,12 +406,6 @@ struct ref_store;
struct ref_store_init_options {
/* The kind of operations that the ref_store is allowed to perform. */
unsigned int access_flags;

/*
* Denotes under what conditions reflogs should be created when updating
* references.
*/
enum log_refs_config log_all_ref_updates;
};

/*

View File

@ -46,9 +46,9 @@ static void reftable_backend_on_reload(void *payload)

static int reftable_backend_init(struct reftable_backend *be,
const char *path,
const struct reftable_write_options *_opts)
const struct reftable_stack_options *_opts)
{
struct reftable_write_options opts = *_opts;
struct reftable_stack_options opts = *_opts;
opts.on_reload = reftable_backend_on_reload;
opts.on_reload_payload = be;
return reftable_new_stack(&be->stack, path, &opts);
@ -138,10 +138,22 @@ struct reftable_ref_store {
* is populated lazily when we try to resolve `worktrees/$worktree` refs.
*/
struct strmap worktree_backends;
struct reftable_write_options write_options;
struct reftable_stack_options stack_options;

/*
* Options used when writing to or compacting the reftable stacks.
* These are parsed from the configuration lazily on first use via
* `reftable_be_write_options()` so that we don't have to access the
* configuration when initializing the ref store. Do not access these
* fields directly, but use the accessor instead.
*/
struct reftable_be_write_options {
struct reftable_write_options opts;
enum log_refs_config log_all_ref_updates;
bool initialized;
} write_opts_lazy_loaded;

unsigned int store_flags;
enum log_refs_config log_all_ref_updates;
int err;
};

@ -188,7 +200,7 @@ static int backend_for_worktree(struct reftable_backend **out,

CALLOC_ARRAY(*out, 1);
store->err = ret = reftable_backend_init(*out, worktree_dir.buf,
&store->write_options);
&store->stack_options);
if (ret < 0) {
free(*out);
goto out;
@ -282,26 +294,6 @@ out:
return ret;
}

static int should_write_log(struct reftable_ref_store *refs, const char *refname)
{
enum log_refs_config log_refs_cfg = refs->log_all_ref_updates;
if (log_refs_cfg == LOG_REFS_UNSET)
log_refs_cfg = is_bare_repository(refs->base.repo) ? LOG_REFS_NONE : LOG_REFS_NORMAL;

switch (log_refs_cfg) {
case LOG_REFS_NONE:
return refs_reflog_exists(&refs->base, refname);
case LOG_REFS_ALWAYS:
return 1;
case LOG_REFS_NORMAL:
if (should_autocreate_reflog(log_refs_cfg, refname))
return 1;
return refs_reflog_exists(&refs->base, refname);
default:
BUG("unhandled core.logAllRefUpdates value %d", log_refs_cfg);
}
}

static void fill_reftable_log_record(struct reftable_log_record *log, const struct ident_split *split)
{
const char *tz_begin;
@ -330,39 +322,88 @@ static void fill_reftable_log_record(struct reftable_log_record *log, const stru

static int reftable_be_config(const char *var, const char *value,
const struct config_context *ctx,
void *_opts)
void *payload)
{
struct reftable_write_options *opts = _opts;
struct reftable_ref_store *refs = payload;
struct reftable_be_write_options *opts = &refs->write_opts_lazy_loaded;

if (!strcmp(var, "reftable.blocksize")) {
unsigned long block_size = git_config_ulong(var, value, ctx->kvi);
if (block_size > 16777215)
die("reftable block size cannot exceed 16MB");
opts->block_size = block_size;
opts->opts.block_size = block_size;
} else if (!strcmp(var, "reftable.restartinterval")) {
unsigned long restart_interval = git_config_ulong(var, value, ctx->kvi);
if (restart_interval > UINT16_MAX)
die("reftable block size cannot exceed %u", (unsigned)UINT16_MAX);
opts->restart_interval = restart_interval;
opts->opts.restart_interval = restart_interval;
} else if (!strcmp(var, "reftable.indexobjects")) {
opts->skip_index_objects = !git_config_bool(var, value);
opts->opts.skip_index_objects = !git_config_bool(var, value);
} else if (!strcmp(var, "reftable.geometricfactor")) {
unsigned long factor = git_config_ulong(var, value, ctx->kvi);
if (factor > UINT8_MAX)
die("reftable geometric factor cannot exceed %u", (unsigned)UINT8_MAX);
opts->auto_compaction_factor = factor;
opts->opts.auto_compaction_factor = factor;
} else if (!strcmp(var, "reftable.locktimeout")) {
int64_t lock_timeout = git_config_int64(var, value, ctx->kvi);
if (lock_timeout > LONG_MAX)
die("reftable lock timeout cannot exceed %"PRIdMAX, (intmax_t)LONG_MAX);
if (lock_timeout < 0 && lock_timeout != -1)
die("reftable lock timeout does not support negative values other than -1");
opts->lock_timeout_ms = lock_timeout;
opts->opts.lock_timeout_ms = lock_timeout;
} else if (!strcmp(var, "core.logallrefupdates")) {
opts->log_all_ref_updates = refs_parse_log_all_ref_updates_config(value);
}

return 0;
}

static const struct reftable_be_write_options *reftable_be_write_options(struct reftable_ref_store *refs)
{
struct reftable_be_write_options *opts = &refs->write_opts_lazy_loaded;
mode_t mask;

if (opts->initialized)
return opts;

mask = umask(0);
umask(mask);

opts->opts.default_permissions = calc_shared_perm(refs->base.repo, 0666 & ~mask);
opts->opts.disable_auto_compact =
!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
opts->opts.lock_timeout_ms = 100;
opts->log_all_ref_updates = LOG_REFS_UNSET;

repo_config(refs->base.repo, reftable_be_config, refs);

/*
* It is somewhat unfortunate that we have to mirror the default block
* size of the reftable library here. But given that the write options
* wouldn't be updated by the library here, and given that we require
* the proper block size to trim reflog message so that they fit, we
* must set up a proper value here.
*/
if (!opts->opts.block_size)
opts->opts.block_size = 4096;

opts->initialized = true;
return opts;
}

static void reftable_be_reparent(const char *name UNUSED,
const char *old_cwd,
const char *new_cwd,
void *payload)
{
struct reftable_ref_store *refs = payload;
char *tmp;

tmp = reparent_relative_path(old_cwd, new_cwd, refs->base.gitdir);
free(refs->base.gitdir);
refs->base.gitdir = tmp;
}

static struct ref_store *reftable_be_init(struct repository *repo,
const char *payload,
const char *gitdir,
@ -373,45 +414,24 @@ static struct ref_store *reftable_be_init(struct repository *repo,
struct strbuf refdir = STRBUF_INIT;
struct strbuf path = STRBUF_INIT;
bool is_worktree;
mode_t mask;

mask = umask(0);
umask(mask);

refs_compute_filesystem_location(gitdir, payload, &is_worktree, &refdir,
&ref_common_dir);

base_ref_store_init(&refs->base, repo, refdir.buf, &refs_be_reftable);
strmap_init(&refs->worktree_backends);
refs->log_all_ref_updates = opts->log_all_ref_updates;
refs->store_flags = opts->access_flags;

switch (repo->hash_algo->format_id) {
case GIT_SHA1_FORMAT_ID:
refs->write_options.hash_id = REFTABLE_HASH_SHA1;
refs->stack_options.hash_id = REFTABLE_HASH_SHA1;
break;
case GIT_SHA256_FORMAT_ID:
refs->write_options.hash_id = REFTABLE_HASH_SHA256;
refs->stack_options.hash_id = REFTABLE_HASH_SHA256;
break;
default:
BUG("unknown hash algorithm %d", repo->hash_algo->format_id);
}
refs->write_options.default_permissions = calc_shared_perm(repo, 0666 & ~mask);
refs->write_options.disable_auto_compact =
!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
refs->write_options.lock_timeout_ms = 100;

repo_config(repo, reftable_be_config, &refs->write_options);

/*
* It is somewhat unfortunate that we have to mirror the default block
* size of the reftable library here. But given that the write options
* wouldn't be updated by the library here, and given that we require
* the proper block size to trim reflog message so that they fit, we
* must set up a proper value here.
*/
if (!refs->write_options.block_size)
refs->write_options.block_size = 4096;

/*
* Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
@ -424,7 +444,7 @@ static struct ref_store *reftable_be_init(struct repository *repo,
}
strbuf_addstr(&path, "/reftable");
refs->err = reftable_backend_init(&refs->main_backend, path.buf,
&refs->write_options);
&refs->stack_options);
if (refs->err)
goto done;

@ -440,12 +460,12 @@ static struct ref_store *reftable_be_init(struct repository *repo,
strbuf_addstr(&refdir, "/reftable");

refs->err = reftable_backend_init(&refs->worktree_backend, refdir.buf,
&refs->write_options);
&refs->stack_options);
if (refs->err)
goto done;
}

chdir_notify_reparent("reftables-backend $GIT_DIR", &refs->base.gitdir);
chdir_notify_register(NULL, reftable_be_reparent, refs);

done:
assert(refs->err != REFTABLE_API_ERROR);
@ -472,6 +492,7 @@ static void reftable_be_release(struct ref_store *ref_store)
free(be);
}
strmap_clear(&refs->worktree_backends, 0);
chdir_notify_unregister(NULL, reftable_be_reparent, refs);
}

static int reftable_be_create_on_disk(struct ref_store *ref_store,
@ -979,6 +1000,7 @@ static int prepare_transaction_update(struct write_transaction_table_arg **out,
struct reftable_addition *addition;

ret = reftable_stack_new_addition(&addition, be->stack,
&reftable_be_write_options(refs)->opts,
REFTABLE_STACK_NEW_ADDITION_RELOAD);
if (ret) {
if (ret == REFTABLE_LOCK_ERROR)
@ -1417,6 +1439,26 @@ static int transaction_update_cmp(const void *a, const void *b)
return strcmp(update_a->update->refname, update_b->update->refname);
}

static int should_write_log(struct reftable_ref_store *refs, const char *refname)
{
enum log_refs_config log_refs_cfg = reftable_be_write_options(refs)->log_all_ref_updates;
if (log_refs_cfg == LOG_REFS_UNSET)
log_refs_cfg = is_bare_repository(refs->base.repo) ? LOG_REFS_NONE : LOG_REFS_NORMAL;

switch (log_refs_cfg) {
case LOG_REFS_NONE:
return refs_reflog_exists(&refs->base, refname);
case LOG_REFS_ALWAYS:
return 1;
case LOG_REFS_NORMAL:
if (should_autocreate_reflog(log_refs_cfg, refname))
return 1;
return refs_reflog_exists(&refs->base, refname);
default:
BUG("unhandled core.logAllRefUpdates value %d", log_refs_cfg);
}
}

static int write_transaction_table(struct reftable_writer *writer, void *cb_data)
{
struct write_transaction_table_arg *arg = cb_data;
@ -1551,7 +1593,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
memcpy(log->value.update.old_hash,
tx_update->current_oid.hash, GIT_MAX_RAWSZ);
log->value.update.message =
xstrndup(u->msg, arg->refs->write_options.block_size / 2);
xstrndup(u->msg, reftable_be_write_options(arg->refs)->opts.block_size / 2);
}
}

@ -1667,9 +1709,9 @@ static int reftable_be_optimize(struct ref_store *ref_store,
stack = refs->main_backend.stack;

if (opts->flags & REFS_OPTIMIZE_AUTO)
ret = reftable_stack_auto_compact(stack);
ret = reftable_stack_auto_compact(stack, &reftable_be_write_options(refs)->opts);
else
ret = reftable_stack_compact_all(stack, NULL);
ret = reftable_stack_compact_all(stack, &reftable_be_write_options(refs)->opts, NULL);
if (ret < 0) {
ret = error(_("unable to compact stack: %s"),
reftable_error_str(ret));
@ -1703,8 +1745,8 @@ static int reftable_be_optimize_required(struct ref_store *ref_store,
if (opts->flags & REFS_OPTIMIZE_AUTO)
use_heuristics = true;

return reftable_stack_compaction_required(stack, use_heuristics,
required);
return reftable_stack_compaction_required(stack, &reftable_be_write_options(refs)->opts,
use_heuristics, required);
}

struct write_create_symref_arg {
@ -1823,7 +1865,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
logs[logs_nr].refname = xstrdup(arg->newname);
logs[logs_nr].update_index = deletion_ts;
logs[logs_nr].value.update.message =
xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
xstrndup(arg->logmsg, reftable_be_write_options(arg->refs)->opts.block_size / 2);
memcpy(logs[logs_nr].value.update.old_hash, old_ref.value.val1, GIT_MAX_RAWSZ);
logs_nr++;

@ -1862,7 +1904,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
logs[logs_nr].refname = xstrdup(arg->newname);
logs[logs_nr].update_index = creation_ts;
logs[logs_nr].value.update.message =
xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
xstrndup(arg->logmsg, reftable_be_write_options(arg->refs)->opts.block_size / 2);
memcpy(logs[logs_nr].value.update.new_hash, old_ref.value.val1, GIT_MAX_RAWSZ);
logs_nr++;

@ -1961,6 +2003,7 @@ static int reftable_be_rename_ref(struct ref_store *ref_store,
if (ret)
goto done;
ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg,
&reftable_be_write_options(refs)->opts,
REFTABLE_STACK_NEW_ADDITION_RELOAD);

done:
@ -1991,6 +2034,7 @@ static int reftable_be_copy_ref(struct ref_store *ref_store,
if (ret)
goto done;
ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg,
&reftable_be_write_options(refs)->opts,
REFTABLE_STACK_NEW_ADDITION_RELOAD);

done:
@ -2356,6 +2400,7 @@ static int reftable_be_create_reflog(struct ref_store *ref_store,
arg.stack = be->stack;

ret = reftable_stack_add(be->stack, &write_reflog_existence_table, &arg,
&reftable_be_write_options(refs)->opts,
REFTABLE_STACK_NEW_ADDITION_RELOAD);

done:
@ -2428,6 +2473,7 @@ static int reftable_be_delete_reflog(struct ref_store *ref_store,
arg.stack = be->stack;

ret = reftable_stack_add(be->stack, &write_reflog_delete_table, &arg,
&reftable_be_write_options(refs)->opts,
REFTABLE_STACK_NEW_ADDITION_RELOAD);

assert(ret != REFTABLE_API_ERROR);
@ -2550,6 +2596,7 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,
goto done;

ret = reftable_stack_new_addition(&add, be->stack,
&reftable_be_write_options(refs)->opts,
REFTABLE_STACK_NEW_ADDITION_RELOAD);
if (ret < 0)
goto done;

View File

@ -26,11 +26,29 @@
*/
struct reftable_stack;

/* Options related to opening a stack. */
struct reftable_stack_options {
/*
* 4-byte identifier ("sha1", "s256") of the hash. Defaults to SHA1 if
* unset.
*/
enum reftable_hash hash_id;

/*
* Callback function to execute whenever the stack is being reloaded.
* This can be used e.g. to discard cached information that relies on
* the old stack's data. The payload data will be passed as argument to
* the callback.
*/
void (*on_reload)(void *payload);
void *on_reload_payload;
};

/* open a new reftable stack. The tables along with the table list will be
* stored in 'dir'. Typically, this should be .git/reftables.
*/
int reftable_new_stack(struct reftable_stack **dest, const char *dir,
const struct reftable_write_options *opts);
const struct reftable_stack_options *opts);

/* returns the update_index at which a next table should be written. */
uint64_t reftable_stack_next_update_index(struct reftable_stack *st);
@ -52,6 +70,7 @@ enum {
*/
int reftable_stack_new_addition(struct reftable_addition **dest,
struct reftable_stack *st,
const struct reftable_write_options *opts,
unsigned int flags);

/* Adds a reftable to transaction. */
@ -77,7 +96,9 @@ void reftable_addition_destroy(struct reftable_addition *add);
int reftable_stack_add(struct reftable_stack *st,
int (*write_table)(struct reftable_writer *wr,
void *write_arg),
void *write_arg, unsigned flags);
void *write_arg,
const struct reftable_write_options *opts,
unsigned flags);

struct reftable_iterator;

@ -122,6 +143,7 @@ struct reftable_log_expiry_config {
/* compacts all reftables into a giant table. Expire reflog entries if config is
* non-NULL */
int reftable_stack_compact_all(struct reftable_stack *st,
const struct reftable_write_options *opts,
struct reftable_log_expiry_config *config);

/*
@ -132,11 +154,13 @@ int reftable_stack_compact_all(struct reftable_stack *st,
* compacted to maintain geometric progression.
*/
int reftable_stack_compaction_required(struct reftable_stack *st,
const struct reftable_write_options *opts,
bool use_heuristics,
bool *required);

/* heuristically compact unbalanced table stack. */
int reftable_stack_auto_compact(struct reftable_stack *st);
int reftable_stack_auto_compact(struct reftable_stack *st,
const struct reftable_write_options *opts);

/* delete stale .ref tables. */
int reftable_stack_clean(struct reftable_stack *st);

View File

@ -28,11 +28,6 @@ struct reftable_write_options {
/* how often to write complete keys in each block. */
uint16_t restart_interval;

/* 4-byte identifier ("sha1", "s256") of the hash.
* Defaults to SHA1 if unset
*/
enum reftable_hash hash_id;

/* Default mode for creating files. If unset, use 0666 (+umask) */
unsigned int default_permissions;

@ -60,15 +55,6 @@ struct reftable_write_options {
* negative value will cause us to block indefinitely.
*/
long lock_timeout_ms;

/*
* Callback function to execute whenever the stack is being reloaded.
* This can be used e.g. to discard cached information that relies on
* the old stack's data. The payload data will be passed as argument to
* the callback.
*/
void (*on_reload)(void *payload);
void *on_reload_payload;
};

/* reftable_block_stats holds statistics for a single block type */
@ -114,7 +100,8 @@ struct reftable_writer;
int reftable_writer_new(struct reftable_writer **out,
ssize_t (*writer_func)(void *, const void *, size_t),
int (*flush_func)(void *),
void *writer_arg, const struct reftable_write_options *opts);
void *writer_arg, enum reftable_hash hash_id,
const struct reftable_write_options *opts);

/*
* Set the range of update indices for the records we will add. When writing a

View File

@ -501,10 +501,10 @@ out:
}

int reftable_new_stack(struct reftable_stack **dest, const char *dir,
const struct reftable_write_options *_opts)
const struct reftable_stack_options *_opts)
{
struct reftable_buf list_file_name = REFTABLE_BUF_INIT;
struct reftable_write_options opts = { 0 };
struct reftable_stack_options opts = { 0 };
struct reftable_stack *p;
int err;

@ -629,6 +629,7 @@ int reftable_stack_reload(struct reftable_stack *st)
struct reftable_addition {
struct reftable_flock tables_list_lock;
struct reftable_stack *stack;
struct reftable_write_options opts;

char **new_tables;
size_t new_tables_len, new_tables_cap;
@ -657,6 +658,7 @@ static void reftable_addition_close(struct reftable_addition *add)

static int reftable_stack_init_addition(struct reftable_addition *add,
struct reftable_stack *st,
const struct reftable_write_options *opts,
unsigned int flags)
{
struct reftable_buf lock_file_name = REFTABLE_BUF_INIT;
@ -664,15 +666,17 @@ static int reftable_stack_init_addition(struct reftable_addition *add,

memset(add, 0, sizeof(*add));
add->stack = st;
if (opts)
add->opts = *opts;

err = flock_acquire(&add->tables_list_lock, st->list_file,
st->opts.lock_timeout_ms);
add->opts.lock_timeout_ms);
if (err < 0)
goto done;

if (st->opts.default_permissions) {
if (add->opts.default_permissions) {
if (chmod(add->tables_list_lock.path,
st->opts.default_permissions) < 0) {
add->opts.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
@ -702,12 +706,14 @@ done:
static int stack_try_add(struct reftable_stack *st,
int (*write_table)(struct reftable_writer *wr,
void *arg),
void *arg, unsigned flags)
void *arg,
const struct reftable_write_options *opts,
unsigned flags)
{
struct reftable_addition add;
int err;

err = reftable_stack_init_addition(&add, st, flags);
err = reftable_stack_init_addition(&add, st, opts, flags);
if (err < 0)
goto done;

@ -723,9 +729,11 @@ done:

int reftable_stack_add(struct reftable_stack *st,
int (*write)(struct reftable_writer *wr, void *arg),
void *arg, unsigned flags)
void *arg,
const struct reftable_write_options *opts,
unsigned flags)
{
int err = stack_try_add(st, write, arg, flags);
int err = stack_try_add(st, write, arg, opts, flags);
if (err < 0) {
if (err == REFTABLE_OUTDATED_ERROR) {
/* Ignore error return, we want to propagate
@ -810,7 +818,7 @@ int reftable_addition_commit(struct reftable_addition *add)
if (err)
goto done;

if (!add->stack->opts.disable_auto_compact) {
if (!add->opts.disable_auto_compact) {
/*
* Auto-compact the stack to keep the number of tables in
* control. It is possible that a concurrent writer is already
@ -820,7 +828,7 @@ int reftable_addition_commit(struct reftable_addition *add)
* concurrent writer, which causes `REFTABLE_OUTDATED_ERROR`.
* Both of these errors are benign, so we simply ignore them.
*/
err = reftable_stack_auto_compact(add->stack);
err = reftable_stack_auto_compact(add->stack, &add->opts);
if (err < 0 && err != REFTABLE_LOCK_ERROR &&
err != REFTABLE_OUTDATED_ERROR)
goto done;
@ -834,6 +842,7 @@ done:

int reftable_stack_new_addition(struct reftable_addition **dest,
struct reftable_stack *st,
const struct reftable_write_options *opts,
unsigned int flags)
{
int err;
@ -842,7 +851,7 @@ int reftable_stack_new_addition(struct reftable_addition **dest,
if (!*dest)
return REFTABLE_OUT_OF_MEMORY_ERROR;

err = reftable_stack_init_addition(*dest, st, flags);
err = reftable_stack_init_addition(*dest, st, opts, flags);
if (err) {
reftable_free(*dest);
*dest = NULL;
@ -862,7 +871,7 @@ int reftable_addition_add(struct reftable_addition *add,
struct reftable_writer *wr = NULL;
struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT;
struct fd_writer writer = {
.opts = &add->stack->opts,
.opts = &add->opts,
};
int err = 0;

@ -883,9 +892,9 @@ int reftable_addition_add(struct reftable_addition *add,
err = tmpfile_from_pattern(&tab_file, temp_tab_file_name.buf);
if (err < 0)
goto done;
if (add->stack->opts.default_permissions) {
if (add->opts.default_permissions) {
if (chmod(tab_file.path,
add->stack->opts.default_permissions)) {
add->opts.default_permissions)) {
err = REFTABLE_IO_ERROR;
goto done;
}
@ -893,7 +902,7 @@ int reftable_addition_add(struct reftable_addition *add,

writer.fd = tab_file.fd;
err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush,
&writer, &add->stack->opts);
&writer, add->stack->opts.hash_id, &add->opts);
if (err < 0)
goto done;

@ -1066,13 +1075,14 @@ done:
static int stack_compact_locked(struct reftable_stack *st,
size_t first, size_t last,
struct reftable_log_expiry_config *config,
const struct reftable_write_options *opts,
struct reftable_tmpfile *tab_file_out)
{
struct reftable_buf next_name = REFTABLE_BUF_INIT;
struct reftable_buf tab_file_path = REFTABLE_BUF_INIT;
struct reftable_writer *wr = NULL;
struct fd_writer writer= {
.opts = &st->opts,
.opts = opts,
};
struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT;
int err = 0;
@ -1094,15 +1104,15 @@ static int stack_compact_locked(struct reftable_stack *st,
if (err < 0)
goto done;

if (st->opts.default_permissions &&
chmod(tab_file.path, st->opts.default_permissions) < 0) {
if (opts->default_permissions &&
chmod(tab_file.path, opts->default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}

writer.fd = tab_file.fd;
err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush,
&writer, &st->opts);
&writer, st->opts.hash_id, opts);
if (err < 0)
goto done;

@ -1150,6 +1160,7 @@ enum stack_compact_range_flags {
static int stack_compact_range(struct reftable_stack *st,
size_t first, size_t last,
struct reftable_log_expiry_config *expiry,
const struct reftable_write_options *opts,
unsigned int flags)
{
struct reftable_buf tables_list_buf = REFTABLE_BUF_INIT;
@ -1175,7 +1186,7 @@ static int stack_compact_range(struct reftable_stack *st,
* Hold the lock so that we can read "tables.list" and lock all tables
* which are part of the user-specified range.
*/
err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms);
err = flock_acquire(&tables_list_lock, st->list_file, opts->lock_timeout_ms);
if (err < 0)
goto done;

@ -1274,7 +1285,7 @@ static int stack_compact_range(struct reftable_stack *st,
* these tables may end up with an empty new table in case tombstones
* end up cancelling out all refs in that range.
*/
err = stack_compact_locked(st, first, last, expiry, &new_table);
err = stack_compact_locked(st, first, last, expiry, opts, &new_table);
if (err < 0) {
if (err != REFTABLE_EMPTY_TABLE_ERROR)
goto done;
@ -1286,13 +1297,13 @@ static int stack_compact_range(struct reftable_stack *st,
* "tables.list". We'll then replace the compacted range of tables with
* the new table.
*/
err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms);
err = flock_acquire(&tables_list_lock, st->list_file, opts->lock_timeout_ms);
if (err < 0)
goto done;

if (st->opts.default_permissions) {
if (opts->default_permissions) {
if (chmod(tables_list_lock.path,
st->opts.default_permissions) < 0) {
opts->default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
@ -1513,10 +1524,16 @@ done:
}

int reftable_stack_compact_all(struct reftable_stack *st,
const struct reftable_write_options *opts,
struct reftable_log_expiry_config *config)
{
struct reftable_write_options opts_default = { 0 };
size_t last = st->merged->tables_len ? st->merged->tables_len - 1 : 0;
return stack_compact_range(st, 0, last, config, 0);

if (!opts)
opts = &opts_default;

return stack_compact_range(st, 0, last, config, opts, 0);
}

static int segment_size(struct segment *s)
@ -1601,6 +1618,7 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n,
}

static int stack_segments_for_compaction(struct reftable_stack *st,
const struct reftable_write_options *opts,
struct segment *seg)
{
int version = (st->opts.hash_id == REFTABLE_HASH_SHA1) ? 1 : 2;
@ -1615,13 +1633,14 @@ static int stack_segments_for_compaction(struct reftable_stack *st,
sizes[i] = st->tables[i]->size - overhead;

*seg = suggest_compaction_segment(sizes, st->merged->tables_len,
st->opts.auto_compaction_factor);
opts->auto_compaction_factor);
reftable_free(sizes);

return 0;
}

static int update_segment_if_compaction_required(struct reftable_stack *st,
const struct reftable_write_options *opts,
struct segment *seg,
bool use_geometric,
bool *required)
@ -1638,7 +1657,7 @@ static int update_segment_if_compaction_required(struct reftable_stack *st,
return 0;
}

err = stack_segments_for_compaction(st, seg);
err = stack_segments_for_compaction(st, opts, seg);
if (err)
return err;

@ -1647,27 +1666,40 @@ static int update_segment_if_compaction_required(struct reftable_stack *st,
}

int reftable_stack_compaction_required(struct reftable_stack *st,
const struct reftable_write_options *opts,
bool use_heuristics,
bool *required)
{
struct reftable_write_options opts_default = { 0 };
struct segment seg;
return update_segment_if_compaction_required(st, &seg, use_heuristics,
required);

if (!opts)
opts = &opts_default;

return update_segment_if_compaction_required(st, opts, &seg,
use_heuristics, required);
}

int reftable_stack_auto_compact(struct reftable_stack *st)
int reftable_stack_auto_compact(struct reftable_stack *st,
const struct reftable_write_options *opts)
{
struct reftable_write_options opts_default = { 0 };
struct segment seg;
bool required;
int err;

err = update_segment_if_compaction_required(st, &seg, true, &required);
if (!opts)
opts = &opts_default;

err = update_segment_if_compaction_required(st, opts, &seg, true,
&required);
if (err)
return err;

if (required)
return stack_compact_range(st, seg.start, seg.end - 1,
NULL, STACK_COMPACT_RANGE_BEST_EFFORT);
NULL, opts,
STACK_COMPACT_RANGE_BEST_EFFORT);

return 0;
}
@ -1807,7 +1839,7 @@ static int reftable_stack_clean_locked(struct reftable_stack *st)
int reftable_stack_clean(struct reftable_stack *st)
{
struct reftable_addition *add = NULL;
int err = reftable_stack_new_addition(&add, st, 0);
int err = reftable_stack_new_addition(&add, st, NULL, 0);
if (err < 0) {
goto done;
}

View File

@ -20,7 +20,7 @@ struct reftable_stack {

char *reftable_dir;

struct reftable_write_options opts;
struct reftable_stack_options opts;

struct reftable_table **tables;
size_t tables_len;

View File

@ -80,9 +80,6 @@ static void options_set_defaults(struct reftable_write_options *opts)
opts->restart_interval = 16;
}

if (opts->hash_id == 0) {
opts->hash_id = REFTABLE_HASH_SHA1;
}
if (opts->block_size == 0) {
opts->block_size = DEFAULT_BLOCK_SIZE;
}
@ -90,7 +87,7 @@ static void options_set_defaults(struct reftable_write_options *opts)

static int writer_version(struct reftable_writer *w)
{
return (w->opts.hash_id == 0 || w->opts.hash_id == REFTABLE_HASH_SHA1) ?
return (w->hash_id == 0 || w->hash_id == REFTABLE_HASH_SHA1) ?
1 :
2;
}
@ -107,7 +104,7 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
if (writer_version(w) == 2) {
uint32_t hash_id;

switch (w->opts.hash_id) {
switch (w->hash_id) {
case REFTABLE_HASH_SHA1:
hash_id = REFTABLE_FORMAT_ID_SHA1;
break;
@ -134,7 +131,7 @@ static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
reftable_buf_reset(&w->last_key);
ret = block_writer_init(&w->block_writer_data, typ, w->block,
w->opts.block_size, block_start,
hash_size(w->opts.hash_id));
hash_size(w->hash_id));
if (ret < 0)
return ret;

@ -147,7 +144,9 @@ static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
int reftable_writer_new(struct reftable_writer **out,
ssize_t (*writer_func)(void *, const void *, size_t),
int (*flush_func)(void *),
void *writer_arg, const struct reftable_write_options *_opts)
void *writer_arg,
enum reftable_hash hash_id,
const struct reftable_write_options *_opts)
{
struct reftable_write_options opts = {0};
struct reftable_writer *wp;
@ -162,6 +161,9 @@ int reftable_writer_new(struct reftable_writer **out,
if (opts.block_size >= (1 << 24))
return REFTABLE_API_ERROR;

if (!hash_id)
hash_id = REFTABLE_HASH_SHA1;

reftable_buf_init(&wp->block_writer_data.last_key);
reftable_buf_init(&wp->last_key);
reftable_buf_init(&wp->scratch);
@ -173,6 +175,7 @@ int reftable_writer_new(struct reftable_writer **out,
wp->write = writer_func;
wp->write_arg = writer_arg;
wp->opts = opts;
wp->hash_id = hash_id;
wp->flush = flush_func;
writer_reinit_block_writer(wp, REFTABLE_BLOCK_TYPE_REF);

@ -367,7 +370,7 @@ int reftable_writer_add_ref(struct reftable_writer *w,
if (!w->opts.skip_index_objects && reftable_ref_record_val1(ref)) {
reftable_buf_reset(&w->scratch);
err = reftable_buf_add(&w->scratch, (char *)reftable_ref_record_val1(ref),
hash_size(w->opts.hash_id));
hash_size(w->hash_id));
if (err < 0)
goto out;

@ -379,7 +382,7 @@ int reftable_writer_add_ref(struct reftable_writer *w,
if (!w->opts.skip_index_objects && reftable_ref_record_val2(ref)) {
reftable_buf_reset(&w->scratch);
err = reftable_buf_add(&w->scratch, reftable_ref_record_val2(ref),
hash_size(w->opts.hash_id));
hash_size(w->hash_id));
if (err < 0)
goto out;


View File

@ -27,6 +27,7 @@ struct reftable_writer {
uint64_t next;
uint64_t min_update_index, max_update_index;
struct reftable_write_options opts;
enum reftable_hash hash_id;

/* memory buffer for writing */
uint8_t *block;

View File

@ -177,22 +177,6 @@ void repo_settings_set_big_file_threshold(struct repository *repo, unsigned long
repo->settings.big_file_threshold = value;
}

enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo)
{
const char *value;

if (!repo_config_get_string_tmp(repo, "core.logallrefupdates", &value)) {
if (value && !strcasecmp(value, "always"))
return LOG_REFS_ALWAYS;
else if (git_config_bool("core.logallrefupdates", value))
return LOG_REFS_NORMAL;
else
return LOG_REFS_NONE;
}

return LOG_REFS_UNSET;
}

int repo_settings_get_warn_ambiguous_refs(struct repository *repo)
{
prepare_repo_settings(repo);

View File

@ -16,13 +16,6 @@ enum fetch_negotiation_setting {
FETCH_NEGOTIATION_NOOP,
};

enum log_refs_config {
LOG_REFS_UNSET = -1,
LOG_REFS_NONE = 0,
LOG_REFS_NORMAL,
LOG_REFS_ALWAYS
};

struct repo_settings {
int initialized;

@ -86,8 +79,6 @@ struct repo_settings {
void prepare_repo_settings(struct repository *r);
void repo_settings_clear(struct repository *r);

/* Read the value for "core.logAllRefUpdates". */
enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo);
/* Read the value for "core.warnAmbiguousRefs". */
int repo_settings_get_warn_ambiguous_refs(struct repository *repo);
/* Read the value for "core.hooksPath". */

View File

@ -422,6 +422,11 @@ void repo_clear(struct repository *repo)
FREE_AND_NULL(repo->remote_state);
}

if (repo->refs_private) {
ref_store_release(repo->refs_private);
FREE_AND_NULL(repo->refs_private);
}

strmap_for_each_entry(&repo->submodule_ref_stores, &iter, e)
ref_store_release(e->value);
strmap_clear(&repo->submodule_ref_stores, 1);

101
setup.c
View File

@ -1798,32 +1798,6 @@ int apply_repository_format(struct repository *repo,
return 0;
}

/*
* Check the repository format version in the path found in repo_get_git_dir(repo),
* and die if it is a version we don't understand. Generally one would
* set_git_dir() before calling this, and use it only for "are we in a valid
* repo?".
*
* If successful and fmt is not NULL, fill fmt with data.
*/
static void check_and_apply_repository_format(struct repository *repo,
struct repository_format *fmt,
enum apply_repository_format_flags flags)
{
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
struct strbuf err = STRBUF_INIT;

if (!fmt)
fmt = &repo_fmt;

check_repository_format_gently(repo_get_git_dir(repo), fmt, NULL);
if (apply_repository_format(repo, fmt, flags, &err) < 0)
die("%s", err.buf);
startup_info->have_repository = 1;

clear_repository_format(&repo_fmt);
}

const char *enter_repo(struct repository *repo, const char *path, unsigned flags)
{
static struct strbuf validated_path = STRBUF_INIT;
@ -1897,9 +1871,17 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags
}

if (is_git_directory(".")) {
struct repository_format fmt = REPOSITORY_FORMAT_INIT;
struct strbuf err = STRBUF_INIT;

set_git_dir(repo, ".", 0);
check_and_apply_repository_format(repo, NULL,
APPLY_REPOSITORY_FORMAT_HONOR_ENV);
check_repository_format_gently(".", &fmt, NULL);
if (apply_repository_format(repo, &fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
die("%s", err.buf);
startup_info->have_repository = 1;

clear_repository_format(&fmt);
strbuf_release(&err);
return path;
}

@ -1934,7 +1916,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
static struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
const char *prefix = NULL;
const char *ref_backend_uri;
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;

/*
@ -2060,6 +2041,25 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)

if (startup_info->have_repository) {
struct strbuf err = STRBUF_INIT;
const char *ref_backend_uri;

/*
* The env variable should override the repository config
* for 'extensions.refStorage'.
*/
ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT);
if (ref_backend_uri) {
char *format;

free(repo_fmt.ref_storage_payload);

parse_reference_uri(ref_backend_uri, &format, &repo_fmt.ref_storage_payload);
repo_fmt.ref_storage_format = ref_storage_format_by_name(format);
if (repo_fmt.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
die(_("unknown ref storage format: '%s'"), format);

free(format);
}

if (apply_repository_format(repo, &repo_fmt,
APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
@ -2085,25 +2085,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
}

/*
* The env variable should override the repository config
* for 'extensions.refStorage'.
*/
ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT);
if (ref_backend_uri) {
char *backend, *payload;
enum ref_storage_format format;

parse_reference_uri(ref_backend_uri, &backend, &payload);
format = ref_storage_format_by_name(backend);
if (format == REF_STORAGE_FORMAT_UNKNOWN)
die(_("unknown ref storage format: '%s'"), backend);
repo_set_ref_storage_format(repo, format, payload);

free(backend);
free(payload);
}

setup_original_cwd(repo);

strbuf_release(&dir);
@ -2613,10 +2594,14 @@ static int create_default_files(struct repository *repo,
if (is_bare_repository(repo))
repo_config_set(repo, "core.bare", "true");
else {
const char *value;

repo_config_set(repo, "core.bare", "false");

/* allow template config file to override the default */
if (repo_settings_get_log_all_ref_updates(repo) == LOG_REFS_UNSET)
if (repo_config_get_string_tmp(repo, "core.logallrefupdates", &value))
repo_config_set(repo, "core.logallrefupdates", "true");

if (needs_work_tree_config(original_git_dir, work_tree))
repo_config_set(repo, "core.worktree", work_tree);
}
@ -2738,8 +2723,7 @@ out:
return ret;
}

static void repository_format_configure(struct repository *repo,
struct repository_format *repo_fmt,
static void repository_format_configure(struct repository_format *repo_fmt,
int hash, enum ref_storage_format ref_format)
{
struct default_format_config cfg = {
@ -2776,7 +2760,6 @@ static void repository_format_configure(struct repository *repo,
} else if (cfg.hash != GIT_HASH_UNKNOWN) {
repo_fmt->hash_algo = cfg.hash;
}
repo_set_hash_algo(repo, repo_fmt->hash_algo);

env = getenv("GIT_DEFAULT_REF_FORMAT");
if (repo_fmt->version >= 0 &&
@ -2814,9 +2797,6 @@ static void repository_format_configure(struct repository *repo,

free(backend);
}

repo_set_ref_storage_format(repo, repo_fmt->ref_storage_format,
repo_fmt->ref_storage_payload);
}

int init_db(struct repository *repo,
@ -2830,6 +2810,7 @@ int init_db(struct repository *repo,
int exist_ok = flags & INIT_DB_EXIST_OK;
char *original_git_dir = real_pathdup(git_dir, 1);
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
struct strbuf err = STRBUF_INIT;

if (real_git_dir) {
struct stat st;
@ -2856,10 +2837,11 @@ int init_db(struct repository *repo,
* config file, so this will not fail. What we are catching
* is an attempt to reinitialize new repository with an old tool.
*/
check_and_apply_repository_format(repo, &repo_fmt,
APPLY_REPOSITORY_FORMAT_HONOR_ENV);

repository_format_configure(repo, &repo_fmt, hash, ref_storage_format);
check_repository_format_gently(repo_get_git_dir(repo), &repo_fmt, NULL);
repository_format_configure(&repo_fmt, hash, ref_storage_format);
if (apply_repository_format(repo, &repo_fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
die("%s", err.buf);
startup_info->have_repository = 1;

/*
* Ensure `core.hidedotfiles` is processed. This must happen after we
@ -2914,6 +2896,7 @@ int init_db(struct repository *repo,
}

clear_repository_format(&repo_fmt);
strbuf_release(&err);
free(original_git_dir);
return 0;
}

View File

@ -174,7 +174,7 @@ static int dump_table(struct reftable_merged_table *mt)
static int dump_stack(const char *stackdir, uint32_t hash_id)
{
struct reftable_stack *stack = NULL;
struct reftable_write_options opts = { .hash_id = hash_id };
struct reftable_stack_options opts = { .hash_id = hash_id };
struct reftable_merged_table *merged = NULL;

int err = reftable_new_stack(&stack, stackdir, &opts);

View File

@ -519,4 +519,25 @@ test_expect_success 'symref transaction supports false symlink config' '
test_cmp expect actual
'

test_expect_success SYMLINKS,!MINGW,!WITH_BREAKING_CHANGES 'core.preferSymlinkRefs can be set up via onbranch condition' '
test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" &&
test_when_finished "rm -f .git/include" &&
git update-ref refs/heads/new @ &&
cat >.git/include <<-\EOF &&
[core]
preferSymlinkRefs = true
EOF
test_config includeIf.onbranch:"$(git branch --show-current)".path \
"$(pwd)/.git/include" &&
cat >stdin <<-EOF &&
start
symref-create TEST_SYMREF_HEAD refs/heads/new
prepare
commit
EOF
git update-ref --no-deref --stdin <stdin &&
test_path_is_symlink .git/TEST_SYMREF_HEAD &&
test "$(test_readlink .git/TEST_SYMREF_HEAD)" = refs/heads/new
'

test_done

View File

@ -278,4 +278,23 @@ test_expect_success 'object index can be disabled' '
)
'

test_expect_success 'write options can be set up via onbranch condition' '
test_config_global core.logAllRefUpdates false &&
test_when_finished "rm -rf repo" &&
init_repo &&
(
cd repo &&
test_commit A &&
test_commit B &&
cat >.git/include <<-\EOF &&
[reftable]
blockSize = 123
EOF
git config includeIf.onbranch:master.path "$(pwd)/.git/include" &&
git refs optimize &&
test-tool dump-reftable -b .git/reftable/*.ref >stats &&
test_grep "block_size: 123" stats
)
'

test_done

View File

@ -178,6 +178,18 @@ test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always'
test_must_fail git reflog exists $outside
'

test_expect_success 'core.logAllRefUpdates can be set up via onbranch condition' '
test_when_finished "git update-ref -d $outside" &&
test_when_finished "rm -f .git/include" &&
cat >.git/include <<-\EOF &&
[core]
logAllRefUpdates = always
EOF
test_config includeIf.onbranch:main.path "$(pwd)/.git/include" &&
git update-ref $outside $A &&
git reflog exists $outside
'

test_expect_success "create $m (by HEAD)" '
git update-ref HEAD $A &&
test $A = $(git show-ref -s --verify $m)

View File

@ -25,11 +25,12 @@ static int strbuf_writer_flush(void *arg UNUSED)
}

struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
enum reftable_hash hash_id,
struct reftable_write_options *opts)
{
struct reftable_writer *writer;
int ret = reftable_writer_new(&writer, &strbuf_writer_write, &strbuf_writer_flush,
buf, opts);
buf, hash_id, opts);
cl_assert(!ret);
return writer;
}
@ -39,6 +40,7 @@ void cl_reftable_write_to_buf(struct reftable_buf *buf,
size_t nrefs,
struct reftable_log_record *logs,
size_t nlogs,
enum reftable_hash hash_id,
struct reftable_write_options *_opts)
{
struct reftable_write_options opts = { 0 };
@ -66,7 +68,7 @@ void cl_reftable_write_to_buf(struct reftable_buf *buf,
min = ui;
}

writer = cl_reftable_strbuf_writer(buf, &opts);
writer = cl_reftable_strbuf_writer(buf, hash_id, &opts);
ret = reftable_writer_set_limits(writer, min, max);
cl_assert(!ret);

@ -88,7 +90,7 @@ void cl_reftable_write_to_buf(struct reftable_buf *buf,
size_t off = i * (opts.block_size ? opts.block_size
: DEFAULT_BLOCK_SIZE);
if (!off)
off = header_size(opts.hash_id == REFTABLE_HASH_SHA256 ? 2 : 1);
off = header_size(hash_id == REFTABLE_HASH_SHA256 ? 2 : 1);
cl_assert(buf->buf[off] == 'r');
}


View File

@ -10,6 +10,7 @@ struct reftable_buf;
void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);

struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
enum reftable_hash hash_id,
struct reftable_write_options *opts);

void cl_reftable_write_to_buf(struct reftable_buf *buf,
@ -17,4 +18,5 @@ void cl_reftable_write_to_buf(struct reftable_buf *buf,
size_t nrecords,
struct reftable_log_record *logs,
size_t nlogs,
enum reftable_hash hash_id,
struct reftable_write_options *opts);

View File

@ -34,7 +34,8 @@ merged_table_from_records(struct reftable_ref_record **refs,
cl_assert(*source != NULL);

for (size_t i = 0; i < n; i++) {
cl_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, &opts);
cl_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0,
REFTABLE_HASH_SHA1, &opts);
block_source_from_buf(&(*source)[i], &buf[i]);

err = reftable_table_new(&(*tables)[i], &(*source)[i],
@ -357,7 +358,8 @@ merged_table_from_log_records(struct reftable_log_record **logs,
cl_assert(*source != NULL);

for (size_t i = 0; i < n; i++) {
cl_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i], &opts);
cl_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i],
REFTABLE_HASH_SHA1, &opts);
block_source_from_buf(&(*source)[i], &buf[i]);

err = reftable_table_new(&(*tables)[i], &(*source)[i],
@ -487,7 +489,8 @@ void test_reftable_merged__default_write_opts(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1, &opts);
struct reftable_ref_record rec = {
.refname = (char *) "master",
.update_index = 1,

View File

@ -48,7 +48,6 @@ static void write_table(char ***names, struct reftable_buf *buf, int N,
{
struct reftable_write_options opts = {
.block_size = block_size,
.hash_id = hash_id,
};
struct reftable_ref_record *refs;
struct reftable_log_record *logs;
@ -78,7 +77,7 @@ static void write_table(char ***names, struct reftable_buf *buf, int N,
logs[i].value.update.message = (char *) "message";
}

cl_reftable_write_to_buf(buf, refs, N, logs, N, &opts);
cl_reftable_write_to_buf(buf, refs, N, logs, N, hash_id, &opts);

reftable_free(refs);
reftable_free(logs);
@ -103,6 +102,7 @@ void test_reftable_readwrite__log_buffer_size(void)
.message = (char *) "commit: 9\n",
} } };
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);

/* This tests buffer extension for log compression. Must use a random
@ -143,6 +143,7 @@ void test_reftable_readwrite__log_overflow(void)
},
};
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);

memset(msg, 'x', sizeof(msg) - 1);
@ -157,6 +158,7 @@ void test_reftable_readwrite__log_write_limits(void)
struct reftable_write_options opts = { 0 };
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);
struct reftable_log_record log = {
.refname = (char *)"refs/head/master",
@ -202,7 +204,9 @@ void test_reftable_readwrite__log_write_read(void)
struct reftable_table *table;
struct reftable_block_source source = { 0 };
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);
const struct reftable_stats *stats = NULL;
int N = 2, i;
char **names;
@ -299,6 +303,7 @@ void test_reftable_readwrite__log_zlib_corruption(void)
struct reftable_block_source source = { 0 };
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);
const struct reftable_stats *stats = NULL;
char message[100] = { 0 };
@ -531,6 +536,7 @@ static void t_table_refs_for(int indexed)
struct reftable_block_source source = { 0 };
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);
struct reftable_iterator it = { 0 };
int N = 50, j, i;
@ -622,7 +628,9 @@ void test_reftable_readwrite__write_empty_table(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);
struct reftable_block_source source = { 0 };
struct reftable_table *table = NULL;
struct reftable_ref_record rec = { 0 };
@ -660,7 +668,9 @@ void test_reftable_readwrite__write_object_id_min_length(void)
.block_size = 75,
};
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);
struct reftable_ref_record ref = {
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
@ -691,7 +701,9 @@ void test_reftable_readwrite__write_object_id_length(void)
.block_size = 75,
};
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);
struct reftable_ref_record ref = {
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
@ -721,7 +733,9 @@ void test_reftable_readwrite__write_empty_key(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);
struct reftable_ref_record ref = {
.refname = (char *) "",
.update_index = 1,
@ -740,7 +754,9 @@ void test_reftable_readwrite__write_key_order(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_buf buf = REFTABLE_BUF_INIT;
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
REFTABLE_HASH_SHA1,
&opts);
struct reftable_ref_record refs[2] = {
{
.refname = (char *) "b",
@ -787,7 +803,8 @@ void test_reftable_readwrite__write_multiple_indices(void)
int i;
int err;

writer = cl_reftable_strbuf_writer(&writer_buf, &opts);
writer = cl_reftable_strbuf_writer(&writer_buf, REFTABLE_HASH_SHA1,
&opts);
reftable_writer_set_limits(writer, 1, 1);
for (i = 0; i < 100; i++) {
struct reftable_ref_record ref = {
@ -861,7 +878,8 @@ void test_reftable_readwrite__write_multi_level_index(void)
struct reftable_table *table;
int err;

writer = cl_reftable_strbuf_writer(&writer_buf, &opts);
writer = cl_reftable_strbuf_writer(&writer_buf, REFTABLE_HASH_SHA1,
&opts);
reftable_writer_set_limits(writer, 1, 1);
for (size_t i = 0; i < 200; i++) {
struct reftable_ref_record ref = {

View File

@ -111,10 +111,9 @@ static int write_test_ref(struct reftable_writer *wr, void *arg)
static void write_n_ref_tables(struct reftable_stack *st,
size_t n)
{
int disable_auto_compact;

disable_auto_compact = st->opts.disable_auto_compact;
st->opts.disable_auto_compact = 1;
struct reftable_write_options opts = {
.disable_auto_compact = 1,
};

for (size_t i = 0; i < n; i++) {
struct reftable_ref_record ref = {
@ -128,10 +127,8 @@ static void write_n_ref_tables(struct reftable_stack *st,
cl_reftable_set_hash(ref.value.val1, i, REFTABLE_HASH_SHA1);

cl_assert_equal_i(reftable_stack_add(st,
&write_test_ref, &ref, 0), 0);
&write_test_ref, &ref, &opts, 0), 0);
}

st->opts.disable_auto_compact = disable_auto_compact;
}

struct write_log_arg {
@ -168,10 +165,10 @@ void test_reftable_stack__add_one(void)
struct stat stat_result = { 0 };
int err;

err = reftable_new_stack(&st, dir, &opts);
err = reftable_new_stack(&st, dir, NULL);
cl_assert(!err);

err = reftable_stack_add(st, write_test_ref, &ref, 0);
err = reftable_stack_add(st, write_test_ref, &ref, &opts, 0);
cl_assert(!err);

err = reftable_stack_read_ref(st, ref.refname, &dest);
@ -210,7 +207,6 @@ void test_reftable_stack__add_one(void)

void test_reftable_stack__uptodate(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL;
struct reftable_stack *st2 = NULL;
char *dir = get_tmp_dir(__LINE__);
@ -232,15 +228,15 @@ void test_reftable_stack__uptodate(void)
/* simulate multi-process access to the same stack
by creating two stacks for the same directory.
*/
cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0);
cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0);
cl_assert_equal_i(reftable_stack_add(st1, write_test_ref,
&ref1, 0), 0);
&ref1, NULL, 0), 0);
cl_assert_equal_i(reftable_stack_add(st2, write_test_ref,
&ref2, 0), REFTABLE_OUTDATED_ERROR);
&ref2, NULL, 0), REFTABLE_OUTDATED_ERROR);
cl_assert_equal_i(reftable_stack_reload(st2), 0);
cl_assert_equal_i(reftable_stack_add(st2, write_test_ref,
&ref2, 0), 0);
&ref2, NULL, 0), 0);
reftable_stack_destroy(st1);
reftable_stack_destroy(st2);
clear_dir(dir);
@ -249,7 +245,6 @@ void test_reftable_stack__uptodate(void)
void test_reftable_stack__transaction_api(void)
{
char *dir = get_tmp_dir(__LINE__);
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
struct reftable_addition *add = NULL;

@ -261,11 +256,11 @@ void test_reftable_stack__transaction_api(void)
};
struct reftable_ref_record dest = { 0 };

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

reftable_addition_destroy(add);

cl_assert_equal_i(reftable_stack_new_addition(&add, st, 0), 0);
cl_assert_equal_i(reftable_stack_new_addition(&add, st, NULL, 0), 0);
cl_assert_equal_i(reftable_addition_add(add, write_test_ref,
&ref), 0);
cl_assert_equal_i(reftable_addition_commit(add), 0);
@ -306,7 +301,7 @@ void test_reftable_stack__transaction_with_reload(void)

cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0);
cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0);
cl_assert_equal_i(reftable_stack_new_addition(&add, st1, 0), 0);
cl_assert_equal_i(reftable_stack_new_addition(&add, st1, NULL, 0), 0);
cl_assert_equal_i(reftable_addition_add(add, write_test_ref,
&refs[0]), 0);
cl_assert_equal_i(reftable_addition_commit(add), 0);
@ -317,9 +312,9 @@ void test_reftable_stack__transaction_with_reload(void)
* create the addition and lock the stack by default, but allow the
* reload to happen when REFTABLE_STACK_NEW_ADDITION_RELOAD is set.
*/
cl_assert_equal_i(reftable_stack_new_addition(&add, st2, 0),
cl_assert_equal_i(reftable_stack_new_addition(&add, st2, NULL, 0),
REFTABLE_OUTDATED_ERROR);
cl_assert_equal_i(reftable_stack_new_addition(&add, st2,
cl_assert_equal_i(reftable_stack_new_addition(&add, st2, NULL,
REFTABLE_STACK_NEW_ADDITION_RELOAD), 0);
cl_assert_equal_i(reftable_addition_add(add, write_test_ref,
&refs[1]), 0);
@ -342,12 +337,11 @@ void test_reftable_stack__transaction_with_reload(void)
void test_reftable_stack__transaction_api_performs_auto_compaction(void)
{
char *dir = get_tmp_dir(__LINE__);
struct reftable_write_options opts = {0};
struct reftable_addition *add = NULL;
struct reftable_stack *st = NULL;
size_t n = 20;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

for (size_t i = 0; i <= n; i++) {
struct reftable_ref_record ref = {
@ -356,6 +350,9 @@ void test_reftable_stack__transaction_api_performs_auto_compaction(void)
.value.symref = (char *) "master",
};
char name[100];
struct reftable_write_options write_opts = {
.disable_auto_compact = (i != n),
};

snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);
ref.refname = name;
@ -365,10 +362,8 @@ void test_reftable_stack__transaction_api_performs_auto_compaction(void)
* we can ensure that we indeed honor this setting and have
* better control over when exactly auto compaction runs.
*/
st->opts.disable_auto_compact = i != n;

cl_assert_equal_i(reftable_stack_new_addition(&add,
st, 0), 0);
st, &write_opts, 0), 0);
cl_assert_equal_i(reftable_addition_add(add,
write_test_ref, &ref), 0);
cl_assert_equal_i(reftable_addition_commit(add), 0);
@ -398,15 +393,14 @@ void test_reftable_stack__auto_compaction_fails_gracefully(void)
.value_type = REFTABLE_REF_VAL1,
.value.val1 = {0x01},
};
struct reftable_write_options opts = { 0 };
struct reftable_stack *st;
struct reftable_buf table_path = REFTABLE_BUF_INIT;
char *dir = get_tmp_dir(__LINE__);
int err;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
&ref, 0), 0);
&ref, NULL, 0), 0);
cl_assert_equal_i(st->merged->tables_len, 1);
cl_assert_equal_i(st->stats.attempts, 0);
cl_assert_equal_i(st->stats.failures, 0);
@ -424,7 +418,7 @@ void test_reftable_stack__auto_compaction_fails_gracefully(void)
write_file_buf(table_path.buf, "", 0);

ref.update_index = 2;
err = reftable_stack_add(st, write_test_ref, &ref, 0);
err = reftable_stack_add(st, write_test_ref, &ref, NULL, 0);
cl_assert(!err);
cl_assert_equal_i(st->merged->tables_len, 2);
cl_assert_equal_i(st->stats.attempts, 1);
@ -443,7 +437,6 @@ static int write_error(struct reftable_writer *wr UNUSED, void *arg)
void test_reftable_stack__update_index_check(void)
{
char *dir = get_tmp_dir(__LINE__);
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
struct reftable_ref_record ref1 = {
.refname = (char *) "name1",
@ -458,11 +451,11 @@ void test_reftable_stack__update_index_check(void)
.value.symref = (char *) "master",
};

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
&ref1, 0), 0);
&ref1, NULL, 0), 0);
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
&ref2, 0), REFTABLE_API_ERROR);
&ref2, NULL, 0), REFTABLE_API_ERROR);
reftable_stack_destroy(st);
clear_dir(dir);
}
@ -470,14 +463,13 @@ void test_reftable_stack__update_index_check(void)
void test_reftable_stack__lock_failure(void)
{
char *dir = get_tmp_dir(__LINE__);
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
int i;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);
for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--)
cl_assert_equal_i(reftable_stack_add(st, write_error,
&i, 0), i);
&i, NULL, 0), i);

reftable_stack_destroy(st);
clear_dir(dir);
@ -499,7 +491,7 @@ void test_reftable_stack__add(void)
size_t i, N = ARRAY_SIZE(refs);
int err = 0;

err = reftable_new_stack(&st, dir, &opts);
err = reftable_new_stack(&st, dir, NULL);
cl_assert(!err);

for (i = 0; i < N; i++) {
@ -521,7 +513,7 @@ void test_reftable_stack__add(void)

for (i = 0; i < N; i++)
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
&refs[i], 0), 0);
&refs[i], &opts, 0), 0);

for (i = 0; i < N; i++) {
struct write_log_arg arg = {
@ -529,10 +521,10 @@ void test_reftable_stack__add(void)
.update_index = reftable_stack_next_update_index(st),
};
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
&arg, 0), 0);
&arg, &opts, 0), 0);
}

cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0);
cl_assert_equal_i(reftable_stack_compact_all(st, &opts, NULL), 0);

for (i = 0; i < N; i++) {
struct reftable_ref_record dest = { 0 };
@ -584,7 +576,6 @@ void test_reftable_stack__add(void)

void test_reftable_stack__iterator(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
struct reftable_ref_record refs[10] = { 0 };
@ -593,7 +584,7 @@ void test_reftable_stack__iterator(void)
size_t N = ARRAY_SIZE(refs), i;
int err;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

for (i = 0; i < N; i++) {
refs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
@ -613,7 +604,7 @@ void test_reftable_stack__iterator(void)

for (i = 0; i < N; i++)
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
&refs[i], 0), 0);
&refs[i], NULL, 0), 0);

for (i = 0; i < N; i++) {
struct write_log_arg arg = {
@ -622,7 +613,7 @@ void test_reftable_stack__iterator(void)
};

cl_assert_equal_i(reftable_stack_add(st, write_test_log,
&arg, 0), 0);
&arg, NULL, 0), 0);
}

reftable_stack_init_ref_iterator(st, &it);
@ -669,9 +660,6 @@ void test_reftable_stack__iterator(void)

void test_reftable_stack__log_normalize(void)
{
struct reftable_write_options opts = {
0,
};
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
struct reftable_log_record input = {
@ -693,15 +681,15 @@ void test_reftable_stack__log_normalize(void)
.update_index = 1,
};

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

input.value.update.message = (char *) "one\ntwo";
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
&arg, 0), REFTABLE_API_ERROR);
&arg, NULL, 0), REFTABLE_API_ERROR);

input.value.update.message = (char *) "one";
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
&arg, 0), 0);
&arg, NULL, 0), 0);
cl_assert_equal_i(reftable_stack_read_log(st, input.refname,
&dest), 0);
cl_assert_equal_s(dest.value.update.message, "one\n");
@ -709,7 +697,7 @@ void test_reftable_stack__log_normalize(void)
input.value.update.message = (char *) "two\n";
arg.update_index = 2;
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
&arg, 0), 0);
&arg, NULL, 0), 0);
cl_assert_equal_i(reftable_stack_read_log(st, input.refname,
&dest), 0);
cl_assert_equal_s(dest.value.update.message, "two\n");
@ -723,7 +711,6 @@ void test_reftable_stack__log_normalize(void)
void test_reftable_stack__tombstone(void)
{
char *dir = get_tmp_dir(__LINE__);
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
struct reftable_ref_record refs[2] = { 0 };
struct reftable_log_record logs[2] = { 0 };
@ -731,7 +718,7 @@ void test_reftable_stack__tombstone(void)
struct reftable_ref_record dest = { 0 };
struct reftable_log_record log_dest = { 0 };

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

/* even entries add the refs, odd entries delete them. */
for (i = 0; i < N; i++) {
@ -760,7 +747,7 @@ void test_reftable_stack__tombstone(void)
}
for (i = 0; i < N; i++)
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
&refs[i], 0), 0);
&refs[i], NULL, 0), 0);

for (i = 0; i < N; i++) {
struct write_log_arg arg = {
@ -768,7 +755,7 @@ void test_reftable_stack__tombstone(void)
.update_index = reftable_stack_next_update_index(st),
};
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
&arg, 0), 0);
&arg, NULL, 0), 0);
}

cl_assert_equal_i(reftable_stack_read_ref(st, "branch",
@ -779,7 +766,7 @@ void test_reftable_stack__tombstone(void)
&log_dest), 1);
reftable_log_record_release(&log_dest);

cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0);
cl_assert_equal_i(reftable_stack_compact_all(st, NULL, NULL), 0);
cl_assert_equal_i(reftable_stack_read_ref(st, "branch",
&dest), 1);
cl_assert_equal_i(reftable_stack_read_log(st, "branch",
@ -799,7 +786,6 @@ void test_reftable_stack__tombstone(void)
void test_reftable_stack__hash_id(void)
{
char *dir = get_tmp_dir(__LINE__);
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;

struct reftable_ref_record ref = {
@ -808,15 +794,14 @@ void test_reftable_stack__hash_id(void)
.value.symref = (char *) "target",
.update_index = 1,
};
struct reftable_write_options opts32 = { .hash_id = REFTABLE_HASH_SHA256 };
struct reftable_stack_options opts32 = { .hash_id = REFTABLE_HASH_SHA256 };
struct reftable_stack *st32 = NULL;
struct reftable_write_options opts_default = { 0 };
struct reftable_stack *st_default = NULL;
struct reftable_ref_record dest = { 0 };

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);
cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
&ref, 0), 0);
&ref, NULL, 0), 0);

/* can't read it with the wrong hash ID. */
cl_assert_equal_i(reftable_new_stack(&st32, dir,
@ -824,7 +809,7 @@ void test_reftable_stack__hash_id(void)

/* check that we can read it back with default opts too. */
cl_assert_equal_i(reftable_new_stack(&st_default, dir,
&opts_default), 0);
NULL), 0);
cl_assert_equal_i(reftable_stack_read_ref(st_default, "master",
&dest), 0);
cl_assert(reftable_ref_record_equal(&ref, &dest,
@ -855,7 +840,6 @@ void test_reftable_stack__suggest_compaction_segment_nothing(void)
void test_reftable_stack__reflog_expire(void)
{
char *dir = get_tmp_dir(__LINE__);
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
struct reftable_log_record logs[20] = { 0 };
size_t i, N = ARRAY_SIZE(logs) - 1;
@ -864,7 +848,7 @@ void test_reftable_stack__reflog_expire(void)
};
struct reftable_log_record log = { 0 };

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

for (i = 1; i <= N; i++) {
char buf[256];
@ -885,18 +869,18 @@ void test_reftable_stack__reflog_expire(void)
.update_index = reftable_stack_next_update_index(st),
};
cl_assert_equal_i(reftable_stack_add(st, write_test_log,
&arg, 0), 0);
&arg, NULL, 0), 0);
}

cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0);
cl_assert_equal_i(reftable_stack_compact_all(st, &expiry), 0);
cl_assert_equal_i(reftable_stack_compact_all(st, NULL, NULL), 0);
cl_assert_equal_i(reftable_stack_compact_all(st, NULL, &expiry), 0);
cl_assert_equal_i(reftable_stack_read_log(st, logs[9].refname,
&log), 1);
cl_assert_equal_i(reftable_stack_read_log(st, logs[11].refname,
&log), 0);

expiry.min_update_index = 15;
cl_assert_equal_i(reftable_stack_compact_all(st, &expiry), 0);
cl_assert_equal_i(reftable_stack_compact_all(st, NULL, &expiry), 0);
cl_assert_equal_i(reftable_stack_read_log(st, logs[14].refname,
&log), 1);
cl_assert_equal_i(reftable_stack_read_log(st, logs[16].refname,
@ -918,15 +902,14 @@ static int write_nothing(struct reftable_writer *wr, void *arg UNUSED)

void test_reftable_stack__empty_add(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
struct reftable_stack *st2 = NULL;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);
cl_assert_equal_i(reftable_stack_add(st, write_nothing,
NULL, 0), 0);
cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0);
NULL, NULL, 0), 0);
cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0);
clear_dir(dir);
reftable_stack_destroy(st);
reftable_stack_destroy(st2);
@ -952,7 +935,7 @@ void test_reftable_stack__auto_compaction(void)
size_t i, N = 100;
int err;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

for (i = 0; i < N; i++) {
char name[100];
@ -964,10 +947,10 @@ void test_reftable_stack__auto_compaction(void)
};
snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);

err = reftable_stack_add(st, write_test_ref, &ref, 0);
err = reftable_stack_add(st, write_test_ref, &ref, &opts, 0);
cl_assert(!err);

err = reftable_stack_auto_compact(st);
err = reftable_stack_auto_compact(st, &opts);
cl_assert(!err);
cl_assert(i < 2 || st->merged->tables_len < 2 * fastlogN(i, 2));
}
@ -989,7 +972,7 @@ void test_reftable_stack__auto_compaction_factor(void)
size_t N = 100;
int err;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

for (size_t i = 0; i < N; i++) {
char name[20];
@ -1000,7 +983,7 @@ void test_reftable_stack__auto_compaction_factor(void)
};
xsnprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);

err = reftable_stack_add(st, &write_test_ref, &ref, 0);
err = reftable_stack_add(st, &write_test_ref, &ref, &opts, 0);
cl_assert(!err);

cl_assert(i < 5 || st->merged->tables_len < 5 * fastlogN(i, 5));
@ -1020,7 +1003,7 @@ void test_reftable_stack__auto_compaction_with_locked_tables(void)
char *dir = get_tmp_dir(__LINE__);
int err;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

write_n_ref_tables(st, 5);
cl_assert_equal_i(st->merged->tables_len, 5);
@ -1042,7 +1025,7 @@ void test_reftable_stack__auto_compaction_with_locked_tables(void)
* would in theory compact all tables, due to the preexisting lock we
* only compact the newest two tables.
*/
err = reftable_stack_auto_compact(st);
err = reftable_stack_auto_compact(st, &opts);
cl_assert(!err);
cl_assert_equal_i(st->stats.failures, 0);
cl_assert_equal_i(st->merged->tables_len, 4);
@ -1054,12 +1037,11 @@ void test_reftable_stack__auto_compaction_with_locked_tables(void)

void test_reftable_stack__add_performs_auto_compaction(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
size_t i, n = 20;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

for (i = 0; i <= n; i++) {
struct reftable_ref_record ref = {
@ -1067,6 +1049,9 @@ void test_reftable_stack__add_performs_auto_compaction(void)
.value_type = REFTABLE_REF_SYMREF,
.value.symref = (char *) "master",
};
struct reftable_write_options write_opts = {
.disable_auto_compact = (i != n),
};
bool required = false;
char buf[128];

@ -1075,20 +1060,18 @@ void test_reftable_stack__add_performs_auto_compaction(void)
* we can ensure that we indeed honor this setting and have
* better control over when exactly auto compaction runs.
*/
st->opts.disable_auto_compact = i != n;

snprintf(buf, sizeof(buf), "branch-%04"PRIuMAX, (uintmax_t)i);
ref.refname = buf;

cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
&ref, 0), 0);
&ref, &write_opts, 0), 0);

/*
* The stack length should grow continuously for all runs where
* auto compaction is disabled. When enabled, we should merge
* all tables in the stack.
*/
cl_assert_equal_i(reftable_stack_compaction_required(st, true, &required), 0);
cl_assert_equal_i(reftable_stack_compaction_required(st, NULL, true, &required), 0);
if (i != n) {
cl_assert_equal_i(st->merged->tables_len, i + 1);
if (i < 1)
@ -1115,7 +1098,7 @@ void test_reftable_stack__compaction_with_locked_tables(void)
char *dir = get_tmp_dir(__LINE__);
int err;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

write_n_ref_tables(st, 3);
cl_assert_equal_i(st->merged->tables_len, 3);
@ -1131,7 +1114,7 @@ void test_reftable_stack__compaction_with_locked_tables(void)
* Compaction is expected to fail given that we were not able to
* compact all tables.
*/
err = reftable_stack_compact_all(st, NULL);
err = reftable_stack_compact_all(st, &opts, NULL);
cl_assert_equal_i(err, REFTABLE_LOCK_ERROR);
cl_assert_equal_i(st->stats.failures, 1);
cl_assert_equal_i(st->merged->tables_len, 3);
@ -1143,15 +1126,14 @@ void test_reftable_stack__compaction_with_locked_tables(void)

void test_reftable_stack__compaction_concurrent(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL, *st2 = NULL;
char *dir = get_tmp_dir(__LINE__);

cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0);
write_n_ref_tables(st1, 3);

cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0);
cl_assert_equal_i(reftable_stack_compact_all(st1, NULL), 0);
cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0);
cl_assert_equal_i(reftable_stack_compact_all(st1, NULL, NULL), 0);

reftable_stack_destroy(st1);
reftable_stack_destroy(st2);
@ -1171,20 +1153,19 @@ static void unclean_stack_close(struct reftable_stack *st)

void test_reftable_stack__compaction_concurrent_clean(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
char *dir = get_tmp_dir(__LINE__);

cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0);
write_n_ref_tables(st1, 3);

cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0);
cl_assert_equal_i(reftable_stack_compact_all(st1, NULL), 0);
cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0);
cl_assert_equal_i(reftable_stack_compact_all(st1, NULL, NULL), 0);

unclean_stack_close(st1);
unclean_stack_close(st2);

cl_assert_equal_i(reftable_new_stack(&st3, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st3, dir, NULL), 0);
cl_assert_equal_i(reftable_stack_clean(st3), 0);
cl_assert_equal_i(count_dir_entries(dir), 2);

@ -1197,7 +1178,6 @@ void test_reftable_stack__compaction_concurrent_clean(void)

void test_reftable_stack__read_across_reload(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_stack *st1 = NULL, *st2 = NULL;
struct reftable_ref_record rec = { 0 };
struct reftable_iterator it = { 0 };
@ -1205,17 +1185,17 @@ void test_reftable_stack__read_across_reload(void)
int err;

/* Create a first stack and set up an iterator for it. */
cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0);
write_n_ref_tables(st1, 2);
cl_assert_equal_i(st1->merged->tables_len, 2);
reftable_stack_init_ref_iterator(st1, &it);
cl_assert_equal_i(reftable_iterator_seek_ref(&it, ""), 0);

/* Set up a second stack for the same directory and compact it. */
err = reftable_new_stack(&st2, dir, &opts);
err = reftable_new_stack(&st2, dir, NULL);
cl_assert(!err);
cl_assert_equal_i(st2->merged->tables_len, 2);
err = reftable_stack_compact_all(st2, NULL);
err = reftable_stack_compact_all(st2, NULL, NULL);
cl_assert(!err);
cl_assert_equal_i(st2->merged->tables_len, 1);

@ -1244,7 +1224,6 @@ void test_reftable_stack__read_across_reload(void)

void test_reftable_stack__reload_with_missing_table(void)
{
struct reftable_write_options opts = { 0 };
struct reftable_stack *st = NULL;
struct reftable_ref_record rec = { 0 };
struct reftable_iterator it = { 0 };
@ -1253,7 +1232,7 @@ void test_reftable_stack__reload_with_missing_table(void)
int err;

/* Create a first stack and set up an iterator for it. */
cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);
write_n_ref_tables(st, 2);
cl_assert_equal_i(st->merged->tables_len, 2);
reftable_stack_init_ref_iterator(st, &it);
@ -1320,11 +1299,11 @@ void test_reftable_stack__invalid_limit_updates(void)
char *dir = get_tmp_dir(__LINE__);
struct reftable_stack *st = NULL;

cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0);
cl_assert_equal_i(reftable_new_stack(&st, dir, NULL), 0);

reftable_addition_destroy(add);

cl_assert_equal_i(reftable_stack_new_addition(&add, st, 0), 0);
cl_assert_equal_i(reftable_stack_new_addition(&add, st, &opts, 0), 0);

/*
* write_limits_after_ref also updates the update indexes after adding

View File

@ -22,7 +22,8 @@ void test_reftable_table__seek_once(void)
struct reftable_buf buf = REFTABLE_BUF_INIT;
int ret;

cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL);
cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0,
REFTABLE_HASH_SHA1, NULL);
block_source_from_buf(&source, &buf);

ret = reftable_table_new(&table, &source, "name");
@ -64,7 +65,7 @@ void test_reftable_table__reseek(void)
int ret;

cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records),
NULL, 0, NULL);
NULL, 0, REFTABLE_HASH_SHA1, NULL);
block_source_from_buf(&source, &buf);

ret = reftable_table_new(&table, &source, "name");
@ -147,7 +148,8 @@ void test_reftable_table__block_iterator(void)
(uintmax_t) i);
}

cl_reftable_write_to_buf(&buf, records, nrecords, NULL, 0, NULL);
cl_reftable_write_to_buf(&buf, records, nrecords, NULL, 0,
REFTABLE_HASH_SHA1, NULL);
block_source_from_buf(&source, &buf);

ret = reftable_table_new(&table, &source, "name");