Skip to content

Commit 5f14d20

Browse files
committed
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()`
2 parents c1322ca + 7dd4051 commit 5f14d20

14 files changed

+836
-42
lines changed

Documentation/git-update-ref.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ performs all modifications together. Specify commands of the form:
6565
create SP <ref> SP <new-oid> LF
6666
delete SP <ref> [SP <old-oid>] LF
6767
verify SP <ref> [SP <old-oid>] LF
68+
symref-update SP <ref> SP <new-target> [SP (ref SP <old-target> | oid SP <old-oid>)] LF
69+
symref-create SP <ref> SP <new-target> LF
70+
symref-delete SP <ref> [SP <old-target>] LF
71+
symref-verify SP <ref> [SP <old-target>] LF
6872
option SP <opt> LF
6973
start LF
7074
prepare LF
@@ -86,6 +90,10 @@ quoting:
8690
create SP <ref> NUL <new-oid> NUL
8791
delete SP <ref> NUL [<old-oid>] NUL
8892
verify SP <ref> NUL [<old-oid>] NUL
93+
symref-update SP <ref> NUL <new-target> [NUL (ref NUL <old-target> | oid NUL <old-oid>)] NUL
94+
symref-create SP <ref> NUL <new-target> NUL
95+
symref-delete SP <ref> [NUL <old-target>] NUL
96+
symref-verify SP <ref> [NUL <old-target>] NUL
8997
option SP <opt> NUL
9098
start NUL
9199
prepare NUL
@@ -113,10 +121,27 @@ delete::
113121
Delete <ref> after verifying it exists with <old-oid>, if
114122
given. If given, <old-oid> may not be zero.
115123

124+
symref-update::
125+
Set <ref> to <new-target> after verifying <old-target> or <old-oid>,
126+
if given. Specify a zero <old-oid> to ensure that the ref does not
127+
exist before the update.
128+
116129
verify::
117130
Verify <ref> against <old-oid> but do not change it. If
118131
<old-oid> is zero or missing, the ref must not exist.
119132

133+
symref-create:
134+
Create symbolic ref <ref> with <new-target> after verifying
135+
it does not exist.
136+
137+
symref-delete::
138+
Delete <ref> after verifying it exists with <old-target>, if given.
139+
140+
symref-verify::
141+
Verify symbolic <ref> against <old-target> but do not change it.
142+
If <old-target> is missing, the ref must not exist. Can only be
143+
used in `no-deref` mode.
144+
120145
option::
121146
Modify the behavior of the next command naming a <ref>.
122147
The only valid option is `no-deref` to avoid dereferencing

builtin/clone.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ static void write_remote_refs(const struct ref *local_refs)
580580
if (!r->peer_ref)
581581
continue;
582582
if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid,
583-
0, NULL, &err))
583+
NULL, 0, NULL, &err))
584584
die("%s", err.buf);
585585
}
586586

builtin/fetch.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,8 +1391,8 @@ static int prune_refs(struct display_state *display_state,
13911391
if (!dry_run) {
13921392
if (transaction) {
13931393
for (ref = stale_refs; ref; ref = ref->next) {
1394-
result = ref_transaction_delete(transaction, ref->name, NULL, 0,
1395-
"fetch: prune", &err);
1394+
result = ref_transaction_delete(transaction, ref->name, NULL,
1395+
NULL, 0, "fetch: prune", &err);
13961396
if (result)
13971397
goto cleanup;
13981398
}

builtin/receive-pack.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1576,7 +1576,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
15761576
if (ref_transaction_delete(transaction,
15771577
namespaced_name,
15781578
old_oid,
1579-
0, "push", &err)) {
1579+
NULL, 0,
1580+
"push", &err)) {
15801581
rp_error("%s", err.buf);
15811582
ret = "failed to delete";
15821583
} else {

builtin/update-ref.c

Lines changed: 225 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,65 @@ static char *parse_refname(const char **next)
7676
return strbuf_detach(&ref, NULL);
7777
}
7878

79+
/*
80+
* Wrapper around parse_refname which skips the next delimiter.
81+
*/
82+
static char *parse_next_refname(const char **next)
83+
{
84+
if (line_termination) {
85+
/* Without -z, consume SP and use next argument */
86+
if (!**next || **next == line_termination)
87+
return NULL;
88+
if (**next != ' ')
89+
die("expected SP but got: %s", *next);
90+
} else {
91+
/* With -z, read the next NUL-terminated line */
92+
if (**next)
93+
return NULL;
94+
}
95+
/* Skip the delimiter */
96+
(*next)++;
97+
98+
return parse_refname(next);
99+
}
100+
101+
/*
102+
* Wrapper around parse_arg which skips the next delimiter.
103+
*/
104+
static char *parse_next_arg(const char **next)
105+
{
106+
struct strbuf arg = STRBUF_INIT;
107+
108+
if (line_termination) {
109+
/* Without -z, consume SP and use next argument */
110+
if (!**next || **next == line_termination)
111+
return NULL;
112+
if (**next != ' ')
113+
die("expected SP but got: %s", *next);
114+
} else {
115+
/* With -z, read the next NUL-terminated line */
116+
if (**next)
117+
return NULL;
118+
}
119+
/* Skip the delimiter */
120+
(*next)++;
121+
122+
if (line_termination) {
123+
/* Without -z, use the next argument */
124+
*next = parse_arg(*next, &arg);
125+
} else {
126+
/* With -z, use everything up to the next NUL */
127+
strbuf_addstr(&arg, *next);
128+
*next += arg.len;
129+
}
130+
131+
if (arg.len)
132+
return strbuf_detach(&arg, NULL);
133+
134+
strbuf_release(&arg);
135+
return NULL;
136+
}
137+
79138
/*
80139
* The value being parsed is <old-oid> (as opposed to <new-oid>; the
81140
* difference affects which error messages are generated):
@@ -214,6 +273,61 @@ static void parse_cmd_update(struct ref_transaction *transaction,
214273
strbuf_release(&err);
215274
}
216275

276+
static void parse_cmd_symref_update(struct ref_transaction *transaction,
277+
const char *next, const char *end)
278+
{
279+
char *refname, *new_target, *old_arg;
280+
char *old_target = NULL;
281+
struct strbuf err = STRBUF_INIT;
282+
struct object_id old_oid;
283+
int have_old_oid = 0;
284+
285+
refname = parse_refname(&next);
286+
if (!refname)
287+
die("symref-update: missing <ref>");
288+
289+
new_target = parse_next_refname(&next);
290+
if (!new_target)
291+
die("symref-update %s: missing <new-target>", refname);
292+
293+
old_arg = parse_next_arg(&next);
294+
if (old_arg) {
295+
old_target = parse_next_arg(&next);
296+
if (!old_target)
297+
die("symref-update %s: expected old value", refname);
298+
299+
if (!strcmp(old_arg, "oid")) {
300+
if (repo_get_oid(the_repository, old_target, &old_oid))
301+
die("symref-update %s: invalid oid: %s", refname, old_target);
302+
303+
have_old_oid = 1;
304+
} else if (!strcmp(old_arg, "ref")) {
305+
if (check_refname_format(old_target, REFNAME_ALLOW_ONELEVEL))
306+
die("symref-update %s: invalid ref: %s", refname, old_target);
307+
} else {
308+
die("symref-update %s: invalid arg '%s' for old value", refname, old_arg);
309+
}
310+
}
311+
312+
if (*next != line_termination)
313+
die("symref-update %s: extra input: %s", refname, next);
314+
315+
if (ref_transaction_update(transaction, refname, NULL,
316+
have_old_oid ? &old_oid : NULL,
317+
new_target,
318+
have_old_oid ? NULL : old_target,
319+
update_flags | create_reflog_flag,
320+
msg, &err))
321+
die("%s", err.buf);
322+
323+
update_flags = default_flags;
324+
free(refname);
325+
free(old_arg);
326+
free(old_target);
327+
free(new_target);
328+
strbuf_release(&err);
329+
}
330+
217331
static void parse_cmd_create(struct ref_transaction *transaction,
218332
const char *next, const char *end)
219333
{
@@ -234,13 +348,42 @@ static void parse_cmd_create(struct ref_transaction *transaction,
234348
if (*next != line_termination)
235349
die("create %s: extra input: %s", refname, next);
236350

237-
if (ref_transaction_create(transaction, refname, &new_oid,
351+
if (ref_transaction_create(transaction, refname, &new_oid, NULL,
352+
update_flags | create_reflog_flag,
353+
msg, &err))
354+
die("%s", err.buf);
355+
356+
update_flags = default_flags;
357+
free(refname);
358+
strbuf_release(&err);
359+
}
360+
361+
362+
static void parse_cmd_symref_create(struct ref_transaction *transaction,
363+
const char *next, const char *end)
364+
{
365+
struct strbuf err = STRBUF_INIT;
366+
char *refname, *new_target;
367+
368+
refname = parse_refname(&next);
369+
if (!refname)
370+
die("symref-create: missing <ref>");
371+
372+
new_target = parse_next_refname(&next);
373+
if (!new_target)
374+
die("symref-create %s: missing <new-target>", refname);
375+
376+
if (*next != line_termination)
377+
die("symref-create %s: extra input: %s", refname, next);
378+
379+
if (ref_transaction_create(transaction, refname, NULL, new_target,
238380
update_flags | create_reflog_flag,
239381
msg, &err))
240382
die("%s", err.buf);
241383

242384
update_flags = default_flags;
243385
free(refname);
386+
free(new_target);
244387
strbuf_release(&err);
245388
}
246389

@@ -270,14 +413,44 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
270413

271414
if (ref_transaction_delete(transaction, refname,
272415
have_old ? &old_oid : NULL,
273-
update_flags, msg, &err))
416+
NULL, update_flags, msg, &err))
274417
die("%s", err.buf);
275418

276419
update_flags = default_flags;
277420
free(refname);
278421
strbuf_release(&err);
279422
}
280423

424+
425+
static void parse_cmd_symref_delete(struct ref_transaction *transaction,
426+
const char *next, const char *end)
427+
{
428+
struct strbuf err = STRBUF_INIT;
429+
char *refname, *old_target;
430+
431+
if (!(update_flags & REF_NO_DEREF))
432+
die("symref-delete: cannot operate with deref mode");
433+
434+
refname = parse_refname(&next);
435+
if (!refname)
436+
die("symref-delete: missing <ref>");
437+
438+
old_target = parse_next_refname(&next);
439+
440+
if (*next != line_termination)
441+
die("symref-delete %s: extra input: %s", refname, next);
442+
443+
if (ref_transaction_delete(transaction, refname, NULL,
444+
old_target, update_flags, msg, &err))
445+
die("%s", err.buf);
446+
447+
update_flags = default_flags;
448+
free(refname);
449+
free(old_target);
450+
strbuf_release(&err);
451+
}
452+
453+
281454
static void parse_cmd_verify(struct ref_transaction *transaction,
282455
const char *next, const char *end)
283456
{
@@ -297,11 +470,47 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
297470
die("verify %s: extra input: %s", refname, next);
298471

299472
if (ref_transaction_verify(transaction, refname, &old_oid,
300-
update_flags, &err))
473+
NULL, update_flags, &err))
474+
die("%s", err.buf);
475+
476+
update_flags = default_flags;
477+
free(refname);
478+
strbuf_release(&err);
479+
}
480+
481+
static void parse_cmd_symref_verify(struct ref_transaction *transaction,
482+
const char *next, const char *end)
483+
{
484+
struct strbuf err = STRBUF_INIT;
485+
struct object_id old_oid;
486+
char *refname, *old_target;
487+
488+
if (!(update_flags & REF_NO_DEREF))
489+
die("symref-verify: cannot operate with deref mode");
490+
491+
refname = parse_refname(&next);
492+
if (!refname)
493+
die("symref-verify: missing <ref>");
494+
495+
/*
496+
* old_ref is optional, if not provided, we need to ensure that the
497+
* ref doesn't exist.
498+
*/
499+
old_target = parse_next_refname(&next);
500+
if (!old_target)
501+
oidcpy(&old_oid, null_oid());
502+
503+
if (*next != line_termination)
504+
die("symref-verify %s: extra input: %s", refname, next);
505+
506+
if (ref_transaction_verify(transaction, refname,
507+
old_target ? NULL : &old_oid,
508+
old_target, update_flags, &err))
301509
die("%s", err.buf);
302510

303511
update_flags = default_flags;
304512
free(refname);
513+
free(old_target);
305514
strbuf_release(&err);
306515
}
307516

@@ -380,15 +589,19 @@ static const struct parse_cmd {
380589
unsigned args;
381590
enum update_refs_state state;
382591
} command[] = {
383-
{ "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
384-
{ "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
385-
{ "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
386-
{ "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
387-
{ "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
388-
{ "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
389-
{ "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
390-
{ "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
391-
{ "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
592+
{ "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
593+
{ "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
594+
{ "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
595+
{ "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
596+
{ "symref-update", parse_cmd_symref_update, 4, UPDATE_REFS_OPEN },
597+
{ "symref-create", parse_cmd_symref_create, 2, UPDATE_REFS_OPEN },
598+
{ "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN },
599+
{ "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN },
600+
{ "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
601+
{ "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
602+
{ "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
603+
{ "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
604+
{ "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
392605
};
393606

394607
static void update_refs_stdin(void)

0 commit comments

Comments
 (0)