From patchwork Wed Mar 25 09:54:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Steinhardt X-Patchwork-Id: 11457355 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3AC0B92A for ; Wed, 25 Mar 2020 09:54:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 071F12078E for ; Wed, 25 Mar 2020 09:54:26 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=pks.im header.i=@pks.im header.b="ED/D5Rj8"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="gh/7bDQL" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727566AbgCYJyZ (ORCPT ); Wed, 25 Mar 2020 05:54:25 -0400 Received: from out5-smtp.messagingengine.com ([66.111.4.29]:34409 "EHLO out5-smtp.messagingengine.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726043AbgCYJyY (ORCPT ); Wed, 25 Mar 2020 05:54:24 -0400 Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.nyi.internal (Postfix) with ESMTP id 1AD625C0229; Wed, 25 Mar 2020 05:54:23 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute3.internal (MEProxy); Wed, 25 Mar 2020 05:54:23 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pks.im; h=date :from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; s=fm3; bh=Wh+k+Hnb0FDreKuj4sizYeeAjpL /fLJ5sNSAB30OMvM=; b=ED/D5Rj8RNiHxNCkcauO5v3gQD3w4WczmgSkyGTP9gt XCgi1BQoRjQsbGu235dl8Q6UMiGXM6qSz9RBw83w0Bv5mxNvDV4O9AcawLPGkcof OfNXfPAoDzkRuqtF8RbpG7cDmPSZswOuKBfpWA8u1gTaPuf2y+6gJx2yhV4a7VDm ph/BVYDCJt8c1sHpeJ8AfBj1csxszMn1mfgc4dBqLkYQ+1k8GtqiO7VL9nQl1efq 8rBLitXN6qG3uo5qP47CeQW/Tju8zhdXjEbcUG8NVVNV2VY8CgWW4qza3ZBKtaTV zW2UgnygSehJ8NWBgMP7NrbynONdoRho5UFWnPEslMQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; bh=Wh+k+H nb0FDreKuj4sizYeeAjpL/fLJ5sNSAB30OMvM=; b=gh/7bDQLMDuKuuVG5RnGUu W0JxJquc8taH4fz6KiNLaww8IEkfbkyHXG6faH2NRRdv2w/JsTog+gMgPmNVT6XW CTOOCmwqBO5UE0iZqBnFHff2tPlZLc19mSYWjvrB9+EPYpPrONwshsnJ0v1qUAzz PPfKIgvTF4eqIhRe+19jICoUUBz7o4PbhjwWLpmRHreotgjG4hsI0wpHCN4TlEnc kRKqssew4WUA2idDbxVbYC1HFS3c+tTpCjrGibT+nhCOyL1fn7ClPWo9tha/N1oo mjzyruz1Gn0EH0XSuQdf4bJ+91lzms8YCqjN/0Gw+J5G/1bNk3EJrgHkF51RmT6A == X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedugedrudehfedgtdekucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvffukfhfgggtuggjsehgtderredttddvnecuhfhrohhmpefrrghtrhhi tghkucfuthgvihhnhhgrrhguthcuoehpshesphhkshdrihhmqeenucfkphepjeejrddufe drudekhedrjedvnecuvehluhhsthgvrhfuihiivgepjeenucfrrghrrghmpehmrghilhhf rhhomhepphhssehpkhhsrdhimh X-ME-Proxy: Received: from vm-mail.pks.im (x4d0db948.dyn.telefonica.de [77.13.185.72]) by mail.messagingengine.com (Postfix) with ESMTPA id 7A907328005A; Wed, 25 Mar 2020 05:54:22 -0400 (EDT) Received: from localhost (ncase [10.192.0.11]) by vm-mail.pks.im (OpenSMTPD) with ESMTPSA id 95263dc8 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Wed, 25 Mar 2020 09:54:21 +0000 (UTC) Date: Wed, 25 Mar 2020 10:54:22 +0100 From: Patrick Steinhardt To: git Cc: Christian Couder Subject: [PATCH 8/9] update-ref: read commands in a line-wise fashion Message-ID: <1db63f97ec78fad794cec51c5d96b093f7cd2440.1585129843.git.ps@pks.im> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org 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 --- builtin/update-ref.c | 70 ++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 5f97bb0b75..f35e3ca5ae 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -178,8 +178,8 @@ static int parse_next_oid(const char **next, const char *end, * line_termination is set. */ -static const char *parse_cmd_update(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_update(struct ref_transaction *transaction, + const char *next, const char *end) { struct strbuf err = STRBUF_INIT; char *refname; @@ -212,12 +212,10 @@ static const char *parse_cmd_update(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); - - return next; } -static const char *parse_cmd_create(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_create(struct ref_transaction *transaction, + const char *next, const char *end) { struct strbuf err = STRBUF_INIT; char *refname; @@ -247,12 +245,10 @@ static const char *parse_cmd_create(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); - - return next; } -static const char *parse_cmd_delete(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_delete(struct ref_transaction *transaction, + const char *next, const char *end) { struct strbuf err = STRBUF_INIT; char *refname; @@ -286,12 +282,10 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); - - return next; } -static const char *parse_cmd_verify(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_verify(struct ref_transaction *transaction, + const char *next, const char *end) { struct strbuf err = STRBUF_INIT; char *refname; @@ -318,12 +312,10 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); - - return next; } -static const char *parse_cmd_option(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_option(struct ref_transaction *transaction, + const char *next, const char *end) { const char *rest; if (!skip_prefix(next, " ", &next)) @@ -332,60 +324,60 @@ static const char *parse_cmd_option(struct ref_transaction *transaction, update_flags |= REF_NO_DEREF; else die("option unknown: %s", next); - return rest; } static const struct parse_cmd { const char *prefix; - const char *(*fn)(struct ref_transaction *, const char *, const char *); + void (*fn)(struct ref_transaction *, const char *, const char *); + unsigned extra_lines; } commands[] = { - { "update", parse_cmd_update }, - { "create", parse_cmd_create }, - { "delete", parse_cmd_delete }, - { "verify", parse_cmd_verify }, - { "option", parse_cmd_option }, + { "update", parse_cmd_update, 2 }, + { "create", parse_cmd_create, 1 }, + { "delete", parse_cmd_delete, 1 }, + { "verify", parse_cmd_verify, 1 }, + { "option", parse_cmd_option, 0 }, }; static void update_refs_stdin(void) { struct strbuf input = STRBUF_INIT, err = STRBUF_INIT; struct ref_transaction *transaction; - const char *next; - int i; - - if (strbuf_read(&input, 0, 1000) < 0) - die_errno("could not read from stdin"); - next = input.buf; + int i, j; transaction = ref_transaction_begin(&err); if (!transaction) die("%s", err.buf); /* Read each line dispatch its command */ - while (next < input.buf + input.len) { + while (!strbuf_getwholeline(&input, stdin, line_termination)) { const struct parse_cmd *cmd = NULL; - if (*next == line_termination) + if (*input.buf == line_termination) die("empty command in input"); - else if (isspace(*next)) - die("whitespace before command: %s", next); + else if (isspace(*input.buf)) + die("whitespace before command: %s", input.buf); for (i = 0; i < ARRAY_SIZE(commands); i++) { - if (!skip_prefix(next, commands[i].prefix , &next)) + if (!starts_with(input.buf, commands[i].prefix)) continue; cmd = &commands[i]; break; } if (!cmd) - die("unknown command: %s", next); + die("unknown command: %s", input.buf); if (input.buf[strlen(cmd->prefix)] != line_termination && input.buf[strlen(cmd->prefix)] != '\0' && input.buf[strlen(cmd->prefix)] != ' ') die("%s: no separator after command", cmd->prefix); - next = cmd->fn(transaction, next, input.buf + input.len); - next++; + /* Read extra lines if NUL-terminated */ + for (j = 0; line_termination == '\0' && j < cmd->extra_lines; j++) + if (strbuf_appendwholeline(&input, stdin, line_termination)) + break; + + cmd->fn(transaction, input.buf + strlen(cmd->prefix), + input.buf + input.len); } if (ref_transaction_commit(transaction, &err))