Merge branch 'kn/refs-generic-helpers' into next

Refactor service routines in the ref subsystem backends.

* kn/refs-generic-helpers:
  refs: use peeled tag values in reference backends
  refs: add peeled object ID to the `ref_update` struct
  refs: move object parsing to the generic layer
  update-ref: handle rejections while adding updates
  update-ref: move `print_rejected_refs()` up
  refs: return `ref_transaction_error` from `ref_transaction_update()`
  refs: extract out reflog config to generic layer
  refs: introduce `ref_store_init_options`
  refs: remove unused typedef 'ref_transaction_commit_fn'
This commit is contained in:
Junio C Hamano
2026-05-15 10:46:52 +09:00
10 changed files with 266 additions and 174 deletions

View File

@@ -1642,8 +1642,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
ret = NULL; /* good */
}
strbuf_release(&err);
}
else {
} else {
enum ref_transaction_error tx_err;
struct strbuf err = STRBUF_INIT;
if (shallow_update && si->shallow_ref[cmd->index] &&
update_shallow_ref(cmd, si)) {
@@ -1651,14 +1651,18 @@ static const char *update(struct command *cmd, struct shallow_info *si)
goto out;
}
if (ref_transaction_update(transaction,
namespaced_name,
new_oid, old_oid,
NULL, NULL,
0, "push",
&err)) {
tx_err = ref_transaction_update(transaction,
namespaced_name,
new_oid, old_oid,
NULL, NULL,
0, "push",
&err);
if (tx_err) {
rp_error("%s", err.buf);
ret = "failed to update ref";
if (tx_err == REF_TRANSACTION_ERROR_GENERIC)
ret = "failed to update ref";
else
ret = ref_transaction_error_msg(tx_err);
} else {
ret = NULL; /* good */
}

View File

@@ -25,6 +25,15 @@ static unsigned int default_flags;
static unsigned create_reflog_flag;
static const char *msg;
struct command_options {
/*
* Individual updates are allowed to fail without causing
* update-ref to exit. This is set when using the
* '--batch-updates' flag.
*/
bool allow_update_failures;
};
/*
* Parse one whitespace- or NUL-terminated, possibly C-quoted argument
* and append the result to arg. Return a pointer to the terminator.
@@ -234,6 +243,53 @@ static int parse_next_oid(const char **next, const char *end,
command, refname);
}
static void print_rejected_refs(const char *refname,
const struct object_id *old_oid,
const struct object_id *new_oid,
const char *old_target,
const char *new_target,
enum ref_transaction_error err,
const char *details,
void *cb_data UNUSED)
{
struct strbuf sb = STRBUF_INIT;
if (details && *details)
error("%s", details);
strbuf_addf(&sb, "rejected %s %s %s %s\n", refname,
new_oid ? oid_to_hex(new_oid) : new_target,
old_oid ? oid_to_hex(old_oid) : old_target,
ref_transaction_error_msg(err));
fwrite(sb.buf, sb.len, 1, stdout);
strbuf_release(&sb);
}
/*
* Handle transaction errors. If we're using batches updates, we want to only
* die for generic errors and print the remaining to the user.
*/
static void handle_ref_transaction_error(const char *refname,
struct object_id *new_oid,
struct object_id *old_oid,
const char *new_target,
const char *old_target,
enum ref_transaction_error tx_err,
struct strbuf *err,
struct command_options *opts)
{
if (!tx_err)
return;
if (tx_err != REF_TRANSACTION_ERROR_GENERIC && opts->allow_update_failures) {
print_rejected_refs(refname, old_oid, new_oid, old_target,
new_target, tx_err, err->buf, NULL);
return;
}
die("%s", err->buf);
}
/*
* The following five parse_cmd_*() functions parse the corresponding
@@ -246,11 +302,13 @@ static int parse_next_oid(const char **next, const char *end,
*/
static void parse_cmd_update(struct ref_transaction *transaction,
const char *next, const char *end)
const char *next, const char *end,
struct command_options *opts)
{
struct strbuf err = STRBUF_INIT;
char *refname;
struct object_id new_oid, old_oid;
enum ref_transaction_error tx_err;
int have_old;
refname = parse_refname(&next);
@@ -267,12 +325,14 @@ static void parse_cmd_update(struct ref_transaction *transaction,
if (*next != line_termination)
die("update %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname,
&new_oid, have_old ? &old_oid : NULL,
NULL, NULL,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
tx_err = ref_transaction_update(transaction, refname,
&new_oid, have_old ? &old_oid : NULL,
NULL, NULL,
update_flags | create_reflog_flag,
msg, &err);
handle_ref_transaction_error(refname, &new_oid, have_old ? &old_oid : NULL,
NULL, NULL, tx_err, &err, opts);
update_flags = default_flags;
free(refname);
@@ -280,9 +340,11 @@ static void parse_cmd_update(struct ref_transaction *transaction,
}
static void parse_cmd_symref_update(struct ref_transaction *transaction,
const char *next, const char *end UNUSED)
const char *next, const char *end UNUSED,
struct command_options *opts)
{
char *refname, *new_target, *old_arg;
enum ref_transaction_error tx_err;
char *old_target = NULL;
struct strbuf err = STRBUF_INIT;
struct object_id old_oid;
@@ -319,13 +381,15 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
if (*next != line_termination)
die("symref-update %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname, NULL,
have_old_oid ? &old_oid : NULL,
new_target,
have_old_oid ? NULL : old_target,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
tx_err = ref_transaction_update(transaction, refname, NULL,
have_old_oid ? &old_oid : NULL,
new_target,
have_old_oid ? NULL : old_target,
update_flags | create_reflog_flag,
msg, &err);
handle_ref_transaction_error(refname, NULL, have_old_oid ? &old_oid : NULL,
new_target, have_old_oid ? NULL : old_target,
tx_err, &err, opts);
update_flags = default_flags;
free(refname);
@@ -336,11 +400,13 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
}
static void parse_cmd_create(struct ref_transaction *transaction,
const char *next, const char *end)
const char *next, const char *end,
struct command_options *opts)
{
struct strbuf err = STRBUF_INIT;
char *refname;
struct object_id new_oid;
enum ref_transaction_error tx_err;
refname = parse_refname(&next);
if (!refname)
@@ -355,22 +421,24 @@ static void parse_cmd_create(struct ref_transaction *transaction,
if (*next != line_termination)
die("create %s: extra input: %s", refname, next);
if (ref_transaction_create(transaction, refname, &new_oid, NULL,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
tx_err = ref_transaction_create(transaction, refname, &new_oid, NULL,
update_flags | create_reflog_flag,
msg, &err);
handle_ref_transaction_error(refname, &new_oid, NULL, NULL, NULL, tx_err,
&err, opts);
update_flags = default_flags;
free(refname);
strbuf_release(&err);
}
static void parse_cmd_symref_create(struct ref_transaction *transaction,
const char *next, const char *end UNUSED)
const char *next, const char *end UNUSED,
struct command_options *opts)
{
struct strbuf err = STRBUF_INIT;
char *refname, *new_target;
enum ref_transaction_error tx_err;
refname = parse_refname(&next);
if (!refname)
@@ -383,10 +451,11 @@ static void parse_cmd_symref_create(struct ref_transaction *transaction,
if (*next != line_termination)
die("symref-create %s: extra input: %s", refname, next);
if (ref_transaction_create(transaction, refname, NULL, new_target,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
tx_err = ref_transaction_create(transaction, refname, NULL, new_target,
update_flags | create_reflog_flag,
msg, &err);
handle_ref_transaction_error(refname, NULL, NULL, new_target, NULL,
tx_err, &err, opts);
update_flags = default_flags;
free(refname);
@@ -395,7 +464,8 @@ static void parse_cmd_symref_create(struct ref_transaction *transaction,
}
static void parse_cmd_delete(struct ref_transaction *transaction,
const char *next, const char *end)
const char *next, const char *end,
struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname;
@@ -428,9 +498,9 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
strbuf_release(&err);
}
static void parse_cmd_symref_delete(struct ref_transaction *transaction,
const char *next, const char *end UNUSED)
const char *next, const char *end UNUSED,
struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname, *old_target;
@@ -457,9 +527,9 @@ static void parse_cmd_symref_delete(struct ref_transaction *transaction,
strbuf_release(&err);
}
static void parse_cmd_verify(struct ref_transaction *transaction,
const char *next, const char *end)
const char *next, const char *end,
struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname;
@@ -486,7 +556,8 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
}
static void parse_cmd_symref_verify(struct ref_transaction *transaction,
const char *next, const char *end UNUSED)
const char *next, const char *end UNUSED,
struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
struct object_id old_oid;
@@ -528,7 +599,8 @@ static void report_ok(const char *command)
}
static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
const char *next, const char *end UNUSED)
const char *next, const char *end UNUSED,
struct command_options *opts UNUSED)
{
const char *rest;
if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
@@ -538,7 +610,8 @@ static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
}
static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
const char *next, const char *end UNUSED)
const char *next, const char *end UNUSED,
struct command_options *opts UNUSED)
{
if (*next != line_termination)
die("start: extra input: %s", next);
@@ -546,7 +619,8 @@ static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
}
static void parse_cmd_prepare(struct ref_transaction *transaction,
const char *next, const char *end UNUSED)
const char *next, const char *end UNUSED,
struct command_options *opts UNUSED)
{
struct strbuf error = STRBUF_INIT;
if (*next != line_termination)
@@ -557,7 +631,8 @@ static void parse_cmd_prepare(struct ref_transaction *transaction,
}
static void parse_cmd_abort(struct ref_transaction *transaction,
const char *next, const char *end UNUSED)
const char *next, const char *end UNUSED,
struct command_options *opts UNUSED)
{
struct strbuf error = STRBUF_INIT;
if (*next != line_termination)
@@ -567,31 +642,9 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
report_ok("abort");
}
static void print_rejected_refs(const char *refname,
const struct object_id *old_oid,
const struct object_id *new_oid,
const char *old_target,
const char *new_target,
enum ref_transaction_error err,
const char *details,
void *cb_data UNUSED)
{
struct strbuf sb = STRBUF_INIT;
if (details && *details)
error("%s", details);
strbuf_addf(&sb, "rejected %s %s %s %s\n", refname,
new_oid ? oid_to_hex(new_oid) : new_target,
old_oid ? oid_to_hex(old_oid) : old_target,
ref_transaction_error_msg(err));
fwrite(sb.buf, sb.len, 1, stdout);
strbuf_release(&sb);
}
static void parse_cmd_commit(struct ref_transaction *transaction,
const char *next, const char *end UNUSED)
const char *next, const char *end UNUSED,
struct command_options *opts UNUSED)
{
struct strbuf error = STRBUF_INIT;
if (*next != line_termination)
@@ -619,7 +672,8 @@ enum update_refs_state {
static const struct parse_cmd {
const char *prefix;
void (*fn)(struct ref_transaction *, const char *, const char *);
void (*fn)(struct ref_transaction *, const char *, const char *,
struct command_options *);
unsigned args;
enum update_refs_state state;
} command[] = {
@@ -645,6 +699,10 @@ static void update_refs_stdin(unsigned int flags)
struct ref_transaction *transaction;
int i, j;
struct command_options opts = {
.allow_update_failures = flags & REF_TRANSACTION_ALLOW_FAILURE,
};
transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
flags, &err);
if (!transaction)
@@ -722,7 +780,7 @@ static void update_refs_stdin(unsigned int flags)
}
cmd->fn(transaction, input.buf + strlen(cmd->prefix) + !!cmd->args,
input.buf + input.len);
input.buf + input.len, &opts);
}
switch (state) {

60
refs.c
View File

@@ -1307,6 +1307,7 @@ struct ref_update *ref_transaction_add_update(
const char *refname, unsigned int flags,
const struct object_id *new_oid,
const struct object_id *old_oid,
const struct object_id *peeled,
const char *new_target, const char *old_target,
const char *committer_info,
const char *msg)
@@ -1339,6 +1340,8 @@ struct ref_update *ref_transaction_add_update(
update->committer_info = xstrdup_or_null(committer_info);
update->msg = normalize_reflog_message(msg);
}
if (flags & REF_HAVE_PEELED)
oidcpy(&update->peeled, peeled);
/*
* This list is generally used by the backends to avoid duplicates.
@@ -1383,25 +1386,27 @@ static int transaction_refname_valid(const char *refname,
return 1;
}
int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
const struct object_id *old_oid,
const char *new_target,
const char *old_target,
unsigned int flags, const char *msg,
struct strbuf *err)
enum ref_transaction_error ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
const struct object_id *old_oid,
const char *new_target,
const char *old_target,
unsigned int flags, const char *msg,
struct strbuf *err)
{
struct object_id peeled;
assert(err);
if ((flags & REF_FORCE_CREATE_REFLOG) &&
(flags & REF_SKIP_CREATE_REFLOG)) {
strbuf_addstr(err, _("refusing to force and skip creation of reflog"));
return -1;
return REF_TRANSACTION_ERROR_GENERIC;
}
if (!transaction_refname_valid(refname, new_oid, flags, err))
return -1;
return REF_TRANSACTION_ERROR_GENERIC;
if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
@@ -1416,8 +1421,32 @@ int ref_transaction_update(struct ref_transaction *transaction,
flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
flags |= (new_target ? REF_HAVE_NEW : 0) | (old_target ? REF_HAVE_OLD : 0);
if ((flags & REF_HAVE_NEW) && !new_target && !is_null_oid(new_oid) &&
!(flags & REF_SKIP_OID_VERIFICATION) && !(flags & REF_LOG_ONLY)) {
struct object *o = parse_object(transaction->ref_store->repo, new_oid);
if (!o) {
strbuf_addf(err,
_("trying to write ref '%s' with nonexistent object %s"),
refname, oid_to_hex(new_oid));
return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
}
if (o->type != OBJ_COMMIT && is_branch(refname)) {
strbuf_addf(err, _("trying to write non-commit object %s to branch '%s'"),
oid_to_hex(new_oid), refname);
return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
}
if (o->type == OBJ_TAG) {
if (!peel_object(transaction->ref_store->repo, new_oid, &peeled,
PEEL_OBJECT_VERIFY_TAGGED_OBJECT_TYPE))
flags |= REF_HAVE_PEELED;
}
}
ref_transaction_add_update(transaction, refname, flags,
new_oid, old_oid, new_target,
new_oid, old_oid, &peeled, new_target,
old_target, NULL, msg);
return 0;
@@ -1444,7 +1473,7 @@ int ref_transaction_update_reflog(struct ref_transaction *transaction,
return -1;
update = ref_transaction_add_update(transaction, refname, flags,
new_oid, old_oid, NULL, NULL,
new_oid, old_oid, NULL, NULL, NULL,
committer_info, msg);
update->index = index;
@@ -2295,6 +2324,10 @@ static struct ref_store *ref_store_init(struct repository *repo,
{
const struct ref_storage_be *be;
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);
if (!be)
@@ -2304,7 +2337,8 @@ static struct ref_store *ref_store_init(struct repository *repo,
* TODO Send in a 'struct worktree' instead of a 'gitdir', and
* allow the backend to handle how it wants to deal with worktrees.
*/
refs = be->init(repo, repo->ref_storage_payload, gitdir, flags);
refs = be->init(repo, repo->ref_storage_payload, gitdir, &opts);
return refs;
}

16
refs.h
View File

@@ -905,14 +905,14 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
* See the above comment "Reference transaction updates" for more
* information.
*/
int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
const struct object_id *old_oid,
const char *new_target,
const char *old_target,
unsigned int flags, const char *msg,
struct strbuf *err);
enum ref_transaction_error ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
const struct object_id *old_oid,
const char *new_target,
const char *old_target,
unsigned int flags, const char *msg,
struct strbuf *err);
/*
* Similar to `ref_transaction_update`, but this function is only for adding

View File

@@ -19,7 +19,6 @@
#include "../iterator.h"
#include "../dir-iterator.h"
#include "../lockfile.h"
#include "../object.h"
#include "../path.h"
#include "../dir.h"
#include "../chdir-notify.h"
@@ -108,7 +107,7 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
static struct ref_store *files_ref_store_init(struct repository *repo,
const char *payload,
const char *gitdir,
unsigned int flags)
const struct ref_store_init_options *opts)
{
struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
@@ -120,11 +119,13 @@ static struct ref_store *files_ref_store_init(struct repository *repo,
&ref_common_dir);
base_ref_store_init(ref_store, repo, refdir.buf, &refs_be_files);
refs->store_flags = flags;
refs->gitcommondir = strbuf_detach(&ref_common_dir, NULL);
refs->packed_ref_store =
packed_ref_store_init(repo, NULL, refs->gitcommondir, flags);
refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo);
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);
@@ -1331,7 +1332,8 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
ref_transaction_add_update(
transaction, r->name,
REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
null_oid(the_hash_algo), &r->oid, NULL, NULL, NULL, NULL);
null_oid(the_hash_algo), &r->oid, NULL, NULL, NULL,
NULL, NULL);
if (ref_transaction_commit(transaction, &err))
goto cleanup;
@@ -1594,7 +1596,6 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
static enum ref_transaction_error write_ref_to_lockfile(struct files_ref_store *refs,
struct ref_lock *lock,
const struct object_id *oid,
int skip_oid_verification,
struct strbuf *err);
static int commit_ref_update(struct files_ref_store *refs,
struct ref_lock *lock,
@@ -1742,7 +1743,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
}
oidcpy(&lock->old_oid, &orig_oid);
if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) ||
if (write_ref_to_lockfile(refs, lock, &orig_oid, &err) ||
commit_ref_update(refs, lock, &orig_oid, logmsg, 0, &err)) {
error("unable to write current sha1 into %s: %s", newrefname, err.buf);
strbuf_release(&err);
@@ -1760,7 +1761,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
goto rollbacklog;
}
if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) ||
if (write_ref_to_lockfile(refs, lock, &orig_oid, &err) ||
commit_ref_update(refs, lock, &orig_oid, NULL, REF_SKIP_CREATE_REFLOG, &err)) {
error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
strbuf_release(&err);
@@ -2004,32 +2005,11 @@ static int files_log_ref_write(struct files_ref_store *refs,
static enum ref_transaction_error write_ref_to_lockfile(struct files_ref_store *refs,
struct ref_lock *lock,
const struct object_id *oid,
int skip_oid_verification,
struct strbuf *err)
{
static char term = '\n';
struct object *o;
int fd;
if (!skip_oid_verification) {
o = parse_object(refs->base.repo, oid);
if (!o) {
strbuf_addf(
err,
"trying to write ref '%s' with nonexistent object %s",
lock->ref_name, oid_to_hex(oid));
unlock_ref(lock);
return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
}
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
strbuf_addf(
err,
"trying to write non-commit object %s to branch '%s'",
oid_to_hex(oid), lock->ref_name);
unlock_ref(lock);
return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
}
}
fd = get_lock_file_fd(&lock->lk);
if (write_in_full(fd, oid_to_hex(oid), refs->base.repo->hash_algo->hexsz) < 0 ||
write_in_full(fd, &term, 1) < 0 ||
@@ -2496,7 +2476,7 @@ 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 | REF_LOG_VIA_SPLIT,
&update->new_oid, &update->old_oid,
&update->new_oid, &update->old_oid, &update->peeled,
NULL, NULL, update->committer_info, update->msg);
new_update->parent_update = update;
@@ -2558,8 +2538,8 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update,
transaction, referent, new_flags,
update->new_target ? NULL : &update->new_oid,
update->old_target ? NULL : &update->old_oid,
update->new_target, update->old_target, NULL,
update->msg);
&update->peeled, update->new_target, update->old_target,
NULL, update->msg);
new_update->parent_update = update;
@@ -2833,7 +2813,6 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
} else {
ret = write_ref_to_lockfile(
refs, lock, &update->new_oid,
update->flags & REF_SKIP_OID_VERIFICATION,
err);
if (ret) {
char *write_err = strbuf_detach(err, NULL);
@@ -3023,7 +3002,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
ref_transaction_add_update(
packed_transaction, update->refname,
REF_HAVE_NEW | REF_NO_DEREF,
&update->new_oid, NULL,
&update->new_oid, NULL, NULL,
NULL, NULL, NULL, NULL);
}
}
@@ -3229,19 +3208,22 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
if (update->flags & REF_LOG_ONLY)
ref_transaction_add_update(loose_transaction, update->refname,
update->flags, &update->new_oid,
&update->old_oid, NULL, NULL,
&update->old_oid, &update->peeled,
NULL, NULL,
update->committer_info, update->msg);
else
ref_transaction_add_update(loose_transaction, update->refname,
update->flags & ~REF_HAVE_OLD,
update->new_target ? NULL : &update->new_oid, NULL,
update->new_target, NULL, update->committer_info,
&update->peeled, update->new_target,
NULL, update->committer_info,
NULL);
} else {
ref_transaction_add_update(packed_transaction, update->refname,
update->flags & ~REF_HAVE_OLD,
&update->new_oid, &update->old_oid,
NULL, NULL, update->committer_info, NULL);
&update->peeled, NULL, NULL,
update->committer_info, NULL);
}
}

View File

@@ -218,14 +218,14 @@ static size_t snapshot_hexsz(const struct snapshot *snapshot)
struct ref_store *packed_ref_store_init(struct repository *repo,
const char *payload UNUSED,
const char *gitdir,
unsigned int store_flags)
const struct ref_store_init_options *opts)
{
struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
struct strbuf sb = STRBUF_INIT;
base_ref_store_init(ref_store, repo, gitdir, &refs_be_packed);
refs->store_flags = store_flags;
refs->store_flags = opts->access_flags;
strbuf_addf(&sb, "%s/packed-refs", gitdir);
refs->path = strbuf_detach(&sb, NULL);
@@ -1531,13 +1531,11 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
*/
i++;
} else {
struct object_id peeled;
int peel_error = peel_object(refs->base.repo, &update->new_oid,
&peeled, PEEL_OBJECT_VERIFY_TAGGED_OBJECT_TYPE);
bool peeled = update->flags & REF_HAVE_PEELED;
if (write_packed_entry(out, update->refname,
&update->new_oid,
peel_error ? NULL : &peeled))
peeled ? &update->peeled : NULL))
goto write_error;
i++;

View File

@@ -3,6 +3,7 @@
struct repository;
struct ref_transaction;
struct ref_store_init_options;
/*
* Support for storing references in a `packed-refs` file.
@@ -16,7 +17,7 @@ struct ref_transaction;
struct ref_store *packed_ref_store_init(struct repository *repo,
const char *payload,
const char *gitdir,
unsigned int store_flags);
const struct ref_store_init_options *options);
/*
* Lock the packed-refs file for writing. Flags is passed to

View File

@@ -39,6 +39,13 @@ struct ref_transaction;
*/
#define REF_LOG_ONLY (1 << 7)
/*
* The reference contains a peeled object ID. This is used when the
* new_oid is pointing to a tag object and the reference backend
* wants to also store the peeled value for optimized retrieval.
*/
#define REF_HAVE_PEELED (1 << 15)
/*
* Return the length of time to retry acquiring a loose reference lock
* before giving up, in milliseconds:
@@ -92,6 +99,12 @@ struct ref_update {
*/
struct object_id old_oid;
/*
* If the new_oid points to a tag object, set this to the peeled
* object ID for optimized retrieval without needed to hit the odb.
*/
struct object_id peeled;
/*
* If set, point the reference to this value. This can also be
* used to convert regular references to become symbolic refs.
@@ -169,6 +182,7 @@ struct ref_update *ref_transaction_add_update(
const char *refname, unsigned int flags,
const struct object_id *new_oid,
const struct object_id *old_oid,
const struct object_id *peeled,
const char *new_target, const char *old_target,
const char *committer_info,
const char *msg);
@@ -385,6 +399,21 @@ struct ref_store;
REF_STORE_ODB | \
REF_STORE_MAIN)
/*
* Options for initializing the ref backend. All backend-agnostic information
* which backends required will be held here.
*/
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;
};
/*
* Initialize the ref_store for the specified gitdir. These functions
* should call base_ref_store_init() to initialize the shared part of
@@ -393,7 +422,7 @@ struct ref_store;
typedef struct ref_store *ref_store_init_fn(struct repository *repo,
const char *payload,
const char *gitdir,
unsigned int flags);
const struct ref_store_init_options *opts);
/*
* Release all memory and resources associated with the ref store.
*/
@@ -421,10 +450,6 @@ typedef int ref_transaction_abort_fn(struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
typedef int ref_transaction_commit_fn(struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
typedef int optimize_fn(struct ref_store *ref_store,
struct refs_optimize_opts *opts);

View File

@@ -12,7 +12,6 @@
#include "../hex.h"
#include "../ident.h"
#include "../iterator.h"
#include "../object.h"
#include "../parse.h"
#include "../path.h"
#include "../refs.h"
@@ -369,7 +368,7 @@ static int reftable_be_config(const char *var, const char *value,
static struct ref_store *reftable_be_init(struct repository *repo,
const char *payload,
const char *gitdir,
unsigned int store_flags)
const struct ref_store_init_options *opts)
{
struct reftable_ref_store *refs = xcalloc(1, sizeof(*refs));
struct strbuf ref_common_dir = STRBUF_INIT;
@@ -386,8 +385,8 @@ static struct ref_store *reftable_be_init(struct repository *repo,
base_ref_store_init(&refs->base, repo, refdir.buf, &refs_be_reftable);
strmap_init(&refs->worktree_backends);
refs->store_flags = store_flags;
refs->log_all_ref_updates = repo_settings_get_log_all_ref_updates(repo);
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:
@@ -1081,25 +1080,6 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
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) &&
!(u->flags & REF_LOG_ONLY)) {
struct object *o = parse_object(refs->base.repo, &u->new_oid);
if (!o) {
strbuf_addf(err,
_("trying to write ref '%s' with nonexistent object %s"),
u->refname, oid_to_hex(&u->new_oid));
return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
}
if (o->type != OBJ_COMMIT && is_branch(u->refname)) {
strbuf_addf(err, _("trying to write non-commit object %s to branch '%s'"),
oid_to_hex(&u->new_oid), u->refname);
return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
}
}
/*
* When we update the reference that HEAD points to we enqueue
* a second log-only update for HEAD so that its reflog is
@@ -1126,8 +1106,8 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
ref_transaction_add_update(
transaction, "HEAD",
u->flags | REF_LOG_ONLY | REF_NO_DEREF,
&u->new_oid, &u->old_oid, NULL, NULL, NULL,
u->msg);
&u->new_oid, &u->old_oid, &u->peeled, NULL, NULL,
NULL, u->msg);
}
ret = reftable_backend_read_ref(be, rewritten_ref,
@@ -1213,7 +1193,7 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
transaction, referent->buf, new_flags,
u->new_target ? NULL : &u->new_oid,
u->old_target ? NULL : &u->old_oid,
u->new_target, u->old_target,
&u->peeled, u->new_target, u->old_target,
u->committer_info, u->msg);
new_update->parent_update = u;
@@ -1603,17 +1583,13 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
goto done;
} else if (u->flags & REF_HAVE_NEW) {
struct reftable_ref_record ref = {0};
struct object_id peeled;
int peel_error;
ref.refname = (char *)u->refname;
ref.update_index = ts;
peel_error = peel_object(arg->refs->base.repo, &u->new_oid, &peeled,
PEEL_OBJECT_VERIFY_TAGGED_OBJECT_TYPE);
if (!peel_error) {
if (u->flags & REF_HAVE_PEELED) {
ref.value_type = REFTABLE_REF_VAL2;
memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
memcpy(ref.value.val2.target_value, u->peeled.hash, GIT_MAX_RAWSZ);
memcpy(ref.value.val2.value, u->new_oid.hash, GIT_MAX_RAWSZ);
} else if (!is_null_oid(&u->new_oid)) {
ref.value_type = REFTABLE_REF_VAL1;

View File

@@ -1196,6 +1196,20 @@ test_expect_success 'stdin -z create ref fails with empty new value' '
test_must_fail git rev-parse --verify -q $c
'
test_expect_success 'stdin -z create ref fails with non commit object' '
printf $F "create $c" "$(test_oid 001)" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
grep "fatal: trying to write ref ${SQ}$c${SQ} with nonexistent object" err &&
test_must_fail git rev-parse --verify -q $c
'
test_expect_success 'stdin -z update ref fails with non commit object' '
printf $F "update $b" "$(test_oid 001)" "" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
grep "fatal: trying to write ref ${SQ}$b${SQ} with nonexistent object" err &&
test_must_fail git rev-parse --verify -q $c
'
test_expect_success 'stdin -z update ref works with right old value' '
printf $F "update $b" "$m~1" "$m" >stdin &&
git update-ref -z --stdin <stdin &&