mirror of
https://github.com/git/git
synced 2024-11-04 16:17:49 +00:00
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:
commit
5f14d20984
14 changed files with 836 additions and 42 deletions
|
@ -65,6 +65,10 @@ performs all modifications together. Specify commands of the form:
|
||||||
create SP <ref> SP <new-oid> LF
|
create SP <ref> SP <new-oid> LF
|
||||||
delete SP <ref> [SP <old-oid>] LF
|
delete SP <ref> [SP <old-oid>] LF
|
||||||
verify SP <ref> [SP <old-oid>] LF
|
verify SP <ref> [SP <old-oid>] LF
|
||||||
|
symref-update SP <ref> SP <new-target> [SP (ref SP <old-target> | oid SP <old-oid>)] LF
|
||||||
|
symref-create SP <ref> SP <new-target> LF
|
||||||
|
symref-delete SP <ref> [SP <old-target>] LF
|
||||||
|
symref-verify SP <ref> [SP <old-target>] LF
|
||||||
option SP <opt> LF
|
option SP <opt> LF
|
||||||
start LF
|
start LF
|
||||||
prepare LF
|
prepare LF
|
||||||
|
@ -86,6 +90,10 @@ quoting:
|
||||||
create SP <ref> NUL <new-oid> NUL
|
create SP <ref> NUL <new-oid> NUL
|
||||||
delete SP <ref> NUL [<old-oid>] NUL
|
delete SP <ref> NUL [<old-oid>] NUL
|
||||||
verify SP <ref> NUL [<old-oid>] NUL
|
verify SP <ref> NUL [<old-oid>] NUL
|
||||||
|
symref-update SP <ref> NUL <new-target> [NUL (ref NUL <old-target> | oid NUL <old-oid>)] NUL
|
||||||
|
symref-create SP <ref> NUL <new-target> NUL
|
||||||
|
symref-delete SP <ref> [NUL <old-target>] NUL
|
||||||
|
symref-verify SP <ref> [NUL <old-target>] NUL
|
||||||
option SP <opt> NUL
|
option SP <opt> NUL
|
||||||
start NUL
|
start NUL
|
||||||
prepare NUL
|
prepare NUL
|
||||||
|
@ -113,10 +121,27 @@ delete::
|
||||||
Delete <ref> after verifying it exists with <old-oid>, if
|
Delete <ref> after verifying it exists with <old-oid>, if
|
||||||
given. If given, <old-oid> may not be zero.
|
given. If given, <old-oid> may not be zero.
|
||||||
|
|
||||||
|
symref-update::
|
||||||
|
Set <ref> to <new-target> after verifying <old-target> or <old-oid>,
|
||||||
|
if given. Specify a zero <old-oid> to ensure that the ref does not
|
||||||
|
exist before the update.
|
||||||
|
|
||||||
verify::
|
verify::
|
||||||
Verify <ref> against <old-oid> but do not change it. If
|
Verify <ref> against <old-oid> but do not change it. If
|
||||||
<old-oid> is zero or missing, the ref must not exist.
|
<old-oid> is zero or missing, the ref must not exist.
|
||||||
|
|
||||||
|
symref-create:
|
||||||
|
Create symbolic ref <ref> with <new-target> after verifying
|
||||||
|
it does not exist.
|
||||||
|
|
||||||
|
symref-delete::
|
||||||
|
Delete <ref> after verifying it exists with <old-target>, if given.
|
||||||
|
|
||||||
|
symref-verify::
|
||||||
|
Verify symbolic <ref> against <old-target> but do not change it.
|
||||||
|
If <old-target> is missing, the ref must not exist. Can only be
|
||||||
|
used in `no-deref` mode.
|
||||||
|
|
||||||
option::
|
option::
|
||||||
Modify the behavior of the next command naming a <ref>.
|
Modify the behavior of the next command naming a <ref>.
|
||||||
The only valid option is `no-deref` to avoid dereferencing
|
The only valid option is `no-deref` to avoid dereferencing
|
||||||
|
|
|
@ -580,7 +580,7 @@ static void write_remote_refs(const struct ref *local_refs)
|
||||||
if (!r->peer_ref)
|
if (!r->peer_ref)
|
||||||
continue;
|
continue;
|
||||||
if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid,
|
if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid,
|
||||||
0, NULL, &err))
|
NULL, 0, NULL, &err))
|
||||||
die("%s", err.buf);
|
die("%s", err.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1391,8 +1391,8 @@ static int prune_refs(struct display_state *display_state,
|
||||||
if (!dry_run) {
|
if (!dry_run) {
|
||||||
if (transaction) {
|
if (transaction) {
|
||||||
for (ref = stale_refs; ref; ref = ref->next) {
|
for (ref = stale_refs; ref; ref = ref->next) {
|
||||||
result = ref_transaction_delete(transaction, ref->name, NULL, 0,
|
result = ref_transaction_delete(transaction, ref->name, NULL,
|
||||||
"fetch: prune", &err);
|
NULL, 0, "fetch: prune", &err);
|
||||||
if (result)
|
if (result)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1576,7 +1576,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
|
||||||
if (ref_transaction_delete(transaction,
|
if (ref_transaction_delete(transaction,
|
||||||
namespaced_name,
|
namespaced_name,
|
||||||
old_oid,
|
old_oid,
|
||||||
0, "push", &err)) {
|
NULL, 0,
|
||||||
|
"push", &err)) {
|
||||||
rp_error("%s", err.buf);
|
rp_error("%s", err.buf);
|
||||||
ret = "failed to delete";
|
ret = "failed to delete";
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -76,6 +76,65 @@ static char *parse_refname(const char **next)
|
||||||
return strbuf_detach(&ref, NULL);
|
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
|
* The value being parsed is <old-oid> (as opposed to <new-oid>; the
|
||||||
* difference affects which error messages are generated):
|
* difference affects which error messages are generated):
|
||||||
|
@ -214,6 +273,61 @@ static void parse_cmd_update(struct ref_transaction *transaction,
|
||||||
strbuf_release(&err);
|
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,
|
static void parse_cmd_create(struct ref_transaction *transaction,
|
||||||
const char *next, const char *end)
|
const char *next, const char *end)
|
||||||
{
|
{
|
||||||
|
@ -234,7 +348,7 @@ static void parse_cmd_create(struct ref_transaction *transaction,
|
||||||
if (*next != line_termination)
|
if (*next != line_termination)
|
||||||
die("create %s: extra input: %s", refname, next);
|
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,
|
update_flags | create_reflog_flag,
|
||||||
msg, &err))
|
msg, &err))
|
||||||
die("%s", err.buf);
|
die("%s", err.buf);
|
||||||
|
@ -244,6 +358,35 @@ static void parse_cmd_create(struct ref_transaction *transaction,
|
||||||
strbuf_release(&err);
|
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,
|
static void parse_cmd_delete(struct ref_transaction *transaction,
|
||||||
const char *next, const char *end)
|
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,
|
if (ref_transaction_delete(transaction, refname,
|
||||||
have_old ? &old_oid : NULL,
|
have_old ? &old_oid : NULL,
|
||||||
update_flags, msg, &err))
|
NULL, update_flags, msg, &err))
|
||||||
die("%s", err.buf);
|
die("%s", err.buf);
|
||||||
|
|
||||||
update_flags = default_flags;
|
update_flags = default_flags;
|
||||||
|
@ -278,6 +421,36 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
|
||||||
strbuf_release(&err);
|
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,
|
static void parse_cmd_verify(struct ref_transaction *transaction,
|
||||||
const char *next, const char *end)
|
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);
|
die("verify %s: extra input: %s", refname, next);
|
||||||
|
|
||||||
if (ref_transaction_verify(transaction, refname, &old_oid,
|
if (ref_transaction_verify(transaction, refname, &old_oid,
|
||||||
update_flags, &err))
|
NULL, update_flags, &err))
|
||||||
die("%s", err.buf);
|
die("%s", err.buf);
|
||||||
|
|
||||||
update_flags = default_flags;
|
update_flags = default_flags;
|
||||||
|
@ -305,6 +478,42 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
|
||||||
strbuf_release(&err);
|
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)
|
static void report_ok(const char *command)
|
||||||
{
|
{
|
||||||
fprintf(stdout, "%s: ok\n", command);
|
fprintf(stdout, "%s: ok\n", command);
|
||||||
|
@ -380,15 +589,19 @@ static const struct parse_cmd {
|
||||||
unsigned args;
|
unsigned args;
|
||||||
enum update_refs_state state;
|
enum update_refs_state state;
|
||||||
} command[] = {
|
} command[] = {
|
||||||
{ "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
|
{ "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
|
||||||
{ "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
|
{ "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
|
||||||
{ "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
|
{ "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
|
||||||
{ "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
|
{ "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
|
||||||
{ "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
|
{ "symref-update", parse_cmd_symref_update, 4, UPDATE_REFS_OPEN },
|
||||||
{ "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
|
{ "symref-create", parse_cmd_symref_create, 2, UPDATE_REFS_OPEN },
|
||||||
{ "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
|
{ "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN },
|
||||||
{ "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
|
{ "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN },
|
||||||
{ "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
|
{ "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)
|
static void update_refs_stdin(void)
|
||||||
|
|
43
refs.c
43
refs.c
|
@ -914,7 +914,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
|
||||||
transaction = ref_store_transaction_begin(refs, &err);
|
transaction = ref_store_transaction_begin(refs, &err);
|
||||||
if (!transaction ||
|
if (!transaction ||
|
||||||
ref_transaction_delete(transaction, refname, old_oid,
|
ref_transaction_delete(transaction, refname, old_oid,
|
||||||
flags, msg, &err) ||
|
NULL, flags, msg, &err) ||
|
||||||
ref_transaction_commit(transaction, &err)) {
|
ref_transaction_commit(transaction, &err)) {
|
||||||
error("%s", err.buf);
|
error("%s", err.buf);
|
||||||
ref_transaction_free(transaction);
|
ref_transaction_free(transaction);
|
||||||
|
@ -1238,43 +1238,57 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
||||||
int ref_transaction_create(struct ref_transaction *transaction,
|
int ref_transaction_create(struct ref_transaction *transaction,
|
||||||
const char *refname,
|
const char *refname,
|
||||||
const struct object_id *new_oid,
|
const struct object_id *new_oid,
|
||||||
|
const char *new_target,
|
||||||
unsigned int flags, const char *msg,
|
unsigned int flags, const char *msg,
|
||||||
struct strbuf *err)
|
struct strbuf *err)
|
||||||
{
|
{
|
||||||
if (!new_oid || is_null_oid(new_oid)) {
|
if (new_oid && new_target)
|
||||||
strbuf_addf(err, "'%s' has a null OID", refname);
|
BUG("create called with both new_oid and new_target set");
|
||||||
|
if ((!new_oid || is_null_oid(new_oid)) && !new_target) {
|
||||||
|
strbuf_addf(err, "'%s' has neither a valid OID nor a target", refname);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return ref_transaction_update(transaction, refname, new_oid,
|
return ref_transaction_update(transaction, refname, new_oid,
|
||||||
null_oid(), NULL, NULL, flags,
|
null_oid(), new_target, NULL, flags,
|
||||||
msg, err);
|
msg, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ref_transaction_delete(struct ref_transaction *transaction,
|
int ref_transaction_delete(struct ref_transaction *transaction,
|
||||||
const char *refname,
|
const char *refname,
|
||||||
const struct object_id *old_oid,
|
const struct object_id *old_oid,
|
||||||
unsigned int flags, const char *msg,
|
const char *old_target,
|
||||||
|
unsigned int flags,
|
||||||
|
const char *msg,
|
||||||
struct strbuf *err)
|
struct strbuf *err)
|
||||||
{
|
{
|
||||||
if (old_oid && is_null_oid(old_oid))
|
if (old_oid && is_null_oid(old_oid))
|
||||||
BUG("delete called with old_oid set to zeros");
|
BUG("delete called with old_oid set to zeros");
|
||||||
|
if (old_oid && old_target)
|
||||||
|
BUG("delete called with both old_oid and old_target set");
|
||||||
|
if (old_target && !(flags & REF_NO_DEREF))
|
||||||
|
BUG("delete cannot operate on symrefs with deref mode");
|
||||||
return ref_transaction_update(transaction, refname,
|
return ref_transaction_update(transaction, refname,
|
||||||
null_oid(), old_oid,
|
null_oid(), old_oid,
|
||||||
NULL, NULL, flags,
|
NULL, old_target, flags,
|
||||||
msg, err);
|
msg, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ref_transaction_verify(struct ref_transaction *transaction,
|
int ref_transaction_verify(struct ref_transaction *transaction,
|
||||||
const char *refname,
|
const char *refname,
|
||||||
const struct object_id *old_oid,
|
const struct object_id *old_oid,
|
||||||
|
const char *old_target,
|
||||||
unsigned int flags,
|
unsigned int flags,
|
||||||
struct strbuf *err)
|
struct strbuf *err)
|
||||||
{
|
{
|
||||||
if (!old_oid)
|
if (!old_target && !old_oid)
|
||||||
BUG("verify called with old_oid set to NULL");
|
BUG("verify called with old_oid and old_target set to NULL");
|
||||||
|
if (old_oid && old_target)
|
||||||
|
BUG("verify called with both old_oid and old_target set");
|
||||||
|
if (old_target && !(flags & REF_NO_DEREF))
|
||||||
|
BUG("verify cannot operate on symrefs with deref mode");
|
||||||
return ref_transaction_update(transaction, refname,
|
return ref_transaction_update(transaction, refname,
|
||||||
NULL, old_oid,
|
NULL, old_oid,
|
||||||
NULL, NULL,
|
NULL, old_target,
|
||||||
flags, NULL, err);
|
flags, NULL, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2485,7 +2499,7 @@ int refs_delete_refs(struct ref_store *refs, const char *logmsg,
|
||||||
|
|
||||||
for_each_string_list_item(item, refnames) {
|
for_each_string_list_item(item, refnames) {
|
||||||
ret = ref_transaction_delete(transaction, item->string,
|
ret = ref_transaction_delete(transaction, item->string,
|
||||||
NULL, flags, msg, &err);
|
NULL, NULL, flags, msg, &err);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
warning(_("could not delete reference %s: %s"),
|
warning(_("could not delete reference %s: %s"),
|
||||||
item->string, err.buf);
|
item->string, err.buf);
|
||||||
|
@ -2595,7 +2609,7 @@ static int migrate_one_ref(const char *refname, const struct object_id *oid,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto done;
|
goto done;
|
||||||
} else {
|
} else {
|
||||||
ret = ref_transaction_create(data->transaction, refname, oid,
|
ret = ref_transaction_create(data->transaction, refname, oid, NULL,
|
||||||
REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
|
REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
|
||||||
NULL, data->errbuf);
|
NULL, data->errbuf);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -2878,3 +2892,10 @@ int repo_migrate_ref_storage_format(struct repository *repo,
|
||||||
strbuf_release(&new_gitdir);
|
strbuf_release(&new_gitdir);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ref_update_expects_existing_old_ref(struct ref_update *update)
|
||||||
|
{
|
||||||
|
return (update->flags & REF_HAVE_OLD) &&
|
||||||
|
(!is_null_oid(&update->old_oid) || update->old_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
6
refs.h
6
refs.h
|
@ -717,6 +717,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
||||||
int ref_transaction_create(struct ref_transaction *transaction,
|
int ref_transaction_create(struct ref_transaction *transaction,
|
||||||
const char *refname,
|
const char *refname,
|
||||||
const struct object_id *new_oid,
|
const struct object_id *new_oid,
|
||||||
|
const char *new_target,
|
||||||
unsigned int flags, const char *msg,
|
unsigned int flags, const char *msg,
|
||||||
struct strbuf *err);
|
struct strbuf *err);
|
||||||
|
|
||||||
|
@ -731,7 +732,9 @@ int ref_transaction_create(struct ref_transaction *transaction,
|
||||||
int ref_transaction_delete(struct ref_transaction *transaction,
|
int ref_transaction_delete(struct ref_transaction *transaction,
|
||||||
const char *refname,
|
const char *refname,
|
||||||
const struct object_id *old_oid,
|
const struct object_id *old_oid,
|
||||||
unsigned int flags, const char *msg,
|
const char *old_target,
|
||||||
|
unsigned int flags,
|
||||||
|
const char *msg,
|
||||||
struct strbuf *err);
|
struct strbuf *err);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -745,6 +748,7 @@ int ref_transaction_delete(struct ref_transaction *transaction,
|
||||||
int ref_transaction_verify(struct ref_transaction *transaction,
|
int ref_transaction_verify(struct ref_transaction *transaction,
|
||||||
const char *refname,
|
const char *refname,
|
||||||
const struct object_id *old_oid,
|
const struct object_id *old_oid,
|
||||||
|
const char *old_target,
|
||||||
unsigned int flags,
|
unsigned int flags,
|
||||||
struct strbuf *err);
|
struct strbuf *err);
|
||||||
|
|
||||||
|
|
|
@ -2457,8 +2457,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
|
||||||
struct strbuf *err)
|
struct strbuf *err)
|
||||||
{
|
{
|
||||||
struct strbuf referent = STRBUF_INIT;
|
struct strbuf referent = STRBUF_INIT;
|
||||||
int mustexist = (update->flags & REF_HAVE_OLD) &&
|
int mustexist = ref_update_expects_existing_old_ref(update);
|
||||||
!is_null_oid(&update->old_oid);
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct ref_lock *lock;
|
struct ref_lock *lock;
|
||||||
|
|
||||||
|
@ -2537,14 +2536,16 @@ static int lock_ref_for_update(struct files_ref_store *refs,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Even if the ref is a regular ref, if `old_target` is set, we
|
* Even if the ref is a regular ref, if `old_target` is set, we
|
||||||
* check the referent value. Ideally `old_target` should only
|
* fail with an error.
|
||||||
* be set for symrefs, but we're strict about its usage.
|
|
||||||
*/
|
*/
|
||||||
if (update->old_target) {
|
if (update->old_target) {
|
||||||
if (ref_update_check_old_target(referent.buf, update, err)) {
|
strbuf_addf(err, _("cannot lock ref '%s': "
|
||||||
ret = TRANSACTION_GENERIC_ERROR;
|
"expected symref with target '%s': "
|
||||||
goto out;
|
"but is a regular ref"),
|
||||||
}
|
ref_update_original_update_refname(update),
|
||||||
|
update->old_target);
|
||||||
|
ret = TRANSACTION_GENERIC_ERROR;
|
||||||
|
goto out;
|
||||||
} else if (check_old_oid(update, &lock->old_oid, err)) {
|
} else if (check_old_oid(update, &lock->old_oid, err)) {
|
||||||
ret = TRANSACTION_GENERIC_ERROR;
|
ret = TRANSACTION_GENERIC_ERROR;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -742,4 +742,10 @@ int ref_update_has_null_new_value(struct ref_update *update);
|
||||||
int ref_update_check_old_target(const char *referent, struct ref_update *update,
|
int ref_update_check_old_target(const char *referent, struct ref_update *update,
|
||||||
struct strbuf *err);
|
struct strbuf *err);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the ref must exist, this means that the old_oid or
|
||||||
|
* old_target is non NULL.
|
||||||
|
*/
|
||||||
|
int ref_update_expects_existing_old_ref(struct ref_update *update);
|
||||||
|
|
||||||
#endif /* REFS_REFS_INTERNAL_H */
|
#endif /* REFS_REFS_INTERNAL_H */
|
||||||
|
|
|
@ -934,7 +934,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
|
||||||
¤t_oid, &referent, &u->type);
|
¤t_oid, &referent, &u->type);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret > 0 && (!(u->flags & REF_HAVE_OLD) || is_null_oid(&u->old_oid))) {
|
if (ret > 0 && !ref_update_expects_existing_old_ref(u)) {
|
||||||
/*
|
/*
|
||||||
* The reference does not exist, and we either have no
|
* The reference does not exist, and we either have no
|
||||||
* old object ID or expect the reference to not exist.
|
* old object ID or expect the reference to not exist.
|
||||||
|
@ -1005,8 +1005,9 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
|
||||||
*/
|
*/
|
||||||
new_update = ref_transaction_add_update(
|
new_update = ref_transaction_add_update(
|
||||||
transaction, referent.buf, new_flags,
|
transaction, referent.buf, new_flags,
|
||||||
&u->new_oid, &u->old_oid, u->new_target,
|
u->new_target ? NULL : &u->new_oid,
|
||||||
u->old_target, u->msg);
|
u->old_target ? NULL : &u->old_oid,
|
||||||
|
u->new_target, u->old_target, u->msg);
|
||||||
|
|
||||||
new_update->parent_update = u;
|
new_update->parent_update = u;
|
||||||
|
|
||||||
|
@ -1038,6 +1039,16 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
|
||||||
* backend returns, which keeps our tests happy.
|
* backend returns, which keeps our tests happy.
|
||||||
*/
|
*/
|
||||||
if (u->old_target) {
|
if (u->old_target) {
|
||||||
|
if (!(u->type & REF_ISSYMREF)) {
|
||||||
|
strbuf_addf(err, _("cannot lock ref '%s': "
|
||||||
|
"expected symref with target '%s': "
|
||||||
|
"but is a regular ref"),
|
||||||
|
ref_update_original_update_refname(u),
|
||||||
|
u->old_target);
|
||||||
|
ret = -1;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
if (ref_update_check_old_target(referent.buf, u, err)) {
|
if (ref_update_check_old_target(referent.buf, u, err)) {
|
||||||
ret = -1;
|
ret = -1;
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
@ -468,4 +468,36 @@ test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
|
||||||
esac
|
esac
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success SYMLINKS 'symref transaction supports symlinks' '
|
||||||
|
test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" &&
|
||||||
|
git update-ref refs/heads/new @ &&
|
||||||
|
test_config core.prefersymlinkrefs true &&
|
||||||
|
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_expect_success 'symref transaction supports false symlink config' '
|
||||||
|
test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" &&
|
||||||
|
git update-ref refs/heads/new @ &&
|
||||||
|
test_config core.prefersymlinkrefs false &&
|
||||||
|
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_file .git/TEST_SYMREF_HEAD &&
|
||||||
|
git symbolic-ref TEST_SYMREF_HEAD >actual &&
|
||||||
|
echo refs/heads/new >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -892,17 +892,23 @@ test_expect_success 'stdin update/create/verify combination works' '
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'stdin verify succeeds for correct value' '
|
test_expect_success 'stdin verify succeeds for correct value' '
|
||||||
|
test-tool ref-store main for-each-reflog-ent $m >before &&
|
||||||
git rev-parse $m >expect &&
|
git rev-parse $m >expect &&
|
||||||
echo "verify $m $m" >stdin &&
|
echo "verify $m $m" >stdin &&
|
||||||
git update-ref --stdin <stdin &&
|
git update-ref --stdin <stdin &&
|
||||||
git rev-parse $m >actual &&
|
git rev-parse $m >actual &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual &&
|
||||||
|
test-tool ref-store main for-each-reflog-ent $m >after &&
|
||||||
|
test_cmp before after
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'stdin verify succeeds for missing reference' '
|
test_expect_success 'stdin verify succeeds for missing reference' '
|
||||||
|
test-tool ref-store main for-each-reflog-ent $m >before &&
|
||||||
echo "verify refs/heads/missing $Z" >stdin &&
|
echo "verify refs/heads/missing $Z" >stdin &&
|
||||||
git update-ref --stdin <stdin &&
|
git update-ref --stdin <stdin &&
|
||||||
test_must_fail git rev-parse --verify -q refs/heads/missing
|
test_must_fail git rev-parse --verify -q refs/heads/missing &&
|
||||||
|
test-tool ref-store main for-each-reflog-ent $m >after &&
|
||||||
|
test_cmp before after
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'stdin verify treats no value as missing' '
|
test_expect_success 'stdin verify treats no value as missing' '
|
||||||
|
@ -1356,6 +1362,7 @@ test_expect_success 'fails with duplicate HEAD update' '
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'fails with duplicate ref update via symref' '
|
test_expect_success 'fails with duplicate ref update via symref' '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref2" &&
|
||||||
git branch target2 $A &&
|
git branch target2 $A &&
|
||||||
git symbolic-ref refs/heads/symref2 refs/heads/target2 &&
|
git symbolic-ref refs/heads/symref2 refs/heads/target2 &&
|
||||||
cat >stdin <<-EOF &&
|
cat >stdin <<-EOF &&
|
||||||
|
@ -1643,4 +1650,423 @@ test_expect_success PIPE 'transaction flushes status updates' '
|
||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
format_command () {
|
||||||
|
if test "$1" = "-z"
|
||||||
|
then
|
||||||
|
shift
|
||||||
|
printf "$F" "$@"
|
||||||
|
else
|
||||||
|
echo "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for type in "" "-z"
|
||||||
|
do
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-verify fails without --no-deref" '
|
||||||
|
git symbolic-ref refs/heads/symref $a &&
|
||||||
|
format_command $type "symref-verify refs/heads/symref" "$a" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type <stdin 2>err &&
|
||||||
|
grep "fatal: symref-verify: cannot operate with deref mode" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-verify fails with too many arguments" '
|
||||||
|
format_command $type "symref-verify refs/heads/symref" "$a" "$a" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
if test "$type" = "-z"
|
||||||
|
then
|
||||||
|
grep "fatal: unknown command: $a" err
|
||||||
|
else
|
||||||
|
grep "fatal: symref-verify refs/heads/symref: extra input: $a" err
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-verify succeeds for correct value" '
|
||||||
|
git symbolic-ref refs/heads/symref >expect &&
|
||||||
|
test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
|
||||||
|
format_command $type "symref-verify refs/heads/symref" "$a" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
|
||||||
|
test_cmp before after
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-verify fails with no value" '
|
||||||
|
git symbolic-ref refs/heads/symref >expect &&
|
||||||
|
format_command $type "symref-verify refs/heads/symref" "" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-verify succeeds for dangling reference" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref2" &&
|
||||||
|
test_must_fail git symbolic-ref refs/heads/nonexistent &&
|
||||||
|
git symbolic-ref refs/heads/symref2 refs/heads/nonexistent &&
|
||||||
|
format_command $type "symref-verify refs/heads/symref2" "refs/heads/nonexistent" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-verify fails for missing reference" '
|
||||||
|
test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
|
||||||
|
format_command $type "symref-verify refs/heads/missing" "refs/heads/unknown" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
grep "fatal: cannot lock ref ${SQ}refs/heads/missing${SQ}: unable to resolve reference ${SQ}refs/heads/missing${SQ}" err &&
|
||||||
|
test_must_fail git rev-parse --verify -q refs/heads/missing &&
|
||||||
|
test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
|
||||||
|
test_cmp before after
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-verify fails for wrong value" '
|
||||||
|
git symbolic-ref refs/heads/symref >expect &&
|
||||||
|
format_command $type "symref-verify refs/heads/symref" "$b" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-verify fails for mistaken null value" '
|
||||||
|
git symbolic-ref refs/heads/symref >expect &&
|
||||||
|
format_command $type "symref-verify refs/heads/symref" "$Z" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-delete fails without --no-deref" '
|
||||||
|
git symbolic-ref refs/heads/symref $a &&
|
||||||
|
format_command $type "symref-delete refs/heads/symref" "$a" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type <stdin 2>err &&
|
||||||
|
grep "fatal: symref-delete: cannot operate with deref mode" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-delete fails with no ref" '
|
||||||
|
format_command $type "symref-delete " >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
grep "fatal: symref-delete: missing <ref>" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-delete fails deleting regular ref" '
|
||||||
|
test_when_finished "git update-ref -d refs/heads/regularref" &&
|
||||||
|
git update-ref refs/heads/regularref $a &&
|
||||||
|
format_command $type "symref-delete refs/heads/regularref" "$a" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: expected symref with target ${SQ}$a${SQ}: but is a regular ref" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-delete fails with too many arguments" '
|
||||||
|
format_command $type "symref-delete refs/heads/symref" "$a" "$a" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
if test "$type" = "-z"
|
||||||
|
then
|
||||||
|
grep "fatal: unknown command: $a" err
|
||||||
|
else
|
||||||
|
grep "fatal: symref-delete refs/heads/symref: extra input: $a" err
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-delete fails with wrong old value" '
|
||||||
|
format_command $type "symref-delete refs/heads/symref" "$m" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected refs/heads/main" err &&
|
||||||
|
git symbolic-ref refs/heads/symref >expect &&
|
||||||
|
echo $a >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-delete works with right old value" '
|
||||||
|
format_command $type "symref-delete refs/heads/symref" "$a" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
test_must_fail git rev-parse --verify -q refs/heads/symref
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-delete works with empty old value" '
|
||||||
|
git symbolic-ref refs/heads/symref $a >stdin &&
|
||||||
|
format_command $type "symref-delete refs/heads/symref" "" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
test_must_fail git rev-parse --verify -q $b
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-delete succeeds for dangling reference" '
|
||||||
|
test_must_fail git symbolic-ref refs/heads/nonexistent &&
|
||||||
|
git symbolic-ref refs/heads/symref2 refs/heads/nonexistent &&
|
||||||
|
format_command $type "symref-delete refs/heads/symref2" "refs/heads/nonexistent" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
test_must_fail git symbolic-ref -d refs/heads/symref2
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-delete deletes regular ref without target" '
|
||||||
|
git update-ref refs/heads/regularref $a &&
|
||||||
|
format_command $type "symref-delete refs/heads/regularref" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-create fails with too many arguments" '
|
||||||
|
format_command $type "symref-create refs/heads/symref" "$a" "$a" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
if test "$type" = "-z"
|
||||||
|
then
|
||||||
|
grep "fatal: unknown command: $a" err
|
||||||
|
else
|
||||||
|
grep "fatal: symref-create refs/heads/symref: extra input: $a" err
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-create fails with no target" '
|
||||||
|
format_command $type "symref-create refs/heads/symref" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-create fails with empty target" '
|
||||||
|
format_command $type "symref-create refs/heads/symref" "" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-create works" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
format_command $type "symref-create refs/heads/symref" "$a" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
git symbolic-ref refs/heads/symref >expect &&
|
||||||
|
echo $a >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-create works with --no-deref" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
format_command $type "symref-create refs/heads/symref" "$a" &&
|
||||||
|
git update-ref --stdin $type <stdin 2>err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type create dangling symref ref works" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
format_command $type "symref-create refs/heads/symref" "refs/heads/unkown" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
git symbolic-ref refs/heads/symref >expect &&
|
||||||
|
echo refs/heads/unkown >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-create does not create reflogs by default" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/symref" &&
|
||||||
|
format_command $type "symref-create refs/symref" "$a" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
git symbolic-ref refs/symref >expect &&
|
||||||
|
echo $a >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
test_must_fail git reflog exists refs/symref
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-create reflogs with --create-reflog" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
format_command $type "symref-create refs/heads/symref" "$a" >stdin &&
|
||||||
|
git update-ref --create-reflog --stdin $type --no-deref <stdin &&
|
||||||
|
git symbolic-ref refs/heads/symref >expect &&
|
||||||
|
echo $a >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
git reflog exists refs/heads/symref
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update fails with too many arguments" '
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" "ref" "$a" "$a" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
if test "$type" = "-z"
|
||||||
|
then
|
||||||
|
grep "fatal: unknown command: $a" err
|
||||||
|
else
|
||||||
|
grep "fatal: symref-update refs/heads/symref: extra input: $a" err
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update fails with wrong old value argument" '
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" "foo" "$a" "$a" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
grep "fatal: symref-update refs/heads/symref: invalid arg ${SQ}foo${SQ} for old value" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update creates with zero old value" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" "oid" "$Z" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
echo $a >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update creates with no old value" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
echo $a >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update creates dangling" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
test_must_fail git rev-parse refs/heads/nonexistent &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "refs/heads/nonexistent" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
echo refs/heads/nonexistent >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update fails with wrong old value" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
git symbolic-ref refs/heads/symref $a &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$m" "ref" "$b" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin 2>err &&
|
||||||
|
grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected $b" err &&
|
||||||
|
test_must_fail git rev-parse --verify -q $c
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update updates dangling ref" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
test_must_fail git rev-parse refs/heads/nonexistent &&
|
||||||
|
git symbolic-ref refs/heads/symref refs/heads/nonexistent &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
echo $a >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update updates dangling ref with old value" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
test_must_fail git rev-parse refs/heads/nonexistent &&
|
||||||
|
git symbolic-ref refs/heads/symref refs/heads/nonexistent &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" "ref" "refs/heads/nonexistent" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
echo $a >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update fails update dangling ref with wrong old value" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
test_must_fail git rev-parse refs/heads/nonexistent &&
|
||||||
|
git symbolic-ref refs/heads/symref refs/heads/nonexistent &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" "ref" "refs/heads/wrongref" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
echo refs/heads/nonexistent >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update works with right old value" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
git symbolic-ref refs/heads/symref $a &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$m" "ref" "$a" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
echo $m >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update works with no old value" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
git symbolic-ref refs/heads/symref $a &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$m" >stdin &&
|
||||||
|
git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
echo $m >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update fails with empty old ref-target" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
git symbolic-ref refs/heads/symref $a &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$m" "ref" "" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type --no-deref <stdin &&
|
||||||
|
echo $a >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update creates (with deref)" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
|
||||||
|
git update-ref --stdin $type <stdin &&
|
||||||
|
echo $a >expect &&
|
||||||
|
git symbolic-ref --no-recurse refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual &&
|
||||||
|
grep "$Z $(git rev-parse $a)" actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update regular ref to symref with correct old-oid" '
|
||||||
|
test_when_finished "git symbolic-ref -d --no-recurse refs/heads/regularref" &&
|
||||||
|
git update-ref --no-deref refs/heads/regularref $a &&
|
||||||
|
format_command $type "symref-update refs/heads/regularref" "$a" "oid" "$(git rev-parse $a)" >stdin &&
|
||||||
|
git update-ref --stdin $type <stdin &&
|
||||||
|
echo $a >expect &&
|
||||||
|
git symbolic-ref --no-recurse refs/heads/regularref >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual &&
|
||||||
|
grep "$(git rev-parse $a) $(git rev-parse $a)" actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update regular ref to symref fails with wrong old-oid" '
|
||||||
|
test_when_finished "git update-ref -d refs/heads/regularref" &&
|
||||||
|
git update-ref --no-deref refs/heads/regularref $a &&
|
||||||
|
format_command $type "symref-update refs/heads/regularref" "$a" "oid" "$(git rev-parse refs/heads/target2)" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type <stdin 2>err &&
|
||||||
|
grep "fatal: cannot lock ref ${SQ}refs/heads/regularref${SQ}: is at $(git rev-parse $a) but expected $(git rev-parse refs/heads/target2)" err &&
|
||||||
|
echo $(git rev-parse $a) >expect &&
|
||||||
|
git rev-parse refs/heads/regularref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update regular ref to symref fails with invalid old-oid" '
|
||||||
|
test_when_finished "git update-ref -d refs/heads/regularref" &&
|
||||||
|
git update-ref --no-deref refs/heads/regularref $a &&
|
||||||
|
format_command $type "symref-update refs/heads/regularref" "$a" "oid" "not-a-ref-oid" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type <stdin 2>err &&
|
||||||
|
grep "fatal: symref-update refs/heads/regularref: invalid oid: not-a-ref-oid" err &&
|
||||||
|
echo $(git rev-parse $a) >expect &&
|
||||||
|
git rev-parse refs/heads/regularref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update existing symref with zero old-oid" '
|
||||||
|
test_when_finished "git symbolic-ref -d --no-recurse refs/heads/symref" &&
|
||||||
|
git symbolic-ref refs/heads/symref refs/heads/target2 &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" "oid" "$Z" >stdin &&
|
||||||
|
test_must_fail git update-ref --stdin $type <stdin 2>err &&
|
||||||
|
grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}: reference already exists" err &&
|
||||||
|
echo refs/heads/target2 >expect &&
|
||||||
|
git symbolic-ref refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update regular ref to symref (with deref)" '
|
||||||
|
test_when_finished "git symbolic-ref -d refs/heads/symref" &&
|
||||||
|
test_when_finished "git update-ref -d --no-deref refs/heads/symref2" &&
|
||||||
|
git update-ref refs/heads/symref2 $a &&
|
||||||
|
git symbolic-ref --no-recurse refs/heads/symref refs/heads/symref2 &&
|
||||||
|
format_command $type "symref-update refs/heads/symref" "$a" >stdin &&
|
||||||
|
git update-ref $type --stdin <stdin &&
|
||||||
|
echo $a >expect &&
|
||||||
|
git symbolic-ref --no-recurse refs/heads/symref2 >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
echo refs/heads/symref2 >expect &&
|
||||||
|
git symbolic-ref --no-recurse refs/heads/symref >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual &&
|
||||||
|
grep "$(git rev-parse $a) $(git rev-parse $a)" actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "stdin $type symref-update regular ref to symref" '
|
||||||
|
test_when_finished "git symbolic-ref -d --no-recurse refs/heads/regularref" &&
|
||||||
|
git update-ref --no-deref refs/heads/regularref $a &&
|
||||||
|
format_command $type "symref-update refs/heads/regularref" "$a" >stdin &&
|
||||||
|
git update-ref $type --stdin <stdin &&
|
||||||
|
echo $a >expect &&
|
||||||
|
git symbolic-ref --no-recurse refs/heads/regularref >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual &&
|
||||||
|
grep "$(git rev-parse $a) $(git rev-parse $a)" actual
|
||||||
|
'
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -157,4 +157,58 @@ test_expect_success 'hook captures git-symbolic-ref updates' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'hook gets all queued symref updates' '
|
||||||
|
test_when_finished "rm actual" &&
|
||||||
|
|
||||||
|
git update-ref refs/heads/branch $POST_OID &&
|
||||||
|
git symbolic-ref refs/heads/symref refs/heads/main &&
|
||||||
|
git symbolic-ref refs/heads/symrefd refs/heads/main &&
|
||||||
|
git symbolic-ref refs/heads/symrefu refs/heads/main &&
|
||||||
|
|
||||||
|
test_hook reference-transaction <<-\EOF &&
|
||||||
|
echo "$*" >>actual
|
||||||
|
while read -r line
|
||||||
|
do
|
||||||
|
printf "%s\n" "$line"
|
||||||
|
done >>actual
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# In the files backend, "delete" also triggers an additional transaction
|
||||||
|
# update on the packed-refs backend, which constitutes additional reflog
|
||||||
|
# entries.
|
||||||
|
if test_have_prereq REFFILES
|
||||||
|
then
|
||||||
|
cat >expect <<-EOF
|
||||||
|
aborted
|
||||||
|
$ZERO_OID $ZERO_OID refs/heads/symrefd
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
>expect
|
||||||
|
fi &&
|
||||||
|
|
||||||
|
cat >>expect <<-EOF &&
|
||||||
|
prepared
|
||||||
|
ref:refs/heads/main $ZERO_OID refs/heads/symref
|
||||||
|
ref:refs/heads/main $ZERO_OID refs/heads/symrefd
|
||||||
|
$ZERO_OID ref:refs/heads/main refs/heads/symrefc
|
||||||
|
ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
|
||||||
|
committed
|
||||||
|
ref:refs/heads/main $ZERO_OID refs/heads/symref
|
||||||
|
ref:refs/heads/main $ZERO_OID refs/heads/symrefd
|
||||||
|
$ZERO_OID ref:refs/heads/main refs/heads/symrefc
|
||||||
|
ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git update-ref --no-deref --stdin <<-EOF &&
|
||||||
|
start
|
||||||
|
symref-verify refs/heads/symref refs/heads/main
|
||||||
|
symref-delete refs/heads/symrefd refs/heads/main
|
||||||
|
symref-create refs/heads/symrefc refs/heads/main
|
||||||
|
symref-update refs/heads/symrefu refs/heads/branch ref refs/heads/main
|
||||||
|
prepare
|
||||||
|
commit
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -163,7 +163,7 @@ test_expect_success REFFILES 'local clone from repo with corrupt refs fails grac
|
||||||
echo a >corrupt/.git/refs/heads/topic &&
|
echo a >corrupt/.git/refs/heads/topic &&
|
||||||
|
|
||||||
test_must_fail git clone corrupt working 2>err &&
|
test_must_fail git clone corrupt working 2>err &&
|
||||||
grep "has a null OID" err
|
grep "has neither a valid OID nor a target" err
|
||||||
'
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
Loading…
Reference in a new issue