git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 4/9] update-ref: organize commands in an array

From
Patrick Steinhardt <ps@pks.im>
Date
Mar 30, 2020, 13:46 UTC
Message-ID
<bd8c059fbcd9dc21203dff61e047af66e4239caf.1585575749.git.ps@pks.im>
In-Reply-To
<cover.1585575749.git.ps@pks.im>

We currently manually wire up all commands known to `git-update-ref --stdin`, making it harder than necessary to preprocess arguments after the command is determined. To make this more extensible, let's refactor the code to use an array of known commands instead. While this doesn't add a lot of value now, it is a preparatory step to implement line-wise reading of commands.

As we're going to introduce commands without trailing spaces, this commit also moves whitespace parsing into the respective commands.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/update-ref.c | 73 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 56 insertions(+), 17 deletions(-)
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 2d8f7f0578..559475681e 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -171,11 +171,11 @@ static int parse_next_oid(struct strbuf *input, const char **next,
 /*
  * The following five parse_cmd_*() functions parse the corresponding
  * command.  In each case, next points at the character following the
- * command name and the following space.  They each return a pointer
- * to the character terminating the command, and die with an
- * explanatory message if there are any parsing problems.  All of
- * these functions handle either text or binary format input,
- * depending on how line_termination is set.
+ * command name.  They each return a pointer to the character
+ * terminating the command, and die with an explanatory message if
+ * there are any parsing problems.  All of these functions handle
+ * either text or binary format input, depending on how
+ * line_termination is set.
  */
 
 static const char *parse_cmd_update(struct ref_transaction *transaction,
@@ -186,6 +186,9 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
 	struct object_id new_oid, old_oid;
 	int have_old;
 
+	if (!skip_prefix(next, " ", &next))
+		die("update: missing space after command");
+
 	refname = parse_refname(input, &next);
 	if (!refname)
 		die("update: missing <ref>");
@@ -220,6 +223,9 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
 	char *refname;
 	struct object_id new_oid;
 
+	if (!skip_prefix(next, " ", &next))
+		die("create: missing space after command");
+
 	refname = parse_refname(input, &next);
 	if (!refname)
 		die("create: missing <ref>");
@@ -253,6 +259,9 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
 	struct object_id old_oid;
 	int have_old;
 
+	if (!skip_prefix(next, " ", &next))
+		die("delete: missing space after command");
+
 	refname = parse_refname(input, &next);
 	if (!refname)
 		die("delete: missing <ref>");
@@ -288,6 +297,9 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
 	char *refname;
 	struct object_id old_oid;
 
+	if (!skip_prefix(next, " ", &next))
+		die("verify: missing space after command");
+
 	refname = parse_refname(input, &next);
 	if (!refname)
 		die("verify: missing <ref>");
@@ -310,9 +322,12 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
 	return next;
 }
 
-static const char *parse_cmd_option(struct strbuf *input, const char *next)
+static const char *parse_cmd_option(struct ref_transaction *transaction,
+				    struct strbuf *input, const char *next)
 {
 	const char *rest;
+	if (!skip_prefix(next, " ", &next))
+		die("option: missing space after command");
 	if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
 		update_flags |= REF_NO_DEREF;
 	else
@@ -320,33 +335,57 @@ static const char *parse_cmd_option(struct strbuf *input, const char *next)
 	return rest;
 }
 
+static const struct parse_cmd {
+	const char *prefix;
+	const char *(*fn)(struct ref_transaction *, struct strbuf *, const char *);
+} command[] = {
+	{ "update", parse_cmd_update },
+	{ "create", parse_cmd_create },
+	{ "delete", parse_cmd_delete },
+	{ "verify", parse_cmd_verify },
+	{ "option", parse_cmd_option },
+};
+
 static void update_refs_stdin(struct ref_transaction *transaction)
 {
 	struct strbuf input = STRBUF_INIT;
 	const char *next;
+	int i;
 
 	if (strbuf_read(&input, 0, 1000) < 0)
 		die_errno("could not read from stdin");
 	next = input.buf;
 	/* Read each line dispatch its command */
 	while (next < input.buf + input.len) {
+		const struct parse_cmd *cmd = NULL;
+
 		if (*next == line_termination)
 			die("empty command in input");
 		else if (isspace(*next))
 			die("whitespace before command: %s", next);
-		else if (skip_prefix(next, "update ", &next))
-			next = parse_cmd_update(transaction, &input, next);
-		else if (skip_prefix(next, "create ", &next))
-			next = parse_cmd_create(transaction, &input, next);
-		else if (skip_prefix(next, "delete ", &next))
-			next = parse_cmd_delete(transaction, &input, next);
-		else if (skip_prefix(next, "verify ", &next))
-			next = parse_cmd_verify(transaction, &input, next);
-		else if (skip_prefix(next, "option ", &next))
-			next = parse_cmd_option(&input, next);
-		else
+
+		for (i = 0; i < ARRAY_SIZE(command); i++) {
+			const char *prefix = command[i].prefix;
+
+			if (!skip_prefix(next, prefix, &next))
+				continue;
+
+			/*
+			 * Check that the command is terminated by an argument
+			 * or line terminator and not only a matching prefix.
+			 */
+			if (input.buf[strlen(prefix)] != line_termination &&
+			    input.buf[strlen(prefix)] != '\0' &&
+			    input.buf[strlen(prefix)] != ' ')
+				continue;
+
+			cmd = &command[i];
+			break;
+		}
+		if (!cmd)
 			die("unknown command: %s", next);
 
+		next = cmd->fn(transaction, &input, next);
 		next++;
 	}
 
-- 
2.26.0
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 28 of 48 in “Support for transactions in `git-update-ref --stdin`”
  1. 0/9 Support for transactions in `git-update-ref --stdin`Patrick Steinhardt, Mar 25, 2020
  2. 1/9 refs: fix segfault when aborting empty transactionPatrick Steinhardt, Mar 25, 2020
  3. Junio C HamanoMar 27, 2020
  4. 2/9 git-update-ref.txt: add missing wordPatrick Steinhardt, Mar 25, 2020
  5. 3/9 strbuf: provide function to append whole linesPatrick Steinhardt, Mar 25, 2020
  6. Junio C HamanoMar 27, 2020
  7. Patrick SteinhardtMar 30, 2020
  8. Junio C HamanoMar 30, 2020
  9. 4/9 update-ref: organize commands in an arrayPatrick Steinhardt, Mar 25, 2020
  10. Junio C HamanoMar 27, 2020
  11. Patrick SteinhardtMar 30, 2020
  12. Junio C HamanoMar 30, 2020
  13. Patrick SteinhardtMar 30, 2020
  14. 5/9 update-ref: drop unused argument for `parse_refname`Patrick Steinhardt, Mar 25, 2020
  15. 6/9 update-ref: pass end pointer instead of strbufPatrick Steinhardt, Mar 25, 2020
  16. 7/9 update-ref: move transaction handling into `update_refs_stdin()`Patrick Steinhardt, Mar 25, 2020
  17. Junio C HamanoMar 27, 2020
  18. 8/9 update-ref: read commands in a line-wise fashionPatrick Steinhardt, Mar 25, 2020
  19. Junio C HamanoMar 27, 2020
  20. Patrick SteinhardtMar 30, 2020
  21. Junio C HamanoMar 30, 2020
  22. 9/9 update-ref: implement interactive transaction handlingPatrick Steinhardt, Mar 25, 2020
  23. Junio C HamanoMar 27, 2020
  24. 0/9 Support for transactions in `git-update-ref --stdin`Patrick Steinhardt, Mar 30, 2020
  25. 1/9 refs: fix segfault when aborting empty transactionPatrick Steinhardt, Mar 30, 2020
  26. 2/9 git-update-ref.txt: add missing wordPatrick Steinhardt, Mar 30, 2020
  27. 3/9 strbuf: provide function to append whole linesPatrick Steinhardt, Mar 30, 2020
  28. 4/9 update-ref: organize commands in an arrayPatrick Steinhardt, Mar 30, 2020
  29. 5/9 update-ref: drop unused argument for `parse_refname`Patrick Steinhardt, Mar 30, 2020
  30. 6/9 update-ref: pass end pointer instead of strbufPatrick Steinhardt, Mar 30, 2020
  31. 7/9 update-ref: move transaction handling into `update_refs_stdin()`Patrick Steinhardt, Mar 30, 2020
  32. 8/9 update-ref: read commands in a line-wise fashionPatrick Steinhardt, Mar 30, 2020
  33. 9/9 update-ref: implement interactive transaction handlingPatrick Steinhardt, Mar 30, 2020
  34. 0/9 Support for transactions in `git-update-ref --stdin`Patrick Steinhardt, Apr 2, 2020
  35. 1/9 refs: fix segfault when aborting empty transactionPatrick Steinhardt, Apr 2, 2020
  36. 2/9 git-update-ref.txt: add missing wordPatrick Steinhardt, Apr 2, 2020
  37. 3/9 strbuf: provide function to append whole linesPatrick Steinhardt, Apr 2, 2020
  38. 4/9 update-ref: organize commands in an arrayPatrick Steinhardt, Apr 2, 2020
  39. 5/9 update-ref: drop unused argument for `parse_refname`Patrick Steinhardt, Apr 2, 2020
  40. 6/9 update-ref: pass end pointer instead of strbufPatrick Steinhardt, Apr 2, 2020
  41. 7/9 update-ref: move transaction handling into `update_refs_stdin()`Patrick Steinhardt, Apr 2, 2020
  42. 8/9 update-ref: read commands in a line-wise fashionPatrick Steinhardt, Apr 2, 2020
  43. 9/9 update-ref: implement interactive transaction handlingPatrick Steinhardt, Apr 2, 2020
  44. Phillip WoodApr 3, 2020
  45. Patrick SteinhardtApr 3, 2020
  46. Junio C HamanoApr 3, 2020
  47. Junio C HamanoApr 3, 2020
  48. Patrick SteinhardtApr 6, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.