Skip to content

Commit 94fd491

Browse files
pks-tgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent de0e0d6 commit 94fd491

File tree

1 file changed

+45
-40
lines changed

1 file changed

+45
-40
lines changed

builtin/update-ref.c

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ static int parse_next_oid(const char **next, const char *end,
178178
* depending on how line_termination is set.
179179
*/
180180

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)
183183
{
184184
struct strbuf err = STRBUF_INIT;
185185
char *refname;
@@ -209,12 +209,10 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
209209
update_flags = default_flags;
210210
free(refname);
211211
strbuf_release(&err);
212-
213-
return next;
214212
}
215213

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)
218216
{
219217
struct strbuf err = STRBUF_INIT;
220218
char *refname;
@@ -241,12 +239,10 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
241239
update_flags = default_flags;
242240
free(refname);
243241
strbuf_release(&err);
244-
245-
return next;
246242
}
247243

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)
250246
{
251247
struct strbuf err = STRBUF_INIT;
252248
char *refname;
@@ -277,12 +273,10 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
277273
update_flags = default_flags;
278274
free(refname);
279275
strbuf_release(&err);
280-
281-
return next;
282276
}
283277

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)
286280
{
287281
struct strbuf err = STRBUF_INIT;
288282
char *refname;
@@ -306,71 +300,82 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
306300
update_flags = default_flags;
307301
free(refname);
308302
strbuf_release(&err);
309-
310-
return next;
311303
}
312304

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)
315307
{
316308
const char *rest;
317309
if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
318310
update_flags |= REF_NO_DEREF;
319311
else
320312
die("option unknown: %s", next);
321-
return rest;
322313
}
323314

324315
static const struct parse_cmd {
325316
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;
327319
} 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 },
333325
};
334326

335327
static void update_refs_stdin(void)
336328
{
337329
struct strbuf input = STRBUF_INIT, err = STRBUF_INIT;
338330
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;
345332

346333
transaction = ref_transaction_begin(&err);
347334
if (!transaction)
348335
die("%s", err.buf);
349336

350337
/* Read each line dispatch its command */
351-
while (next < input.buf + input.len) {
338+
while (!strbuf_getwholeline(&input, stdin, line_termination)) {
352339
const struct parse_cmd *cmd = NULL;
353340

354-
if (*next == line_termination)
341+
if (*input.buf == line_termination)
355342
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);
358345

359346
for (i = 0; i < ARRAY_SIZE(command); i++) {
360347
const char *prefix = command[i].prefix;
348+
char c;
349+
350+
if (!starts_with(input.buf, prefix))
351+
continue;
361352

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)
364360
continue;
365361

366362
cmd = &command[i];
367363
break;
368364
}
369365
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;
371376

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);
374379
}
375380

376381
if (ref_transaction_commit(transaction, &err))

0 commit comments

Comments
 (0)