git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: "Phillip Wood via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Derrick Stolee <derrickstolee@github.com>,
	Johannes Schindelin <Johannes.Schindelin@gmx.de>,
	Phillip Wood <phillip.wood123@gmail.com>,
	Phillip Wood <phillip.wood@dunelm.org.uk>,
	Phillip Wood <phillip.wood@dunelm.org.uk>
Subject: [PATCH v2] rebase -i: check labels and refs when parsing todo list
Date: Mon, 20 Feb 2023 14:19:34 +0000	[thread overview]
Message-ID: <pull.1482.v2.git.1676902774366.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1482.git.1676644675638.gitgitgadget@gmail.com>

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Check that the argument to the "label" and "update-ref" commands is a
valid refname when the todo list is parsed rather than waiting until the
command is executed. This means that the user can deal with any errors
at the beginning of the rebase rather than having it stop halfway
through due to a typo in a label name. The "update-ref" command is
changed to reject single level refs as it is all to easy to type
"update-ref branch" which is incorrect rather than "update-ref
refs/heads/branch"

Note that it is not straight forward to check the arguments to "reset"
and "merge" commands as they may be any revision, not just a refname and
we do not have an equivalent of check_refname_format() for revisions.

Helped-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
    rebase -i: check labels and refs when parsing todo list
    
    Hopefully detecting these errors when the user edits the todo list will
    give a better experience.
    
    Thanks to Stolee for his comments on V1, I've updated the patch based on
    those, there are no functional changes but the code is hopefully
    clearer.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1482%2Fphillipwood%2Frebase-check-labels-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1482/phillipwood/rebase-check-labels-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1482

Range-diff vs v1:

 1:  5a31d880064 ! 1:  2cc62fe5986 rebase -i: check labels and refs when parsing todo list
     @@ Commit message
          and "merge" commands as they may be any revision, not just a refname and
          we do not have an equivalent of check_refname_format() for revisions.
      
     +    Helped-by: Derrick Stolee <derrickstolee@github.com>
          Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
      
       ## sequencer.c ##
     @@ sequencer.c: static int is_command(enum todo_command command, const char **bol)
       
      +static int check_label_or_ref_arg(enum todo_command command, const char *arg)
      +{
     -+	int allow_onelevel =
     -+		command == TODO_LABEL ? REFNAME_ALLOW_ONELEVEL : 0;
     ++	switch (command) {
     ++	case TODO_LABEL:
     ++	  /*
     ++	   * '#' is not a valid label as the merge command uses it to
     ++	   * separate merge parents from the commit subject.
     ++	   */
     ++	  if (!strcmp(arg, "#") ||
     ++		    check_refname_format(arg, REFNAME_ALLOW_ONELEVEL))
     ++			return error(_("'%s' is not a valid label"), arg);
     ++		break;
      +
     -+	if ((command == TODO_LABEL && !strcmp(arg, "#")) ||
     -+	    check_refname_format(arg, allow_onelevel)) {
     -+		if (command == TODO_LABEL)
     -+			error(_("'%s' is not a valid label"), arg);
     -+		else if (check_refname_format(arg, REFNAME_ALLOW_ONELEVEL))
     -+			error(_("'%s' is not a valid refname"), arg);
     -+		else
     -+			error(_("update-ref requires a fully qualified refname e.g. refs/heads/%s"),
     -+			      arg);
     -+		return -1;
     ++	case TODO_UPDATE_REF:
     ++		if (check_refname_format(arg, REFNAME_ALLOW_ONELEVEL))
     ++			return error(_("'%s' is not a valid refname"), arg);
     ++		if (check_refname_format(arg, 0))
     ++			return error(_("update-ref requires a fully qualified "
     ++				       "refname e.g. refs/heads/%s"), arg);
     ++		break;
     ++
     ++	default:
     ++		BUG("unexpected todo_command");
      +	}
      +
      +	return 0;
     @@ sequencer.c: static int is_command(enum todo_command command, const char **bol)
       			   const char *buf, const char *bol, char *eol)
       {
      @@ sequencer.c: static int parse_insn_line(struct repository *r, struct todo_item *item,
     - 		return error(_("missing arguments for %s"),
     - 			     command_to_string(item->command));
       
     --	if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
     -+	if (item->command == TODO_LABEL ||
     + 	if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
       	    item->command == TODO_RESET || item->command == TODO_UPDATE_REF) {
      +		int ret = 0;
      +
     -+		item->commit = NULL;
     -+		item->arg_offset = bol - buf;
     -+		item->arg_len = (int)(eol - bol);
     -+		if (item->command != TODO_RESET) {
     + 		item->commit = NULL;
     + 		item->arg_offset = bol - buf;
     + 		item->arg_len = (int)(eol - bol);
     +-		return 0;
     ++		if (item->command == TODO_LABEL ||
     ++		    item->command == TODO_UPDATE_REF) {
      +			saved = *eol;
      +			*eol = '\0';
      +			ret = check_label_or_ref_arg(item->command, bol);
      +			*eol = saved;
      +		}
      +		return ret;
     -+	}
     -+
     -+	if (item->command == TODO_EXEC) {
     - 		item->commit = NULL;
     - 		item->arg_offset = bol - buf;
     - 		item->arg_len = (int)(eol - bol);
     + 	}
     + 
     + 	if (item->command == TODO_FIXUP) {
      
       ## t/t3404-rebase-interactive.sh ##
     +@@ t/t3404-rebase-interactive.sh: test_expect_success '--update-refs: --edit-todo with no update-ref lines' '
     + '
     + 
     + test_expect_success '--update-refs: check failed ref update' '
     ++	test_when_finished "test_might_fail git rebase --abort" &&
     + 	git checkout -B update-refs-error no-conflict-branch &&
     + 	git branch -f base HEAD~4 &&
     + 	git branch -f first HEAD~3 &&
      @@ t/t3404-rebase-interactive.sh: test_expect_success '--update-refs: check failed ref update' '
     - 	tail -n 6 err >err.last &&
     - 	sed -e "s/Rebasing.*Successfully/Successfully/g" -e "s/^\t//g" \
     - 		<err.last >err.trimmed &&
     --	test_cmp expect err.trimmed
     -+	test_cmp expect err.trimmed &&
     -+	git rebase --abort
     -+'
     -+
     + 	test_cmp expect err.trimmed
     + '
     + 
      +test_expect_success 'bad labels and refs rejected when parsing todo list' '
     ++	test_when_finished "test_might_fail git rebase --abort" &&
      +	cat >todo <<-\EOF &&
      +	exec >execed
      +	label #
     @@ t/t3404-rebase-interactive.sh: test_expect_success '--update-refs: check failed
      +	grep "'\'':bad'\'' is not a valid refname" err &&
      +	grep "update-ref requires a fully qualified refname e.g. refs/heads/topic" \
      +		err &&
     -+	test_path_is_missing execed &&
     -+	git rebase --abort
     - '
     - 
     ++	test_path_is_missing execed
     ++'
     ++
       # This must be the last test in this file
     + test_expect_success '$EDITOR and friends are unchanged' '
     + 	test_editor_unchanged


 sequencer.c                   | 39 ++++++++++++++++++++++++++++++++++-
 t/t3404-rebase-interactive.sh | 23 +++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/sequencer.c b/sequencer.c
index 3e4a1972897..33bdc8bca43 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2477,6 +2477,34 @@ static int is_command(enum todo_command command, const char **bol)
 		 (*bol = p));
 }
 
+static int check_label_or_ref_arg(enum todo_command command, const char *arg)
+{
+	switch (command) {
+	case TODO_LABEL:
+	  /*
+	   * '#' is not a valid label as the merge command uses it to
+	   * separate merge parents from the commit subject.
+	   */
+	  if (!strcmp(arg, "#") ||
+		    check_refname_format(arg, REFNAME_ALLOW_ONELEVEL))
+			return error(_("'%s' is not a valid label"), arg);
+		break;
+
+	case TODO_UPDATE_REF:
+		if (check_refname_format(arg, REFNAME_ALLOW_ONELEVEL))
+			return error(_("'%s' is not a valid refname"), arg);
+		if (check_refname_format(arg, 0))
+			return error(_("update-ref requires a fully qualified "
+				       "refname e.g. refs/heads/%s"), arg);
+		break;
+
+	default:
+		BUG("unexpected todo_command");
+	}
+
+	return 0;
+}
+
 static int parse_insn_line(struct repository *r, struct todo_item *item,
 			   const char *buf, const char *bol, char *eol)
 {
@@ -2525,10 +2553,19 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
 
 	if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
 	    item->command == TODO_RESET || item->command == TODO_UPDATE_REF) {
+		int ret = 0;
+
 		item->commit = NULL;
 		item->arg_offset = bol - buf;
 		item->arg_len = (int)(eol - bol);
-		return 0;
+		if (item->command == TODO_LABEL ||
+		    item->command == TODO_UPDATE_REF) {
+			saved = *eol;
+			*eol = '\0';
+			ret = check_label_or_ref_arg(item->command, bol);
+			*eol = saved;
+		}
+		return ret;
 	}
 
 	if (item->command == TODO_FIXUP) {
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 462cefd25df..efeb74ad50a 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -2072,6 +2072,7 @@ test_expect_success '--update-refs: --edit-todo with no update-ref lines' '
 '
 
 test_expect_success '--update-refs: check failed ref update' '
+	test_when_finished "test_might_fail git rebase --abort" &&
 	git checkout -B update-refs-error no-conflict-branch &&
 	git branch -f base HEAD~4 &&
 	git branch -f first HEAD~3 &&
@@ -2123,6 +2124,28 @@ test_expect_success '--update-refs: check failed ref update' '
 	test_cmp expect err.trimmed
 '
 
+test_expect_success 'bad labels and refs rejected when parsing todo list' '
+	test_when_finished "test_might_fail git rebase --abort" &&
+	cat >todo <<-\EOF &&
+	exec >execed
+	label #
+	label :invalid
+	update-ref :bad
+	update-ref topic
+	EOF
+	rm -f execed &&
+	(
+		set_replace_editor todo &&
+		test_must_fail git rebase -i HEAD 2>err
+	) &&
+	grep "'\''#'\'' is not a valid label" err &&
+	grep "'\'':invalid'\'' is not a valid label" err &&
+	grep "'\'':bad'\'' is not a valid refname" err &&
+	grep "update-ref requires a fully qualified refname e.g. refs/heads/topic" \
+		err &&
+	test_path_is_missing execed
+'
+
 # This must be the last test in this file
 test_expect_success '$EDITOR and friends are unchanged' '
 	test_editor_unchanged

base-commit: b1485644f936ee83a995ec24d23f713f4230a1ae
-- 
gitgitgadget

  parent reply	other threads:[~2023-02-20 14:19 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-17 14:37 [PATCH] rebase -i: check labels and refs when parsing todo list Phillip Wood via GitGitGadget
2023-02-17 15:32 ` Derrick Stolee
2023-02-20  9:24   ` Phillip Wood
2023-02-20 10:03     ` Phillip Wood
2023-02-20 14:19 ` Phillip Wood via GitGitGadget [this message]
2023-02-21 14:24   ` [PATCH v2] " Derrick Stolee
2023-02-21 16:29     ` Phillip Wood
2023-02-21 17:26       ` Junio C Hamano
2023-02-21 17:05   ` [PATCH v3] " Phillip Wood via GitGitGadget
2023-02-21 17:07     ` Derrick Stolee

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=pull.1482.v2.git.1676902774366.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=Johannes.Schindelin@gmx.de \
    --cc=derrickstolee@github.com \
    --cc=git@vger.kernel.org \
    --cc=phillip.wood123@gmail.com \
    --cc=phillip.wood@dunelm.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).