Merge branch 'kn/update-ref-symref'

"git update-ref --stdin" learned to handle transactional updates of
symbolic-refs.

* kn/update-ref-symref:
  update-ref: add support for 'symref-update' command
  reftable: pick either 'oid' or 'target' for new updates
  update-ref: add support for 'symref-create' command
  update-ref: add support for 'symref-delete' command
  update-ref: add support for 'symref-verify' command
  refs: specify error for regular refs with `old_target`
  refs: create and use `ref_update_expects_existing_old_ref()`
This commit is contained in:
Junio C Hamano
2024-06-20 15:45:11 -07:00
14 changed files with 836 additions and 42 deletions

View File

@ -76,6 +76,65 @@ static char *parse_refname(const char **next)
return strbuf_detach(&ref, NULL);
}
/*
* Wrapper around parse_refname which skips the next delimiter.
*/
static char *parse_next_refname(const char **next)
{
if (line_termination) {
/* Without -z, consume SP and use next argument */
if (!**next || **next == line_termination)
return NULL;
if (**next != ' ')
die("expected SP but got: %s", *next);
} else {
/* With -z, read the next NUL-terminated line */
if (**next)
return NULL;
}
/* Skip the delimiter */
(*next)++;
return parse_refname(next);
}
/*
* Wrapper around parse_arg which skips the next delimiter.
*/
static char *parse_next_arg(const char **next)
{
struct strbuf arg = STRBUF_INIT;
if (line_termination) {
/* Without -z, consume SP and use next argument */
if (!**next || **next == line_termination)
return NULL;
if (**next != ' ')
die("expected SP but got: %s", *next);
} else {
/* With -z, read the next NUL-terminated line */
if (**next)
return NULL;
}
/* Skip the delimiter */
(*next)++;
if (line_termination) {
/* Without -z, use the next argument */
*next = parse_arg(*next, &arg);
} else {
/* With -z, use everything up to the next NUL */
strbuf_addstr(&arg, *next);
*next += arg.len;
}
if (arg.len)
return strbuf_detach(&arg, NULL);
strbuf_release(&arg);
return NULL;
}
/*
* The value being parsed is <old-oid> (as opposed to <new-oid>; the
* difference affects which error messages are generated):
@ -214,6 +273,61 @@ static void parse_cmd_update(struct ref_transaction *transaction,
strbuf_release(&err);
}
static void parse_cmd_symref_update(struct ref_transaction *transaction,
const char *next, const char *end)
{
char *refname, *new_target, *old_arg;
char *old_target = NULL;
struct strbuf err = STRBUF_INIT;
struct object_id old_oid;
int have_old_oid = 0;
refname = parse_refname(&next);
if (!refname)
die("symref-update: missing <ref>");
new_target = parse_next_refname(&next);
if (!new_target)
die("symref-update %s: missing <new-target>", refname);
old_arg = parse_next_arg(&next);
if (old_arg) {
old_target = parse_next_arg(&next);
if (!old_target)
die("symref-update %s: expected old value", refname);
if (!strcmp(old_arg, "oid")) {
if (repo_get_oid(the_repository, old_target, &old_oid))
die("symref-update %s: invalid oid: %s", refname, old_target);
have_old_oid = 1;
} else if (!strcmp(old_arg, "ref")) {
if (check_refname_format(old_target, REFNAME_ALLOW_ONELEVEL))
die("symref-update %s: invalid ref: %s", refname, old_target);
} else {
die("symref-update %s: invalid arg '%s' for old value", refname, old_arg);
}
}
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);
update_flags = default_flags;
free(refname);
free(old_arg);
free(old_target);
free(new_target);
strbuf_release(&err);
}
static void parse_cmd_create(struct ref_transaction *transaction,
const char *next, const char *end)
{
@ -234,7 +348,7 @@ 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,
if (ref_transaction_create(transaction, refname, &new_oid, NULL,
update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf);
@ -244,6 +358,35 @@ static void parse_cmd_create(struct ref_transaction *transaction,
strbuf_release(&err);
}
static void parse_cmd_symref_create(struct ref_transaction *transaction,
const char *next, const char *end)
{
struct strbuf err = STRBUF_INIT;
char *refname, *new_target;
refname = parse_refname(&next);
if (!refname)
die("symref-create: missing <ref>");
new_target = parse_next_refname(&next);
if (!new_target)
die("symref-create %s: missing <new-target>", refname);
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);
update_flags = default_flags;
free(refname);
free(new_target);
strbuf_release(&err);
}
static void parse_cmd_delete(struct ref_transaction *transaction,
const char *next, const char *end)
{
@ -270,7 +413,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
if (ref_transaction_delete(transaction, refname,
have_old ? &old_oid : NULL,
update_flags, msg, &err))
NULL, update_flags, msg, &err))
die("%s", err.buf);
update_flags = default_flags;
@ -278,6 +421,36 @@ 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)
{
struct strbuf err = STRBUF_INIT;
char *refname, *old_target;
if (!(update_flags & REF_NO_DEREF))
die("symref-delete: cannot operate with deref mode");
refname = parse_refname(&next);
if (!refname)
die("symref-delete: missing <ref>");
old_target = parse_next_refname(&next);
if (*next != line_termination)
die("symref-delete %s: extra input: %s", refname, next);
if (ref_transaction_delete(transaction, refname, NULL,
old_target, update_flags, msg, &err))
die("%s", err.buf);
update_flags = default_flags;
free(refname);
free(old_target);
strbuf_release(&err);
}
static void parse_cmd_verify(struct ref_transaction *transaction,
const char *next, const char *end)
{
@ -297,7 +470,7 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
die("verify %s: extra input: %s", refname, next);
if (ref_transaction_verify(transaction, refname, &old_oid,
update_flags, &err))
NULL, update_flags, &err))
die("%s", err.buf);
update_flags = default_flags;
@ -305,6 +478,42 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
strbuf_release(&err);
}
static void parse_cmd_symref_verify(struct ref_transaction *transaction,
const char *next, const char *end)
{
struct strbuf err = STRBUF_INIT;
struct object_id old_oid;
char *refname, *old_target;
if (!(update_flags & REF_NO_DEREF))
die("symref-verify: cannot operate with deref mode");
refname = parse_refname(&next);
if (!refname)
die("symref-verify: missing <ref>");
/*
* old_ref is optional, if not provided, we need to ensure that the
* ref doesn't exist.
*/
old_target = parse_next_refname(&next);
if (!old_target)
oidcpy(&old_oid, null_oid());
if (*next != line_termination)
die("symref-verify %s: extra input: %s", refname, next);
if (ref_transaction_verify(transaction, refname,
old_target ? NULL : &old_oid,
old_target, update_flags, &err))
die("%s", err.buf);
update_flags = default_flags;
free(refname);
free(old_target);
strbuf_release(&err);
}
static void report_ok(const char *command)
{
fprintf(stdout, "%s: ok\n", command);
@ -380,15 +589,19 @@ static const struct parse_cmd {
unsigned args;
enum update_refs_state state;
} command[] = {
{ "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
{ "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
{ "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
{ "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
{ "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
{ "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
{ "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
{ "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
{ "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
{ "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
{ "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
{ "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
{ "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
{ "symref-update", parse_cmd_symref_update, 4, UPDATE_REFS_OPEN },
{ "symref-create", parse_cmd_symref_create, 2, UPDATE_REFS_OPEN },
{ "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN },
{ "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN },
{ "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
{ "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
{ "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
{ "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
{ "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
};
static void update_refs_stdin(void)