/src/git/builtin/update-ref.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "builtin.h" |
2 | | #include "config.h" |
3 | | #include "gettext.h" |
4 | | #include "hash.h" |
5 | | #include "refs.h" |
6 | | #include "object-name.h" |
7 | | #include "parse-options.h" |
8 | | #include "quote.h" |
9 | | #include "repository.h" |
10 | | |
11 | | static const char * const git_update_ref_usage[] = { |
12 | | N_("git update-ref [<options>] -d <refname> [<old-oid>]"), |
13 | | N_("git update-ref [<options>] <refname> <new-oid> [<old-oid>]"), |
14 | | N_("git update-ref [<options>] --stdin [-z]"), |
15 | | NULL |
16 | | }; |
17 | | |
18 | | static char line_termination = '\n'; |
19 | | static unsigned int update_flags; |
20 | | static unsigned int default_flags; |
21 | | static unsigned create_reflog_flag; |
22 | | static const char *msg; |
23 | | |
24 | | /* |
25 | | * Parse one whitespace- or NUL-terminated, possibly C-quoted argument |
26 | | * and append the result to arg. Return a pointer to the terminator. |
27 | | * Die if there is an error in how the argument is C-quoted. This |
28 | | * function is only used if not -z. |
29 | | */ |
30 | | static const char *parse_arg(const char *next, struct strbuf *arg) |
31 | 0 | { |
32 | 0 | if (*next == '"') { |
33 | 0 | const char *orig = next; |
34 | |
|
35 | 0 | if (unquote_c_style(arg, next, &next)) |
36 | 0 | die("badly quoted argument: %s", orig); |
37 | 0 | if (*next && !isspace(*next)) |
38 | 0 | die("unexpected character after quoted argument: %s", orig); |
39 | 0 | } else { |
40 | 0 | while (*next && !isspace(*next)) |
41 | 0 | strbuf_addch(arg, *next++); |
42 | 0 | } |
43 | | |
44 | 0 | return next; |
45 | 0 | } |
46 | | |
47 | | /* |
48 | | * Parse the reference name immediately after "command SP". If not |
49 | | * -z, then handle C-quoting. Return a pointer to a newly allocated |
50 | | * string containing the name of the reference, or NULL if there was |
51 | | * an error. Update *next to point at the character that terminates |
52 | | * the argument. Die if C-quoting is malformed or the reference name |
53 | | * is invalid. |
54 | | */ |
55 | | static char *parse_refname(const char **next) |
56 | 0 | { |
57 | 0 | struct strbuf ref = STRBUF_INIT; |
58 | |
|
59 | 0 | if (line_termination) { |
60 | | /* Without -z, use the next argument */ |
61 | 0 | *next = parse_arg(*next, &ref); |
62 | 0 | } else { |
63 | | /* With -z, use everything up to the next NUL */ |
64 | 0 | strbuf_addstr(&ref, *next); |
65 | 0 | *next += ref.len; |
66 | 0 | } |
67 | |
|
68 | 0 | if (!ref.len) { |
69 | 0 | strbuf_release(&ref); |
70 | 0 | return NULL; |
71 | 0 | } |
72 | | |
73 | 0 | if (check_refname_format(ref.buf, REFNAME_ALLOW_ONELEVEL)) |
74 | 0 | die("invalid ref format: %s", ref.buf); |
75 | | |
76 | 0 | return strbuf_detach(&ref, NULL); |
77 | 0 | } |
78 | | |
79 | | /* |
80 | | * Wrapper around parse_refname which skips the next delimiter. |
81 | | */ |
82 | | static char *parse_next_refname(const char **next) |
83 | 0 | { |
84 | 0 | if (line_termination) { |
85 | | /* Without -z, consume SP and use next argument */ |
86 | 0 | if (!**next || **next == line_termination) |
87 | 0 | return NULL; |
88 | 0 | if (**next != ' ') |
89 | 0 | die("expected SP but got: %s", *next); |
90 | 0 | } else { |
91 | | /* With -z, read the next NUL-terminated line */ |
92 | 0 | if (**next) |
93 | 0 | return NULL; |
94 | 0 | } |
95 | | /* Skip the delimiter */ |
96 | 0 | (*next)++; |
97 | |
|
98 | 0 | return parse_refname(next); |
99 | 0 | } |
100 | | |
101 | | /* |
102 | | * Wrapper around parse_arg which skips the next delimiter. |
103 | | */ |
104 | | static char *parse_next_arg(const char **next) |
105 | 0 | { |
106 | 0 | struct strbuf arg = STRBUF_INIT; |
107 | |
|
108 | 0 | if (line_termination) { |
109 | | /* Without -z, consume SP and use next argument */ |
110 | 0 | if (!**next || **next == line_termination) |
111 | 0 | return NULL; |
112 | 0 | if (**next != ' ') |
113 | 0 | die("expected SP but got: %s", *next); |
114 | 0 | } else { |
115 | | /* With -z, read the next NUL-terminated line */ |
116 | 0 | if (**next) |
117 | 0 | return NULL; |
118 | 0 | } |
119 | | /* Skip the delimiter */ |
120 | 0 | (*next)++; |
121 | |
|
122 | 0 | if (line_termination) { |
123 | | /* Without -z, use the next argument */ |
124 | 0 | *next = parse_arg(*next, &arg); |
125 | 0 | } else { |
126 | | /* With -z, use everything up to the next NUL */ |
127 | 0 | strbuf_addstr(&arg, *next); |
128 | 0 | *next += arg.len; |
129 | 0 | } |
130 | |
|
131 | 0 | if (arg.len) |
132 | 0 | return strbuf_detach(&arg, NULL); |
133 | | |
134 | 0 | strbuf_release(&arg); |
135 | 0 | return NULL; |
136 | 0 | } |
137 | | |
138 | | /* |
139 | | * The value being parsed is <old-oid> (as opposed to <new-oid>; the |
140 | | * difference affects which error messages are generated): |
141 | | */ |
142 | 0 | #define PARSE_SHA1_OLD 0x01 |
143 | | |
144 | | /* |
145 | | * For backwards compatibility, accept an empty string for update's |
146 | | * <new-oid> in binary mode to be equivalent to specifying zeros. |
147 | | */ |
148 | 0 | #define PARSE_SHA1_ALLOW_EMPTY 0x02 |
149 | | |
150 | | /* |
151 | | * Parse an argument separator followed by the next argument, if any. |
152 | | * If there is an argument, convert it to a SHA-1, write it to sha1, |
153 | | * set *next to point at the character terminating the argument, and |
154 | | * return 0. If there is no argument at all (not even the empty |
155 | | * string), return 1 and leave *next unchanged. If the value is |
156 | | * provided but cannot be converted to a SHA-1, die. flags can |
157 | | * include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY. |
158 | | */ |
159 | | static int parse_next_oid(const char **next, const char *end, |
160 | | struct object_id *oid, |
161 | | const char *command, const char *refname, |
162 | | int flags) |
163 | 0 | { |
164 | 0 | struct strbuf arg = STRBUF_INIT; |
165 | 0 | int ret = 0; |
166 | |
|
167 | 0 | if (*next == end) |
168 | 0 | goto eof; |
169 | | |
170 | 0 | if (line_termination) { |
171 | | /* Without -z, consume SP and use next argument */ |
172 | 0 | if (!**next || **next == line_termination) |
173 | 0 | return 1; |
174 | 0 | if (**next != ' ') |
175 | 0 | die("%s %s: expected SP but got: %s", |
176 | 0 | command, refname, *next); |
177 | 0 | (*next)++; |
178 | 0 | *next = parse_arg(*next, &arg); |
179 | 0 | if (arg.len) { |
180 | 0 | if (repo_get_oid(the_repository, arg.buf, oid)) |
181 | 0 | goto invalid; |
182 | 0 | } else { |
183 | | /* Without -z, an empty value means all zeros: */ |
184 | 0 | oidclr(oid, the_repository->hash_algo); |
185 | 0 | } |
186 | 0 | } else { |
187 | | /* With -z, read the next NUL-terminated line */ |
188 | 0 | if (**next) |
189 | 0 | die("%s %s: expected NUL but got: %s", |
190 | 0 | command, refname, *next); |
191 | 0 | (*next)++; |
192 | 0 | if (*next == end) |
193 | 0 | goto eof; |
194 | 0 | strbuf_addstr(&arg, *next); |
195 | 0 | *next += arg.len; |
196 | |
|
197 | 0 | if (arg.len) { |
198 | 0 | if (repo_get_oid(the_repository, arg.buf, oid)) |
199 | 0 | goto invalid; |
200 | 0 | } else if (flags & PARSE_SHA1_ALLOW_EMPTY) { |
201 | | /* With -z, treat an empty value as all zeros: */ |
202 | 0 | warning("%s %s: missing <new-oid>, treating as zero", |
203 | 0 | command, refname); |
204 | 0 | oidclr(oid, the_repository->hash_algo); |
205 | 0 | } else { |
206 | | /* |
207 | | * With -z, an empty non-required value means |
208 | | * unspecified: |
209 | | */ |
210 | 0 | ret = 1; |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | 0 | strbuf_release(&arg); |
215 | |
|
216 | 0 | return ret; |
217 | | |
218 | 0 | invalid: |
219 | 0 | die(flags & PARSE_SHA1_OLD ? |
220 | 0 | "%s %s: invalid <old-oid>: %s" : |
221 | 0 | "%s %s: invalid <new-oid>: %s", |
222 | 0 | command, refname, arg.buf); |
223 | | |
224 | 0 | eof: |
225 | 0 | die(flags & PARSE_SHA1_OLD ? |
226 | 0 | "%s %s: unexpected end of input when reading <old-oid>" : |
227 | 0 | "%s %s: unexpected end of input when reading <new-oid>", |
228 | 0 | command, refname); |
229 | 0 | } |
230 | | |
231 | | |
232 | | /* |
233 | | * The following five parse_cmd_*() functions parse the corresponding |
234 | | * command. In each case, next points at the character following the |
235 | | * command name and the following space. They each return a pointer |
236 | | * to the character terminating the command, and die with an |
237 | | * explanatory message if there are any parsing problems. All of |
238 | | * these functions handle either text or binary format input, |
239 | | * depending on how line_termination is set. |
240 | | */ |
241 | | |
242 | | static void parse_cmd_update(struct ref_transaction *transaction, |
243 | | const char *next, const char *end) |
244 | 0 | { |
245 | 0 | struct strbuf err = STRBUF_INIT; |
246 | 0 | char *refname; |
247 | 0 | struct object_id new_oid, old_oid; |
248 | 0 | int have_old; |
249 | |
|
250 | 0 | refname = parse_refname(&next); |
251 | 0 | if (!refname) |
252 | 0 | die("update: missing <ref>"); |
253 | | |
254 | 0 | if (parse_next_oid(&next, end, &new_oid, "update", refname, |
255 | 0 | PARSE_SHA1_ALLOW_EMPTY)) |
256 | 0 | die("update %s: missing <new-oid>", refname); |
257 | | |
258 | 0 | have_old = !parse_next_oid(&next, end, &old_oid, "update", refname, |
259 | 0 | PARSE_SHA1_OLD); |
260 | |
|
261 | 0 | if (*next != line_termination) |
262 | 0 | die("update %s: extra input: %s", refname, next); |
263 | | |
264 | 0 | if (ref_transaction_update(transaction, refname, |
265 | 0 | &new_oid, have_old ? &old_oid : NULL, |
266 | 0 | NULL, NULL, |
267 | 0 | update_flags | create_reflog_flag, |
268 | 0 | msg, &err)) |
269 | 0 | die("%s", err.buf); |
270 | | |
271 | 0 | update_flags = default_flags; |
272 | 0 | free(refname); |
273 | 0 | strbuf_release(&err); |
274 | 0 | } |
275 | | |
276 | | static void parse_cmd_symref_update(struct ref_transaction *transaction, |
277 | | const char *next, const char *end UNUSED) |
278 | 0 | { |
279 | 0 | char *refname, *new_target, *old_arg; |
280 | 0 | char *old_target = NULL; |
281 | 0 | struct strbuf err = STRBUF_INIT; |
282 | 0 | struct object_id old_oid; |
283 | 0 | int have_old_oid = 0; |
284 | |
|
285 | 0 | refname = parse_refname(&next); |
286 | 0 | if (!refname) |
287 | 0 | die("symref-update: missing <ref>"); |
288 | | |
289 | 0 | new_target = parse_next_refname(&next); |
290 | 0 | if (!new_target) |
291 | 0 | die("symref-update %s: missing <new-target>", refname); |
292 | | |
293 | 0 | old_arg = parse_next_arg(&next); |
294 | 0 | if (old_arg) { |
295 | 0 | old_target = parse_next_arg(&next); |
296 | 0 | if (!old_target) |
297 | 0 | die("symref-update %s: expected old value", refname); |
298 | | |
299 | 0 | if (!strcmp(old_arg, "oid")) { |
300 | 0 | if (repo_get_oid(the_repository, old_target, &old_oid)) |
301 | 0 | die("symref-update %s: invalid oid: %s", refname, old_target); |
302 | | |
303 | 0 | have_old_oid = 1; |
304 | 0 | } else if (!strcmp(old_arg, "ref")) { |
305 | 0 | if (check_refname_format(old_target, REFNAME_ALLOW_ONELEVEL)) |
306 | 0 | die("symref-update %s: invalid ref: %s", refname, old_target); |
307 | 0 | } else { |
308 | 0 | die("symref-update %s: invalid arg '%s' for old value", refname, old_arg); |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | 0 | if (*next != line_termination) |
313 | 0 | die("symref-update %s: extra input: %s", refname, next); |
314 | | |
315 | 0 | if (ref_transaction_update(transaction, refname, NULL, |
316 | 0 | have_old_oid ? &old_oid : NULL, |
317 | 0 | new_target, |
318 | 0 | have_old_oid ? NULL : old_target, |
319 | 0 | update_flags | create_reflog_flag, |
320 | 0 | msg, &err)) |
321 | 0 | die("%s", err.buf); |
322 | | |
323 | 0 | update_flags = default_flags; |
324 | 0 | free(refname); |
325 | 0 | free(old_arg); |
326 | 0 | free(old_target); |
327 | 0 | free(new_target); |
328 | 0 | strbuf_release(&err); |
329 | 0 | } |
330 | | |
331 | | static void parse_cmd_create(struct ref_transaction *transaction, |
332 | | const char *next, const char *end) |
333 | 0 | { |
334 | 0 | struct strbuf err = STRBUF_INIT; |
335 | 0 | char *refname; |
336 | 0 | struct object_id new_oid; |
337 | |
|
338 | 0 | refname = parse_refname(&next); |
339 | 0 | if (!refname) |
340 | 0 | die("create: missing <ref>"); |
341 | | |
342 | 0 | if (parse_next_oid(&next, end, &new_oid, "create", refname, 0)) |
343 | 0 | die("create %s: missing <new-oid>", refname); |
344 | | |
345 | 0 | if (is_null_oid(&new_oid)) |
346 | 0 | die("create %s: zero <new-oid>", refname); |
347 | | |
348 | 0 | if (*next != line_termination) |
349 | 0 | die("create %s: extra input: %s", refname, next); |
350 | | |
351 | 0 | if (ref_transaction_create(transaction, refname, &new_oid, NULL, |
352 | 0 | update_flags | create_reflog_flag, |
353 | 0 | msg, &err)) |
354 | 0 | die("%s", err.buf); |
355 | | |
356 | 0 | update_flags = default_flags; |
357 | 0 | free(refname); |
358 | 0 | strbuf_release(&err); |
359 | 0 | } |
360 | | |
361 | | |
362 | | static void parse_cmd_symref_create(struct ref_transaction *transaction, |
363 | | const char *next, const char *end UNUSED) |
364 | 0 | { |
365 | 0 | struct strbuf err = STRBUF_INIT; |
366 | 0 | char *refname, *new_target; |
367 | |
|
368 | 0 | refname = parse_refname(&next); |
369 | 0 | if (!refname) |
370 | 0 | die("symref-create: missing <ref>"); |
371 | | |
372 | 0 | new_target = parse_next_refname(&next); |
373 | 0 | if (!new_target) |
374 | 0 | die("symref-create %s: missing <new-target>", refname); |
375 | | |
376 | 0 | if (*next != line_termination) |
377 | 0 | die("symref-create %s: extra input: %s", refname, next); |
378 | | |
379 | 0 | if (ref_transaction_create(transaction, refname, NULL, new_target, |
380 | 0 | update_flags | create_reflog_flag, |
381 | 0 | msg, &err)) |
382 | 0 | die("%s", err.buf); |
383 | | |
384 | 0 | update_flags = default_flags; |
385 | 0 | free(refname); |
386 | 0 | free(new_target); |
387 | 0 | strbuf_release(&err); |
388 | 0 | } |
389 | | |
390 | | static void parse_cmd_delete(struct ref_transaction *transaction, |
391 | | const char *next, const char *end) |
392 | 0 | { |
393 | 0 | struct strbuf err = STRBUF_INIT; |
394 | 0 | char *refname; |
395 | 0 | struct object_id old_oid; |
396 | 0 | int have_old; |
397 | |
|
398 | 0 | refname = parse_refname(&next); |
399 | 0 | if (!refname) |
400 | 0 | die("delete: missing <ref>"); |
401 | | |
402 | 0 | if (parse_next_oid(&next, end, &old_oid, "delete", refname, |
403 | 0 | PARSE_SHA1_OLD)) { |
404 | 0 | have_old = 0; |
405 | 0 | } else { |
406 | 0 | if (is_null_oid(&old_oid)) |
407 | 0 | die("delete %s: zero <old-oid>", refname); |
408 | 0 | have_old = 1; |
409 | 0 | } |
410 | | |
411 | 0 | if (*next != line_termination) |
412 | 0 | die("delete %s: extra input: %s", refname, next); |
413 | | |
414 | 0 | if (ref_transaction_delete(transaction, refname, |
415 | 0 | have_old ? &old_oid : NULL, |
416 | 0 | NULL, update_flags, msg, &err)) |
417 | 0 | die("%s", err.buf); |
418 | | |
419 | 0 | update_flags = default_flags; |
420 | 0 | free(refname); |
421 | 0 | strbuf_release(&err); |
422 | 0 | } |
423 | | |
424 | | |
425 | | static void parse_cmd_symref_delete(struct ref_transaction *transaction, |
426 | | const char *next, const char *end UNUSED) |
427 | 0 | { |
428 | 0 | struct strbuf err = STRBUF_INIT; |
429 | 0 | char *refname, *old_target; |
430 | |
|
431 | 0 | if (!(update_flags & REF_NO_DEREF)) |
432 | 0 | die("symref-delete: cannot operate with deref mode"); |
433 | | |
434 | 0 | refname = parse_refname(&next); |
435 | 0 | if (!refname) |
436 | 0 | die("symref-delete: missing <ref>"); |
437 | | |
438 | 0 | old_target = parse_next_refname(&next); |
439 | |
|
440 | 0 | if (*next != line_termination) |
441 | 0 | die("symref-delete %s: extra input: %s", refname, next); |
442 | | |
443 | 0 | if (ref_transaction_delete(transaction, refname, NULL, |
444 | 0 | old_target, update_flags, msg, &err)) |
445 | 0 | die("%s", err.buf); |
446 | | |
447 | 0 | update_flags = default_flags; |
448 | 0 | free(refname); |
449 | 0 | free(old_target); |
450 | 0 | strbuf_release(&err); |
451 | 0 | } |
452 | | |
453 | | |
454 | | static void parse_cmd_verify(struct ref_transaction *transaction, |
455 | | const char *next, const char *end) |
456 | 0 | { |
457 | 0 | struct strbuf err = STRBUF_INIT; |
458 | 0 | char *refname; |
459 | 0 | struct object_id old_oid; |
460 | |
|
461 | 0 | refname = parse_refname(&next); |
462 | 0 | if (!refname) |
463 | 0 | die("verify: missing <ref>"); |
464 | | |
465 | 0 | if (parse_next_oid(&next, end, &old_oid, "verify", refname, |
466 | 0 | PARSE_SHA1_OLD)) |
467 | 0 | oidclr(&old_oid, the_repository->hash_algo); |
468 | |
|
469 | 0 | if (*next != line_termination) |
470 | 0 | die("verify %s: extra input: %s", refname, next); |
471 | | |
472 | 0 | if (ref_transaction_verify(transaction, refname, &old_oid, |
473 | 0 | NULL, update_flags, &err)) |
474 | 0 | die("%s", err.buf); |
475 | | |
476 | 0 | update_flags = default_flags; |
477 | 0 | free(refname); |
478 | 0 | strbuf_release(&err); |
479 | 0 | } |
480 | | |
481 | | static void parse_cmd_symref_verify(struct ref_transaction *transaction, |
482 | | const char *next, const char *end UNUSED) |
483 | 0 | { |
484 | 0 | struct strbuf err = STRBUF_INIT; |
485 | 0 | struct object_id old_oid; |
486 | 0 | char *refname, *old_target; |
487 | |
|
488 | 0 | if (!(update_flags & REF_NO_DEREF)) |
489 | 0 | die("symref-verify: cannot operate with deref mode"); |
490 | | |
491 | 0 | refname = parse_refname(&next); |
492 | 0 | if (!refname) |
493 | 0 | 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 | 0 | old_target = parse_next_refname(&next); |
500 | 0 | if (!old_target) |
501 | 0 | oidcpy(&old_oid, null_oid()); |
502 | |
|
503 | 0 | if (*next != line_termination) |
504 | 0 | die("symref-verify %s: extra input: %s", refname, next); |
505 | | |
506 | 0 | if (ref_transaction_verify(transaction, refname, |
507 | 0 | old_target ? NULL : &old_oid, |
508 | 0 | old_target, update_flags, &err)) |
509 | 0 | die("%s", err.buf); |
510 | | |
511 | 0 | update_flags = default_flags; |
512 | 0 | free(refname); |
513 | 0 | free(old_target); |
514 | 0 | strbuf_release(&err); |
515 | 0 | } |
516 | | |
517 | | static void report_ok(const char *command) |
518 | 0 | { |
519 | 0 | fprintf(stdout, "%s: ok\n", command); |
520 | 0 | fflush(stdout); |
521 | 0 | } |
522 | | |
523 | | static void parse_cmd_option(struct ref_transaction *transaction UNUSED, |
524 | | const char *next, const char *end UNUSED) |
525 | 0 | { |
526 | 0 | const char *rest; |
527 | 0 | if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination) |
528 | 0 | update_flags |= REF_NO_DEREF; |
529 | 0 | else |
530 | 0 | die("option unknown: %s", next); |
531 | 0 | } |
532 | | |
533 | | static void parse_cmd_start(struct ref_transaction *transaction UNUSED, |
534 | | const char *next, const char *end UNUSED) |
535 | 0 | { |
536 | 0 | if (*next != line_termination) |
537 | 0 | die("start: extra input: %s", next); |
538 | 0 | report_ok("start"); |
539 | 0 | } |
540 | | |
541 | | static void parse_cmd_prepare(struct ref_transaction *transaction, |
542 | | const char *next, const char *end UNUSED) |
543 | 0 | { |
544 | 0 | struct strbuf error = STRBUF_INIT; |
545 | 0 | if (*next != line_termination) |
546 | 0 | die("prepare: extra input: %s", next); |
547 | 0 | if (ref_transaction_prepare(transaction, &error)) |
548 | 0 | die("prepare: %s", error.buf); |
549 | 0 | report_ok("prepare"); |
550 | 0 | } |
551 | | |
552 | | static void parse_cmd_abort(struct ref_transaction *transaction, |
553 | | const char *next, const char *end UNUSED) |
554 | 0 | { |
555 | 0 | struct strbuf error = STRBUF_INIT; |
556 | 0 | if (*next != line_termination) |
557 | 0 | die("abort: extra input: %s", next); |
558 | 0 | if (ref_transaction_abort(transaction, &error)) |
559 | 0 | die("abort: %s", error.buf); |
560 | 0 | report_ok("abort"); |
561 | 0 | } |
562 | | |
563 | | static void parse_cmd_commit(struct ref_transaction *transaction, |
564 | | const char *next, const char *end UNUSED) |
565 | 0 | { |
566 | 0 | struct strbuf error = STRBUF_INIT; |
567 | 0 | if (*next != line_termination) |
568 | 0 | die("commit: extra input: %s", next); |
569 | 0 | if (ref_transaction_commit(transaction, &error)) |
570 | 0 | die("commit: %s", error.buf); |
571 | 0 | report_ok("commit"); |
572 | 0 | ref_transaction_free(transaction); |
573 | 0 | } |
574 | | |
575 | | enum update_refs_state { |
576 | | /* Non-transactional state open for updates. */ |
577 | | UPDATE_REFS_OPEN, |
578 | | /* A transaction has been started. */ |
579 | | UPDATE_REFS_STARTED, |
580 | | /* References are locked and ready for commit */ |
581 | | UPDATE_REFS_PREPARED, |
582 | | /* Transaction has been committed or closed. */ |
583 | | UPDATE_REFS_CLOSED, |
584 | | }; |
585 | | |
586 | | static const struct parse_cmd { |
587 | | const char *prefix; |
588 | | void (*fn)(struct ref_transaction *, const char *, const char *); |
589 | | unsigned args; |
590 | | enum update_refs_state state; |
591 | | } command[] = { |
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 }, |
605 | | }; |
606 | | |
607 | | static void update_refs_stdin(void) |
608 | 0 | { |
609 | 0 | struct strbuf input = STRBUF_INIT, err = STRBUF_INIT; |
610 | 0 | enum update_refs_state state = UPDATE_REFS_OPEN; |
611 | 0 | struct ref_transaction *transaction; |
612 | 0 | int i, j; |
613 | |
|
614 | 0 | transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), |
615 | 0 | &err); |
616 | 0 | if (!transaction) |
617 | 0 | die("%s", err.buf); |
618 | | |
619 | | /* Read each line dispatch its command */ |
620 | 0 | while (!strbuf_getwholeline(&input, stdin, line_termination)) { |
621 | 0 | const struct parse_cmd *cmd = NULL; |
622 | |
|
623 | 0 | if (*input.buf == line_termination) |
624 | 0 | die("empty command in input"); |
625 | 0 | else if (isspace(*input.buf)) |
626 | 0 | die("whitespace before command: %s", input.buf); |
627 | | |
628 | 0 | for (i = 0; i < ARRAY_SIZE(command); i++) { |
629 | 0 | const char *prefix = command[i].prefix; |
630 | 0 | char c; |
631 | |
|
632 | 0 | if (!starts_with(input.buf, prefix)) |
633 | 0 | continue; |
634 | | |
635 | | /* |
636 | | * If the command has arguments, verify that it's |
637 | | * followed by a space. Otherwise, it shall be followed |
638 | | * by a line terminator. |
639 | | */ |
640 | 0 | c = command[i].args ? ' ' : line_termination; |
641 | 0 | if (input.buf[strlen(prefix)] != c) |
642 | 0 | continue; |
643 | | |
644 | 0 | cmd = &command[i]; |
645 | 0 | break; |
646 | 0 | } |
647 | 0 | if (!cmd) |
648 | 0 | die("unknown command: %s", input.buf); |
649 | | |
650 | | /* |
651 | | * Read additional arguments if NUL-terminated. Do not raise an |
652 | | * error in case there is an early EOF to let the command |
653 | | * handle missing arguments with a proper error message. |
654 | | */ |
655 | 0 | for (j = 1; line_termination == '\0' && j < cmd->args; j++) |
656 | 0 | if (strbuf_appendwholeline(&input, stdin, line_termination)) |
657 | 0 | break; |
658 | |
|
659 | 0 | switch (state) { |
660 | 0 | case UPDATE_REFS_OPEN: |
661 | 0 | case UPDATE_REFS_STARTED: |
662 | 0 | if (state == UPDATE_REFS_STARTED && cmd->state == UPDATE_REFS_STARTED) |
663 | 0 | die("cannot restart ongoing transaction"); |
664 | | /* Do not downgrade a transaction to a non-transaction. */ |
665 | 0 | if (cmd->state >= state) |
666 | 0 | state = cmd->state; |
667 | 0 | break; |
668 | 0 | case UPDATE_REFS_PREPARED: |
669 | 0 | if (cmd->state != UPDATE_REFS_CLOSED) |
670 | 0 | die("prepared transactions can only be closed"); |
671 | 0 | state = cmd->state; |
672 | 0 | break; |
673 | 0 | case UPDATE_REFS_CLOSED: |
674 | 0 | if (cmd->state != UPDATE_REFS_STARTED) |
675 | 0 | die("transaction is closed"); |
676 | | |
677 | | /* |
678 | | * Open a new transaction if we're currently closed and |
679 | | * get a "start". |
680 | | */ |
681 | 0 | state = cmd->state; |
682 | 0 | transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), |
683 | 0 | &err); |
684 | 0 | if (!transaction) |
685 | 0 | die("%s", err.buf); |
686 | | |
687 | 0 | break; |
688 | 0 | } |
689 | | |
690 | 0 | cmd->fn(transaction, input.buf + strlen(cmd->prefix) + !!cmd->args, |
691 | 0 | input.buf + input.len); |
692 | 0 | } |
693 | | |
694 | 0 | switch (state) { |
695 | 0 | case UPDATE_REFS_OPEN: |
696 | | /* Commit by default if no transaction was requested. */ |
697 | 0 | if (ref_transaction_commit(transaction, &err)) |
698 | 0 | die("%s", err.buf); |
699 | 0 | ref_transaction_free(transaction); |
700 | 0 | break; |
701 | 0 | case UPDATE_REFS_STARTED: |
702 | 0 | case UPDATE_REFS_PREPARED: |
703 | | /* If using a transaction, we want to abort it. */ |
704 | 0 | if (ref_transaction_abort(transaction, &err)) |
705 | 0 | die("%s", err.buf); |
706 | 0 | break; |
707 | 0 | case UPDATE_REFS_CLOSED: |
708 | | /* Otherwise no need to do anything, the transaction was closed already. */ |
709 | 0 | break; |
710 | 0 | } |
711 | | |
712 | 0 | strbuf_release(&err); |
713 | 0 | strbuf_release(&input); |
714 | 0 | } |
715 | | |
716 | | int cmd_update_ref(int argc, const char **argv, const char *prefix) |
717 | 0 | { |
718 | 0 | const char *refname, *oldval; |
719 | 0 | struct object_id oid, oldoid; |
720 | 0 | int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0; |
721 | 0 | int create_reflog = 0; |
722 | 0 | struct option options[] = { |
723 | 0 | OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")), |
724 | 0 | OPT_BOOL('d', NULL, &delete, N_("delete the reference")), |
725 | 0 | OPT_BOOL( 0 , "no-deref", &no_deref, |
726 | 0 | N_("update <refname> not the one it points to")), |
727 | 0 | OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")), |
728 | 0 | OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")), |
729 | 0 | OPT_BOOL( 0 , "create-reflog", &create_reflog, N_("create a reflog")), |
730 | 0 | OPT_END(), |
731 | 0 | }; |
732 | |
|
733 | 0 | git_config(git_default_config, NULL); |
734 | 0 | argc = parse_options(argc, argv, prefix, options, git_update_ref_usage, |
735 | 0 | 0); |
736 | 0 | if (msg && !*msg) |
737 | 0 | die("Refusing to perform update with empty message."); |
738 | | |
739 | 0 | create_reflog_flag = create_reflog ? REF_FORCE_CREATE_REFLOG : 0; |
740 | |
|
741 | 0 | if (no_deref) { |
742 | 0 | default_flags = REF_NO_DEREF; |
743 | 0 | update_flags = default_flags; |
744 | 0 | } |
745 | |
|
746 | 0 | if (read_stdin) { |
747 | 0 | if (delete || argc > 0) |
748 | 0 | usage_with_options(git_update_ref_usage, options); |
749 | 0 | if (end_null) |
750 | 0 | line_termination = '\0'; |
751 | 0 | update_refs_stdin(); |
752 | 0 | return 0; |
753 | 0 | } |
754 | | |
755 | 0 | if (end_null) |
756 | 0 | usage_with_options(git_update_ref_usage, options); |
757 | | |
758 | 0 | if (delete) { |
759 | 0 | if (argc < 1 || argc > 2) |
760 | 0 | usage_with_options(git_update_ref_usage, options); |
761 | 0 | refname = argv[0]; |
762 | 0 | oldval = argv[1]; |
763 | 0 | } else { |
764 | 0 | const char *value; |
765 | 0 | if (argc < 2 || argc > 3) |
766 | 0 | usage_with_options(git_update_ref_usage, options); |
767 | 0 | refname = argv[0]; |
768 | 0 | value = argv[1]; |
769 | 0 | oldval = argv[2]; |
770 | 0 | if (repo_get_oid(the_repository, value, &oid)) |
771 | 0 | die("%s: not a valid SHA1", value); |
772 | 0 | } |
773 | | |
774 | 0 | if (oldval) { |
775 | 0 | if (!*oldval) |
776 | | /* |
777 | | * The empty string implies that the reference |
778 | | * must not already exist: |
779 | | */ |
780 | 0 | oidclr(&oldoid, the_repository->hash_algo); |
781 | 0 | else if (repo_get_oid(the_repository, oldval, &oldoid)) |
782 | 0 | die("%s: not a valid old SHA1", oldval); |
783 | 0 | } |
784 | | |
785 | 0 | if (delete) |
786 | | /* |
787 | | * For purposes of backwards compatibility, we treat |
788 | | * NULL_SHA1 as "don't care" here: |
789 | | */ |
790 | 0 | return refs_delete_ref(get_main_ref_store(the_repository), |
791 | 0 | msg, refname, |
792 | 0 | (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL, |
793 | 0 | default_flags); |
794 | 0 | else |
795 | 0 | return refs_update_ref(get_main_ref_store(the_repository), |
796 | 0 | msg, refname, &oid, |
797 | 0 | oldval ? &oldoid : NULL, |
798 | 0 | default_flags | create_reflog_flag, |
799 | 0 | UPDATE_REFS_DIE_ON_ERR); |
800 | 0 | } |