@matheusbsbs / matheus-bagliano / commits / 94fd491a54
Fork 0

update-ref: read commands in a line-wise fashion

The git-update-ref(1) supports a `--stdin` mode that allows it to read all reference updates from standard input. This is mainly used to allow for atomic reference updates that are all or nothing, so that either all references will get updated or none. Currently, git-update-ref(1) reads all commands as a single block of up to 1000 characters and only starts processing after stdin gets closed. This is less flexible than one might wish for, as it doesn't really allow for longer-lived transactions and doesn't allow any verification without committing everything. E.g. one may imagine the following exchange: > start < start: ok > update refs/heads/master $NEWOID1 $OLDOID1 > update refs/heads/branch $NEWOID2 $OLDOID2 > prepare < prepare: ok > commit < commit: ok When reading all input as a whole block, the above interactive protocol is obviously impossible to achieve. But by converting the command to read commands linewise, we can make it more interactive than before. Obviously, the linewise interface is only a first step in making git-update-ref(1) work in a more transaction-oriented way. Missing is most importantly support for transactional commands that manage the current transaction. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Apr 2, 2020 at 09:09 UTC 94fd491a54b955990146f63e7283e9813dc85fef
1 file changed +45 -40
builtin/update-ref.c
+45 -40
@@ -178,8 +178,8 @@ static int parse_next_oid(const char **next, const char *end,
178 * depending on how line_termination is set.
179 */
180
181 -static const char *parse_cmd_update(struct ref_transaction *transaction,
182 - const char *next, const char *end)
181 +static void parse_cmd_update(struct ref_transaction *transaction,
182 + const char *next, const char *end)
183 {
184 struct strbuf err = STRBUF_INIT;
185 char *refname;
@@ -209,12 +209,10 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
209 update_flags = default_flags;
210 free(refname);
211 strbuf_release(&err);
212 -
213 - return next;
212 }
213
216 -static const char *parse_cmd_create(struct ref_transaction *transaction,
217 - const char *next, const char *end)
214 +static void parse_cmd_create(struct ref_transaction *transaction,
215 + const char *next, const char *end)
216 {
217 struct strbuf err = STRBUF_INIT;
218 char *refname;
@@ -241,12 +239,10 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
239 update_flags = default_flags;
240 free(refname);
241 strbuf_release(&err);
244 -
245 - return next;
242 }
243
248 -static const char *parse_cmd_delete(struct ref_transaction *transaction,
249 - const char *next, const char *end)
244 +static void parse_cmd_delete(struct ref_transaction *transaction,
245 + const char *next, const char *end)
246 {
247 struct strbuf err = STRBUF_INIT;
248 char *refname;
@@ -277,12 +273,10 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
273 update_flags = default_flags;
274 free(refname);
275 strbuf_release(&err);
280 -
281 - return next;
276 }
277
284 -static const char *parse_cmd_verify(struct ref_transaction *transaction,
285 - const char *next, const char *end)
278 +static void parse_cmd_verify(struct ref_transaction *transaction,
279 + const char *next, const char *end)
280 {
281 struct strbuf err = STRBUF_INIT;
282 char *refname;
@@ -306,71 +300,82 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
300 update_flags = default_flags;
301 free(refname);
302 strbuf_release(&err);
309 -
310 - return next;
303 }
304
313 -static const char *parse_cmd_option(struct ref_transaction *transaction,
314 - const char *next, const char *end)
305 +static void parse_cmd_option(struct ref_transaction *transaction,
306 + const char *next, const char *end)
307 {
308 const char *rest;
309 if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
310 update_flags |= REF_NO_DEREF;
311 else
312 die("option unknown: %s", next);
321 - return rest;
313 }
314
315 static const struct parse_cmd {
316 const char *prefix;
326 - const char *(*fn)(struct ref_transaction *, const char *, const char *);
317 + void (*fn)(struct ref_transaction *, const char *, const char *);
318 + unsigned args;
319 } command[] = {
328 - { "update", parse_cmd_update },
329 - { "create", parse_cmd_create },
330 - { "delete", parse_cmd_delete },
331 - { "verify", parse_cmd_verify },
332 - { "option", parse_cmd_option },
320 + { "update", parse_cmd_update, 3 },
321 + { "create", parse_cmd_create, 2 },
322 + { "delete", parse_cmd_delete, 2 },
323 + { "verify", parse_cmd_verify, 2 },
324 + { "option", parse_cmd_option, 1 },
325 };
326
327 static void update_refs_stdin(void)
328 {
329 struct strbuf input = STRBUF_INIT, err = STRBUF_INIT;
330 struct ref_transaction *transaction;
339 - const char *next;
340 - int i;
341 -
342 - if (strbuf_read(&input, 0, 1000) < 0)
343 - die_errno("could not read from stdin");
344 - next = input.buf;
331 + int i, j;
332
333 transaction = ref_transaction_begin(&err);
334 if (!transaction)
335 die("%s", err.buf);
336
337 /* Read each line dispatch its command */
351 - while (next < input.buf + input.len) {
338 + while (!strbuf_getwholeline(&input, stdin, line_termination)) {
339 const struct parse_cmd *cmd = NULL;
340
354 - if (*next == line_termination)
341 + if (*input.buf == line_termination)
342 die("empty command in input");
356 - else if (isspace(*next))
357 - die("whitespace before command: %s", next);
343 + else if (isspace(*input.buf))
344 + die("whitespace before command: %s", input.buf);
345
346 for (i = 0; i < ARRAY_SIZE(command); i++) {
347 const char *prefix = command[i].prefix;
348 + char c;
349 +
350 + if (!starts_with(input.buf, prefix))
351 + continue;
352
362 - if (!skip_prefix(next, prefix, &next) ||
363 - !skip_prefix(next, " ", &next))
353 + /*
354 + * If the command has arguments, verify that it's
355 + * followed by a space. Otherwise, it shall be followed
356 + * by a line terminator.
357 + */
358 + c = command[i].args ? ' ' : line_termination;
359 + if (input.buf[strlen(prefix)] != c)
360 continue;
361
362 cmd = &command[i];
363 break;
364 }
365 if (!cmd)
370 - die("unknown command: %s", next);
366 + die("unknown command: %s", input.buf);
367 +
368 + /*
369 + * Read additional arguments if NUL-terminated. Do not raise an
370 + * error in case there is an early EOF to let the command
371 + * handle missing arguments with a proper error message.
372 + */
373 + for (j = 1; line_termination == '\0' && j < cmd->args; j++)
374 + if (strbuf_appendwholeline(&input, stdin, line_termination))
375 + break;
376
372 - next = cmd->fn(transaction, next, input.buf + input.len);
373 - next++;
377 + cmd->fn(transaction, input.buf + strlen(cmd->prefix) + !!cmd->args,
378 + input.buf + input.len);
379 }
380
381 if (ref_transaction_commit(transaction, &err))