Skip to content

Commit c750ba9

Browse files
bradkinggitster
authored andcommitted
update-ref: support multiple simultaneous updates
Add a --stdin signature to read update instructions from standard input and apply multiple ref updates together. Use an input format that supports any update that could be specified via the command-line, including object names like "branch:path with space". Signed-off-by: Brad King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 98aee92 commit c750ba9

File tree

2 files changed

+304
-2
lines changed

2 files changed

+304
-2
lines changed

Documentation/git-update-ref.txt

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ git-update-ref - Update the object name stored in a ref safely
88
SYNOPSIS
99
--------
1010
[verse]
11-
'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>])
11+
'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
1212

1313
DESCRIPTION
1414
-----------
@@ -58,6 +58,58 @@ archive by creating a symlink tree).
5858
With `-d` flag, it deletes the named <ref> after verifying it
5959
still contains <oldvalue>.
6060

61+
With `--stdin`, update-ref reads instructions from standard input and
62+
performs all modifications together. Specify commands of the form:
63+
64+
update SP <ref> SP <newvalue> [SP <oldvalue>] LF
65+
create SP <ref> SP <newvalue> LF
66+
delete SP <ref> [SP <oldvalue>] LF
67+
verify SP <ref> [SP <oldvalue>] LF
68+
option SP <opt> LF
69+
70+
Quote fields containing whitespace as if they were strings in C source
71+
code. Alternatively, use `-z` to specify commands without quoting:
72+
73+
update SP <ref> NUL <newvalue> NUL [<oldvalue>] NUL
74+
create SP <ref> NUL <newvalue> NUL
75+
delete SP <ref> NUL [<oldvalue>] NUL
76+
verify SP <ref> NUL [<oldvalue>] NUL
77+
option SP <opt> NUL
78+
79+
Lines of any other format or a repeated <ref> produce an error.
80+
Command meanings are:
81+
82+
update::
83+
Set <ref> to <newvalue> after verifying <oldvalue>, if given.
84+
Specify a zero <newvalue> to ensure the ref does not exist
85+
after the update and/or a zero <oldvalue> to make sure the
86+
ref does not exist before the update.
87+
88+
create::
89+
Create <ref> with <newvalue> after verifying it does not
90+
exist. The given <newvalue> may not be zero.
91+
92+
delete::
93+
Delete <ref> after verifying it exists with <oldvalue>, if
94+
given. If given, <oldvalue> may not be zero.
95+
96+
verify::
97+
Verify <ref> against <oldvalue> but do not change it. If
98+
<oldvalue> zero or missing, the ref must not exist.
99+
100+
option::
101+
Modify behavior of the next command naming a <ref>.
102+
The only valid option is `no-deref` to avoid dereferencing
103+
a symbolic ref.
104+
105+
Use 40 "0" or the empty string to specify a zero value, except that
106+
with `-z` an empty <oldvalue> is considered missing.
107+
108+
If all <ref>s can be locked with matching <oldvalue>s
109+
simultaneously, all modifications are performed. Otherwise, no
110+
modifications are performed. Note that while each individual
111+
<ref> is updated or deleted atomically, a concurrent reader may
112+
still see a subset of the modifications.
61113

62114
Logging Updates
63115
---------------

builtin/update-ref.c

Lines changed: 251 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,261 @@
22
#include "refs.h"
33
#include "builtin.h"
44
#include "parse-options.h"
5+
#include "quote.h"
6+
#include "argv-array.h"
57

68
static const char * const git_update_ref_usage[] = {
79
N_("git update-ref [options] -d <refname> [<oldval>]"),
810
N_("git update-ref [options] <refname> <newval> [<oldval>]"),
11+
N_("git update-ref [options] --stdin [-z]"),
912
NULL
1013
};
1114

15+
static int updates_alloc;
16+
static int updates_count;
17+
static const struct ref_update **updates;
18+
19+
static char line_termination = '\n';
20+
static int update_flags;
21+
22+
static struct ref_update *update_alloc(void)
23+
{
24+
struct ref_update *update;
25+
26+
/* Allocate and zero-init a struct ref_update */
27+
update = xcalloc(1, sizeof(*update));
28+
ALLOC_GROW(updates, updates_count + 1, updates_alloc);
29+
updates[updates_count++] = update;
30+
31+
/* Store and reset accumulated options */
32+
update->flags = update_flags;
33+
update_flags = 0;
34+
35+
return update;
36+
}
37+
38+
static void update_store_ref_name(struct ref_update *update,
39+
const char *ref_name)
40+
{
41+
if (check_refname_format(ref_name, REFNAME_ALLOW_ONELEVEL))
42+
die("invalid ref format: %s", ref_name);
43+
update->ref_name = xstrdup(ref_name);
44+
}
45+
46+
static void update_store_new_sha1(struct ref_update *update,
47+
const char *newvalue)
48+
{
49+
if (*newvalue && get_sha1(newvalue, update->new_sha1))
50+
die("invalid new value for ref %s: %s",
51+
update->ref_name, newvalue);
52+
}
53+
54+
static void update_store_old_sha1(struct ref_update *update,
55+
const char *oldvalue)
56+
{
57+
if (*oldvalue && get_sha1(oldvalue, update->old_sha1))
58+
die("invalid old value for ref %s: %s",
59+
update->ref_name, oldvalue);
60+
61+
/* We have an old value if non-empty, or if empty without -z */
62+
update->have_old = *oldvalue || line_termination;
63+
}
64+
65+
static const char *parse_arg(const char *next, struct strbuf *arg)
66+
{
67+
/* Parse SP-terminated, possibly C-quoted argument */
68+
if (*next != '"')
69+
while (*next && !isspace(*next))
70+
strbuf_addch(arg, *next++);
71+
else if (unquote_c_style(arg, next, &next))
72+
die("badly quoted argument: %s", next);
73+
74+
/* Return position after the argument */
75+
return next;
76+
}
77+
78+
static const char *parse_first_arg(const char *next, struct strbuf *arg)
79+
{
80+
/* Parse argument immediately after "command SP" */
81+
strbuf_reset(arg);
82+
if (line_termination) {
83+
/* Without -z, use the next argument */
84+
next = parse_arg(next, arg);
85+
} else {
86+
/* With -z, use rest of first NUL-terminated line */
87+
strbuf_addstr(arg, next);
88+
next = next + arg->len;
89+
}
90+
return next;
91+
}
92+
93+
static const char *parse_next_arg(const char *next, struct strbuf *arg)
94+
{
95+
/* Parse next SP-terminated or NUL-terminated argument, if any */
96+
strbuf_reset(arg);
97+
if (line_termination) {
98+
/* Without -z, consume SP and use next argument */
99+
if (!*next)
100+
return NULL;
101+
if (*next != ' ')
102+
die("expected SP but got: %s", next);
103+
next = parse_arg(next + 1, arg);
104+
} else {
105+
/* With -z, read the next NUL-terminated line */
106+
if (*next)
107+
die("expected NUL but got: %s", next);
108+
if (strbuf_getline(arg, stdin, '\0') == EOF)
109+
return NULL;
110+
next = arg->buf + arg->len;
111+
}
112+
return next;
113+
}
114+
115+
static void parse_cmd_update(const char *next)
116+
{
117+
struct strbuf ref = STRBUF_INIT;
118+
struct strbuf newvalue = STRBUF_INIT;
119+
struct strbuf oldvalue = STRBUF_INIT;
120+
struct ref_update *update;
121+
122+
update = update_alloc();
123+
124+
if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
125+
update_store_ref_name(update, ref.buf);
126+
else
127+
die("update line missing <ref>");
128+
129+
if ((next = parse_next_arg(next, &newvalue)) != NULL)
130+
update_store_new_sha1(update, newvalue.buf);
131+
else
132+
die("update %s missing <newvalue>", ref.buf);
133+
134+
if ((next = parse_next_arg(next, &oldvalue)) != NULL)
135+
update_store_old_sha1(update, oldvalue.buf);
136+
else if(!line_termination)
137+
die("update %s missing [<oldvalue>] NUL", ref.buf);
138+
139+
if (next && *next)
140+
die("update %s has extra input: %s", ref.buf, next);
141+
}
142+
143+
static void parse_cmd_create(const char *next)
144+
{
145+
struct strbuf ref = STRBUF_INIT;
146+
struct strbuf newvalue = STRBUF_INIT;
147+
struct ref_update *update;
148+
149+
update = update_alloc();
150+
151+
if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
152+
update_store_ref_name(update, ref.buf);
153+
else
154+
die("create line missing <ref>");
155+
156+
if ((next = parse_next_arg(next, &newvalue)) != NULL)
157+
update_store_new_sha1(update, newvalue.buf);
158+
else
159+
die("create %s missing <newvalue>", ref.buf);
160+
if (is_null_sha1(update->new_sha1))
161+
die("create %s given zero new value", ref.buf);
162+
163+
if (next && *next)
164+
die("create %s has extra input: %s", ref.buf, next);
165+
}
166+
167+
static void parse_cmd_delete(const char *next)
168+
{
169+
struct strbuf ref = STRBUF_INIT;
170+
struct strbuf oldvalue = STRBUF_INIT;
171+
struct ref_update *update;
172+
173+
update = update_alloc();
174+
175+
if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
176+
update_store_ref_name(update, ref.buf);
177+
else
178+
die("delete line missing <ref>");
179+
180+
if ((next = parse_next_arg(next, &oldvalue)) != NULL)
181+
update_store_old_sha1(update, oldvalue.buf);
182+
else if(!line_termination)
183+
die("delete %s missing [<oldvalue>] NUL", ref.buf);
184+
if (update->have_old && is_null_sha1(update->old_sha1))
185+
die("delete %s given zero old value", ref.buf);
186+
187+
if (next && *next)
188+
die("delete %s has extra input: %s", ref.buf, next);
189+
}
190+
191+
static void parse_cmd_verify(const char *next)
192+
{
193+
struct strbuf ref = STRBUF_INIT;
194+
struct strbuf value = STRBUF_INIT;
195+
struct ref_update *update;
196+
197+
update = update_alloc();
198+
199+
if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
200+
update_store_ref_name(update, ref.buf);
201+
else
202+
die("verify line missing <ref>");
203+
204+
if ((next = parse_next_arg(next, &value)) != NULL) {
205+
update_store_old_sha1(update, value.buf);
206+
update_store_new_sha1(update, value.buf);
207+
} else if(!line_termination)
208+
die("verify %s missing [<oldvalue>] NUL", ref.buf);
209+
210+
if (next && *next)
211+
die("verify %s has extra input: %s", ref.buf, next);
212+
}
213+
214+
static void parse_cmd_option(const char *next)
215+
{
216+
if (!strcmp(next, "no-deref"))
217+
update_flags |= REF_NODEREF;
218+
else
219+
die("option unknown: %s", next);
220+
}
221+
222+
static void update_refs_stdin(void)
223+
{
224+
struct strbuf cmd = STRBUF_INIT;
225+
226+
/* Read each line dispatch its command */
227+
while (strbuf_getline(&cmd, stdin, line_termination) != EOF)
228+
if (!cmd.buf[0])
229+
die("empty command in input");
230+
else if (isspace(*cmd.buf))
231+
die("whitespace before command: %s", cmd.buf);
232+
else if (!prefixcmp(cmd.buf, "update "))
233+
parse_cmd_update(cmd.buf + 7);
234+
else if (!prefixcmp(cmd.buf, "create "))
235+
parse_cmd_create(cmd.buf + 7);
236+
else if (!prefixcmp(cmd.buf, "delete "))
237+
parse_cmd_delete(cmd.buf + 7);
238+
else if (!prefixcmp(cmd.buf, "verify "))
239+
parse_cmd_verify(cmd.buf + 7);
240+
else if (!prefixcmp(cmd.buf, "option "))
241+
parse_cmd_option(cmd.buf + 7);
242+
else
243+
die("unknown command: %s", cmd.buf);
244+
245+
strbuf_release(&cmd);
246+
}
247+
12248
int cmd_update_ref(int argc, const char **argv, const char *prefix)
13249
{
14250
const char *refname, *oldval, *msg = NULL;
15251
unsigned char sha1[20], oldsha1[20];
16-
int delete = 0, no_deref = 0, flags = 0;
252+
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0, flags = 0;
17253
struct option options[] = {
18254
OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
19255
OPT_BOOLEAN('d', NULL, &delete, N_("delete the reference")),
256+
OPT_BOOLEAN('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
20257
OPT_BOOLEAN( 0 , "no-deref", &no_deref,
21258
N_("update <refname> not the one it points to")),
259+
OPT_BOOLEAN( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
22260
OPT_END(),
23261
};
24262

@@ -28,6 +266,18 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
28266
if (msg && !*msg)
29267
die("Refusing to perform update with empty message.");
30268

269+
if (read_stdin) {
270+
if (delete || no_deref || argc > 0)
271+
usage_with_options(git_update_ref_usage, options);
272+
if (end_null)
273+
line_termination = '\0';
274+
update_refs_stdin();
275+
return update_refs(msg, updates, updates_count, DIE_ON_ERR);
276+
}
277+
278+
if (end_null)
279+
usage_with_options(git_update_ref_usage, options);
280+
31281
if (delete) {
32282
if (argc < 1 || argc > 2)
33283
usage_with_options(git_update_ref_usage, options);

0 commit comments

Comments
 (0)