From: calicomills <jishnuck26@gmail.com>
To: git@vger.kernel.org
Cc: gitster@pobox.com
Subject: [PATCH] help: prompt user to run corrected command on typo
Date: Thu, 18 Jun 2026 07:26:14 -0700 (PDT) [thread overview]
Message-ID: <6a340006.60da1a74.20db39.8f57@mx.google.com> (raw)
From 0dc9e5c4593611b75e7003e8fdbea9370524c05b Mon Sep 17 00:00:00 2001
From: calicomills <jishnuck26@gmail.com>
Date: Thu, 18 Jun 2026 19:47:12 +0530
Subject: [PATCH] help: prompt user to run corrected command on typo
When a user mistypes a git command and there is exactly one similar
command, git currently prints a suggestion but exits, requiring the
user to retype the corrected command manually.
Instead, when stdin and stderr are both connected to a terminal and
there is a single best match, prompt the user with:
Did you mean 'git checkout neo'? [y/N]
The full corrected invocation (command + original arguments) is shown
in the prompt so the user knows exactly what will run. Answering 'y'
re-executes git with the corrected command and all original arguments.
Answering anything else exits as before.
When there are multiple similarly-named commands, or when running
non-interactively (scripts, pipes), the original behaviour of printing
the suggestion list and exiting is preserved.
The help_unknown_cmd() signature is updated to accept the full args
vector so the prompt can include the original arguments alongside the
corrected command name.
Add tests to t9003 covering:
- non-interactive single match: falls back to suggestion list
- non-interactive multiple matches: falls back to suggestion list
- interactive single match, 'y': corrected command runs (TTY prereq)
- interactive single match, 'n': exits cleanly (TTY prereq)
Signed-off-by: calicomills <jishnuck26@gmail.com>
---
builtin/help.c | 2 +-
git.c | 2 +-
help.c | 40 ++++++++++++++++++++++------
help.h | 3 ++-
t/t9003-help-autocorrect.sh | 53 +++++++++++++++++++++++++++++++++++++
5 files changed, 89 insertions(+), 11 deletions(-)
diff --git a/builtin/help.c b/builtin/help.c
index a140339999..b17e61ccc8 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -618,7 +618,7 @@ static char *check_git_cmd(const char *cmd)
}
if (exclude_guides)
- return help_unknown_cmd(cmd);
+ return help_unknown_cmd(cmd, NULL);
return xstrdup(cmd);
}
diff --git a/git.c b/git.c
index 36f08891ef..d379cc85bb 100644
--- a/git.c
+++ b/git.c
@@ -994,7 +994,7 @@ int cmd_main(int argc, const char **argv)
exit(1);
}
if (!done_help) {
- char *assumed = help_unknown_cmd(cmd);
+ char *assumed = help_unknown_cmd(cmd, &args);
strvec_replace(&args, 0, assumed);
free(assumed);
cmd = args.v[0];
diff --git a/help.c b/help.c
index 46241492ce..30f32a7206 100644
--- a/help.c
+++ b/help.c
@@ -641,7 +641,7 @@ static const char bad_interpreter_advice[] =
N_("'%s' appears to be a git command, but we were not\n"
"able to execute it. Maybe git-%s is broken?");
-char *help_unknown_cmd(const char *cmd)
+char *help_unknown_cmd(const char *cmd, const struct strvec *args)
{
struct help_unknown_cmd_config cfg = { 0 };
int i, n, best_similarity = 0;
@@ -762,13 +762,37 @@ char *help_unknown_cmd(const char *cmd)
fprintf_ln(stderr, _("git: '%s' is not a git command. See 'git --help'."), cmd);
if (SIMILAR_ENOUGH(best_similarity)) {
- fprintf_ln(stderr,
- Q_("\nThe most similar command is",
- "\nThe most similar commands are",
- n));
-
- for (i = 0; i < n; i++)
- fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
+ if (n == 1 && isatty(0) && isatty(2)) {
+ char *answer;
+ struct strbuf msg = STRBUF_INIT;
+ struct strbuf full_cmd = STRBUF_INIT;
+ strbuf_addstr(&full_cmd, main_cmds.names[0]->name);
+ if (args) {
+ for (size_t j = 1; j < args->nr; j++) {
+ strbuf_addch(&full_cmd, ' ');
+ strbuf_addstr(&full_cmd, args->v[j]);
+ }
+ }
+ strbuf_addf(&msg, _("\nDid you mean 'git %s'? [y/N] "),
+ full_cmd.buf);
+ strbuf_release(&full_cmd);
+ answer = git_prompt(msg.buf, PROMPT_ECHO);
+ strbuf_release(&msg);
+ if (starts_with(answer, "y") || starts_with(answer, "Y")) {
+ char *assumed = xstrdup(main_cmds.names[0]->name);
+ cmdnames_release(&cfg.aliases);
+ cmdnames_release(&main_cmds);
+ cmdnames_release(&other_cmds);
+ return assumed;
+ }
+ } else {
+ fprintf_ln(stderr,
+ Q_("\nThe most similar command is",
+ "\nThe most similar commands are",
+ n));
+ for (i = 0; i < n; i++)
+ fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
+ }
}
exit(1);
diff --git a/help.h b/help.h
index c54bf0977d..a8c465b3df 100644
--- a/help.h
+++ b/help.h
@@ -32,7 +32,8 @@ void list_all_other_cmds(struct string_list *list);
void list_cmds_by_category(struct string_list *list,
const char *category);
void list_cmds_by_config(struct string_list *list);
-char *help_unknown_cmd(const char *cmd);
+#include "strvec.h"
+char *help_unknown_cmd(const char *cmd, const struct strvec *args);
void load_command_list(const char *prefix,
struct cmdnames *main_cmds,
struct cmdnames *other_cmds);
diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh
index 8da318d2b5..6fe2da1595 100755
--- a/t/t9003-help-autocorrect.sh
+++ b/t/t9003-help-autocorrect.sh
@@ -70,4 +70,57 @@ test_expect_success 'autocorrect works in work tree created from bare repo' '
git -C worktree -c help.autocorrect=immediate status
'
+# Default behaviour (no help.autocorrect set): when there is exactly one
+# similar command but the session is non-interactive, fall back to printing
+# the suggestion list and exiting rather than showing a prompt.
+test_expect_success 'default: single match non-interactive shows suggestion and fails' '
+ test_might_fail git config --unset help.autocorrect &&
+
+ test_must_fail git lfg 2>actual &&
+ grep "most similar command" actual &&
+ grep "lgf" actual
+'
+
+test_expect_success 'default: multiple matches non-interactive shows list and fails' '
+ test_might_fail git config --unset help.autocorrect &&
+
+ test_must_fail git com 2>actual &&
+ grep "most similar commands" actual &&
+ grep "commit" actual
+'
+
+# Interactive prompt tests require a real TTY. On macOS the TTY prereq is
+# skipped due to IO::Pty reliability issues; these tests run on Linux CI.
+test_expect_success TTY 'default: single match interactive, answer y runs command' '
+ git config --unset help.autocorrect &&
+
+ write_script git-typotest <<-\EOF &&
+ echo typotest-ran
+ EOF
+ PATH="$PATH:." export PATH &&
+
+ # Feed "y" to /dev/tty via a wrapper that answers the prompt
+ write_script answer-prompt <<-\EOF &&
+ # Write the answer to the controlling terminal
+ printf "y\n" >/dev/tty
+ exec "$@"
+ EOF
+
+ test_terminal ./answer-prompt git typotest 2>err >out &&
+ grep "typotest-ran" out &&
+ grep "Did you mean" err
+'
+
+test_expect_success TTY 'default: single match interactive, answer n exits cleanly' '
+ git config --unset help.autocorrect &&
+
+ write_script answer-prompt-no <<-\EOF &&
+ printf "n\n" >/dev/tty
+ exec "$@"
+ EOF
+
+ test_must_fail test_terminal ./answer-prompt-no git typotest 2>err &&
+ grep "Did you mean" err
+'
+
test_done
--
2.50.1
next reply other threads:[~2026-06-18 14:26 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-18 14:26 calicomills [this message]
2026-06-18 17:48 ` [PATCH] help: prompt user to run corrected command on typo Justin Tobler
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6a340006.60da1a74.20db39.8f57@mx.google.com \
--to=jishnuck26@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/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.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.