{"thread":{"id":"14595","subject":"[FYI PATCH] git wrapper: DWIM mistyped commands","startedAt":"2008-07-22T20:01:29Z","lastAt":"2008-07-23T19:04:40Z","messageCount":20,"participants":["Johannes Schindelin","Pierre Habouzit","Alex Riesen","Junio C Hamano","Sverre Rabbelier"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"84353","messageId":"alpine.DEB.1.00.0807222100150.8986@racer","threadId":"14595","inReplyTo":null,"subject":"[FYI PATCH] git wrapper: DWIM mistyped commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-22T20:01:29Z","receivedAt":"2008-07-22T20:01:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThis patch introduces a modified Damerau-Levenshtein algorithm into\nGit's code base, and uses it with the following penalties to show some\nsimilar commands when an unknown command was encountered:\n\n\tswap = 0, insertion = 1, substitution = 2, deletion = 4\n\nA typical output would now look like this:\n\n\t$ git sm\n\tgit: 'sm' is not a git-command. See 'git --help'.\n\n\tDid you mean one of these?\n\t\tam\n\t\trm\n\nThe cut-off is at similarity rating 6, which was empirically determined\nto give sensible results.\n\nAs a convenience, if there is only one candidate, Git continues under\nthe assumption that the user mistyped it.  Example:\n\n\t$ git reabse\n\tWARNING: You called a Git program named 'reabse', which does\n\tnot exist.\n\tContinuing under the assumption that you meant 'rebase'\n\t[...]\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tSo I mistyped 'reabse' for the hundred trillionth time, but I\n\twill never have to correct my mistakes again.\n\n\tNote: this patch is _not_ meant for inclusion.\n\n Makefile      |    2 +\n builtin.h     |    2 +-\n git.c         |    4 ++-\n help.c        |   61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n levenshtein.c |   47 +++++++++++++++++++++++++++++++++++++++++++\n levenshtein.h |    8 +++++++\n 6 files changed, 121 insertions(+), 3 deletions(-)\n create mode 100644 levenshtein.c\n create mode 100644 levenshtein.h\n\ndiff --git a/Makefile b/Makefile\nindex 19bdd03..7e114e0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -347,6 +347,7 @@ LIB_H += git-compat-util.h\n LIB_H += graph.h\n LIB_H += grep.h\n LIB_H += hash.h\n+LIB_H += levenshtein.h\n LIB_H += list-objects.h\n LIB_H += ll-merge.h\n LIB_H += log-tree.h\n@@ -421,6 +422,7 @@ LIB_OBJS += hash.o\n LIB_OBJS += help.o\n LIB_OBJS += ident.o\n LIB_OBJS += interpolate.o\n+LIB_OBJS += levenshtein.o\n LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\ndiff --git a/builtin.h b/builtin.h\nindex 0e605d4..fc5f108 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -11,7 +11,7 @@ extern const char git_usage_string[];\n extern const char git_more_info_string[];\n \n extern void list_common_cmds_help(void);\n-extern void help_unknown_cmd(const char *cmd);\n+extern const char *help_unknown_cmd(const char *cmd);\n extern void prune_packed_objects(int);\n extern int read_line_with_nul(char *buf, int size, FILE *file);\n extern int fmt_merge_msg(int merge_summary, struct strbuf *in,\ndiff --git a/git.c b/git.c\nindex 1bfd271..d7510ef 100644\n--- a/git.c\n+++ b/git.c\n@@ -500,7 +500,9 @@ int main(int argc, const char **argv)\n \t\t\t\tcmd, argv[0]);\n \t\t\texit(1);\n \t\t}\n-\t\thelp_unknown_cmd(cmd);\n+\t\targv[0] = help_unknown_cmd(cmd);\n+\t\thandle_internal_command(argc, argv);\n+\t\texecv_dashed_external(argv);\n \t}\n \n \tfprintf(stderr, \"Failed to run command '%s': %s\\n\",\ndiff --git a/help.c b/help.c\nindex bfc84ae..480befe 100644\n--- a/help.c\n+++ b/help.c\n@@ -9,6 +9,7 @@\n #include \"common-cmds.h\"\n #include \"parse-options.h\"\n #include \"run-command.h\"\n+#include \"levenshtein.h\"\n \n static struct man_viewer_list {\n \tstruct man_viewer_list *next;\n@@ -666,9 +667,67 @@ static void show_html_page(const char *git_cmd)\n \topen_html(page_path.buf);\n }\n \n-void help_unknown_cmd(const char *cmd)\n+static const char *levenshtein_cmd;\n+static int similarity(const char *cmd) {\n+\treturn levenshtein(levenshtein_cmd, cmd, 0, 2, 1, 4);\n+}\n+\n+static int levenshtein_compare(const void *p1, const void *p2)\n+{\n+\tconst struct cmdname *const *c1 = p1, *const *c2 = p2;\n+\tconst char *s1 = (*c1)->name, *s2 = (*c2)->name;\n+\tint l1 = similarity(s1);\n+\tint l2 = similarity(s2);\n+\treturn l1 != l2 ? l1 - l2 : strcmp(s1, s2);\n+}\n+\n+const char *help_unknown_cmd(const char *cmd)\n {\n+\tint i, best_similarity = 0;\n+\tchar cwd[PATH_MAX];\n+\n+\tif (!getcwd(cwd, sizeof(cwd))) {\n+\t\terror(\"Could not get current working directory\");\n+\t\tcwd[0] = '\\0';\n+\t}\n+\n+\tload_command_list();\n+\tALLOC_GROW(main_cmds.names, main_cmds.cnt + other_cmds.cnt,\n+\t\t\tmain_cmds.alloc);\n+\tmemcpy(main_cmds.names + main_cmds.cnt, other_cmds.names,\n+\t\tother_cmds.cnt * sizeof(other_cmds.names[0]));\n+\tmain_cmds.cnt += other_cmds.cnt;\n+\n+\tlevenshtein_cmd = cmd;\n+\tqsort(main_cmds.names, main_cmds.cnt,\n+\t      sizeof(*main_cmds.names), levenshtein_compare);\n+\n+\tif (!main_cmds.cnt)\n+\t\tdie (\"Uh oh.  Your system reports no Git commands at all.\");\n+\tbest_similarity = similarity(main_cmds.names[0]->name);\n+\tif (main_cmds.cnt < 2 || best_similarity <\n+\t\t\tsimilarity(main_cmds.names[1]->name)) {\n+\t\tif (!*cwd)\n+\t\t\texit(1);\n+\t\tif (chdir(cwd))\n+\t\t\tdie (\"Could not change directory back to '%s'\", cwd);\n+\t\tfprintf(stderr, \"WARNING: You called a Git program named '%s', \"\n+\t\t\t\"which does not exist.\\n\"\n+\t\t\t\"Continuing under the assumption that you meant '%s'\\n\",\n+\t\t\tcmd, main_cmds.names[0]->name);\n+\t\treturn main_cmds.names[0]->name;\n+\t}\n+\n \tfprintf(stderr, \"git: '%s' is not a git-command. See 'git --help'.\\n\", cmd);\n+\n+\tif (best_similarity < 6) {\n+\t\tfprintf(stderr, \"\\nDid you mean one of these?\\n\");\n+\n+\t\tfor (i = 0; i < main_cmds.cnt && best_similarity ==\n+\t\t\t\tsimilarity(main_cmds.names[i]->name); i++)\n+\t\t\tfprintf(stderr, \"\\t%s\\n\", main_cmds.names[i]->name);\n+\t}\n+\n \texit(1);\n }\n \ndiff --git a/levenshtein.c b/levenshtein.c\nnew file mode 100644\nindex 0000000..db52f2c\n--- /dev/null\n+++ b/levenshtein.c\n@@ -0,0 +1,47 @@\n+#include \"cache.h\"\n+#include \"levenshtein.h\"\n+\n+int levenshtein(const char *string1, const char *string2,\n+\t\tint w, int s, int a, int d)\n+{\n+\tint len1 = strlen(string1), len2 = strlen(string2);\n+\tint *row0 = xmalloc(sizeof(int) * (len2 + 1));\n+\tint *row1 = xmalloc(sizeof(int) * (len2 + 1));\n+\tint *row2 = xmalloc(sizeof(int) * (len2 + 1));\n+\tint i, j;\n+\n+\tfor (j = 0; j <= len2; j++)\n+\t\trow1[j] = j * a;\n+\tfor (i = 0; i < len1; i++) {\n+\t\tint *dummy;\n+\n+\t\trow2[0] = (i + 1) * d;\n+\t\tfor (j = 0; j < len2; j++) {\n+\t\t\t/* substitution */\n+\t\t\trow2[j + 1] = row1[j] + s * (string1[i] != string2[j]);\n+\t\t\t/* swap */\n+\t\t\tif (i > 0 && j > 0 && string1[i - 1] == string2[j] &&\n+\t\t\t\t\tstring1[i] == string2[j - 1] &&\n+\t\t\t\t\trow2[j + 1] > row0[j - 1] + w)\n+\t\t\t\trow2[j + 1] = row0[j - 1] + w;\n+\t\t\t/* deletion */\n+\t\t\tif (j + 1 < len2 && row2[j + 1] > row1[j + 1] + d)\n+\t\t\t\trow2[j + 1] = row1[j + 1] + d;\n+\t\t\t/* insertion */\n+\t\t\tif (row2[j + 1] > row2[j] + a)\n+\t\t\t\trow2[j + 1] = row2[j] + a;\n+\t\t}\n+\n+\t\tdummy = row0;\n+\t\trow0 = row1;\n+\t\trow1 = row2;\n+\t\trow2 = dummy;\n+\t}\n+\n+\ti = row1[len2];\n+\tfree(row0);\n+\tfree(row1);\n+\tfree(row2);\n+\n+\treturn i;\n+}\ndiff --git a/levenshtein.h b/levenshtein.h\nnew file mode 100644\nindex 0000000..0173abe\n--- /dev/null\n+++ b/levenshtein.h\n@@ -0,0 +1,8 @@\n+#ifndef LEVENSHTEIN_H\n+#define LEVENSHTEIN_H\n+\n+int levenshtein(const char *string1, const char *string2,\n+\tint swap_penalty, int substition_penalty,\n+\tint insertion_penalty, int deletion_penalty);\n+\n+#endif\n-- \n1.6.0.rc0.21.g91175\n"},{"id":"84356","messageId":"20080722201648.GB11831@artemis.madism.org","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807222100150.8986@racer","subject":"[SCNR] Re: [FYI PATCH] git wrapper: DWIM mistyped commands","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2008-07-22T20:16:48Z","receivedAt":"2008-07-22T20:16:48Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"On Tue, Jul 22, 2008 at 08:01:29PM +0000, Johannes Schindelin wrote:\n> \n> This patch introduces a modified Damerau-Levenshtein algorithm into\n> Git's code base, and uses it with the following penalties to show some\n> similar commands when an unknown command was encountered:\n> \n> \tswap = 0, insertion = 1, substitution = 2, deletion = 4\n> \n> A typical output would now look like this:\n> \n> \t$ git sm\n> \tgit: 'sm' is not a git-command. See 'git --help'.\n> \n> \tDid you mean one of these?\n> \t\tam\n> \t\trm\n> \n> The cut-off is at similarity rating 6, which was empirically determined\n> to give sensible results.\n> \n> As a convenience, if there is only one candidate, Git continues under\n> the assumption that the user mistyped it.  Example:\n> \n> \t$ git reabse\n> \tWARNING: You called a Git program named 'reabse', which does\n> \tnot exist.\n> \tContinuing under the assumption that you meant 'rebase'\n> \t[...]\n\n<SCNR>\n    Or use a decent shell:\n\n    When typing e.g.: git tsa<tab>, it yields:\n    $ git status\n    ---- corrections (errors 1)\n    status        -- show working-tree's status\n    tag           -- create tag object signed with GPG\n    tar-tree      -- create tar archive of the files in the named tree\n    ---- original\n    tsa\n\n    and it even works for non git commands ;)\n</SCNR>\n\nDespite that, I really like your idea. **hint hint** One could even hook that\nfor long options into parse-options.\n\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"},{"id":"84358","messageId":"alpine.DEB.1.00.0807222118030.8986@racer","threadId":"14595","inReplyTo":"20080722201648.GB11831@artemis.madism.org","subject":"Re: [SCNR] Re: [FYI PATCH] git wrapper: DWIM mistyped commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-22T20:19:13Z","receivedAt":"2008-07-22T20:19:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 22 Jul 2008, Pierre Habouzit wrote:\n\n> <SCNR>\n>     Or use a decent shell:\n\nI tried that:\n\n\tgit reab<tab><tab><TAB><TTAAABBB!>\n\n> Despite that, I really like your idea. **hint hint**\n\nI said that _I_ did not mean it for inclusion.  **hint hint**\n\nCiao,\nDscho\n"},{"id":"84360","messageId":"20080722203422.GC11831@artemis.madism.org","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807222118030.8986@racer","subject":"Re: [SCNR] Re: [FYI PATCH] git wrapper: DWIM mistyped commands","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2008-07-22T20:34:22Z","receivedAt":"2008-07-22T20:34:22Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"On Tue, Jul 22, 2008 at 08:19:13PM +0000, Johannes Schindelin wrote:\n> Hi,\n> \n> On Tue, 22 Jul 2008, Pierre Habouzit wrote:\n> \n> > <SCNR>\n> >     Or use a decent shell:\n> \n> I tried that:\n> \n> \tgit reab<tab><tab><TAB><TTAAABBB!>\n\nIt yields the following here:\n\n    $ git read-tree\n    ---- corrections (errors 1)\n    read-tree  -- read tree information into the directory index\n    rebase     -- rebase local commits to new upstream head\n    ---- original\n    reab\n\nand indeed, it should really suggest rebase first, I suppose I should reorder\nmy zsh completion error weights. but oh well...\n\n> > Despite that, I really like your idea. **hint hint**\n> \n> I said that _I_ did not mean it for inclusion.  **hint hint**\n\nDamn...\n\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"},{"id":"84361","messageId":"20080722203730.GC5113@blimp.local","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807222100150.8986@racer","subject":"Re: [FYI PATCH] git wrapper: DWIM mistyped commands","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-07-22T20:37:30Z","receivedAt":"2008-07-22T20:37:30Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Johannes Schindelin, Tue, Jul 22, 2008 22:01:29 +0200:\n> As a convenience, if there is only one candidate, Git continues under\n> the assumption that the user mistyped it.  Example:\n> \n> \t$ git reabse\n> \tWARNING: You called a Git program named 'reabse', which does\n> \tnot exist.\n> \tContinuing under the assumption that you meant 'rebase'\n> \t[...]\n\nOh, that would make me suspicios (and I hit Ctrl-C fast when I get\nsuspicios about what happens to my precious data). Could it be\nconfigurable? For example, BASH's cdspell is configurable and even off\nby default.\n\nP.S. I'm still using your first patch and am forced to like it every day :)\n"},{"id":"84364","messageId":"20080722210354.GD5113@blimp.local","threadId":"14595","inReplyTo":"20080722203730.GC5113@blimp.local","subject":"[PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-07-22T21:03:55Z","receivedAt":"2008-07-22T21:03:55Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"It is off by default, to avoid scaring people unless they asked to.\n\n---\n\nAlex Riesen, Tue, Jul 22, 2008 22:37:30 +0200:\n> Johannes Schindelin, Tue, Jul 22, 2008 22:01:29 +0200:\n> > As a convenience, if there is only one candidate, Git continues under\n> > the assumption that the user mistyped it.  Example:\n> > \n> > \t$ git reabse\n> > \tWARNING: You called a Git program named 'reabse', which does\n> > \tnot exist.\n> > \tContinuing under the assumption that you meant 'rebase'\n> > \t[...]\n> \n> Oh, that would make me suspicios (and I hit Ctrl-C fast when I get\n> suspicios about what happens to my precious data). Could it be\n> configurable? For example, BASH's cdspell is configurable and even off\n> by default.\n> \n\nLike this, perhaps?\n\n help.c |    8 ++++++--\n 1 files changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 480befe..f08eb9d 100644\n--- a/help.c\n+++ b/help.c\n@@ -28,6 +28,7 @@ enum help_format {\n \tHELP_FORMAT_WEB,\n };\n \n+static int autocorrect;\n static int show_all = 0;\n static enum help_format help_format = HELP_FORMAT_MAN;\n static struct option builtin_help_options[] = {\n@@ -269,6 +270,8 @@ static int git_help_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!prefixcmp(var, \"man.\"))\n \t\treturn add_man_viewer_info(var, value);\n+\tif (!strcmp(var, \"help.autocorrect\"))\n+\t\tautocorrect = git_config_bool(var,value);\n \n \treturn git_default_config(var, value, cb);\n }\n@@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd)\n \n \tif (!main_cmds.cnt)\n \t\tdie (\"Uh oh.  Your system reports no Git commands at all.\");\n+\tgit_config(git_help_config, NULL);\n \tbest_similarity = similarity(main_cmds.names[0]->name);\n-\tif (main_cmds.cnt < 2 || best_similarity <\n-\t\t\tsimilarity(main_cmds.names[1]->name)) {\n+\tif (autocorrect && (main_cmds.cnt < 2 ||\n+\t\tbest_similarity < similarity(main_cmds.names[1]->name))) {\n \t\tif (!*cwd)\n \t\t\texit(1);\n \t\tif (chdir(cwd))\n-- \n1.6.0.rc0.48.g6dda.dirty\n"},{"id":"84365","messageId":"alpine.DEB.1.00.0807222207110.8986@racer","threadId":"14595","inReplyTo":"20080722210354.GD5113@blimp.local","subject":"Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-22T21:08:19Z","receivedAt":"2008-07-22T21:08:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 22 Jul 2008, Alex Riesen wrote:\n\n> +\tif (autocorrect && (main_cmds.cnt < 2 ||\n> +\t\tbest_similarity < similarity(main_cmds.names[1]->name))) {\n>  \t\tif (!*cwd)\n>  \t\t\texit(1);\n>  \t\tif (chdir(cwd))\n\nIn that case, you need to put in the \"one of these\" / \"this\" conditional \nagain, which I ripped out because I do not need it any more.\n\nCiao,\nDscho\n"},{"id":"84377","messageId":"20080722212633.GF5113@blimp.local","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807222207110.8986@racer","subject":"[PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-07-22T21:26:33Z","receivedAt":"2008-07-22T21:26:33Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"It is off by default, to avoid scaring people unless they asked to.\n---\nJohannes Schindelin, Tue, Jul 22, 2008 23:08:19 +0200:\n> On Tue, 22 Jul 2008, Alex Riesen wrote:\n> \n> > +\tif (autocorrect && (main_cmds.cnt < 2 ||\n> > +\t\tbest_similarity < similarity(main_cmds.names[1]->name))) {\n> >  \t\tif (!*cwd)\n> >  \t\t\texit(1);\n> >  \t\tif (chdir(cwd))\n> \n> In that case, you need to put in the \"one of these\" / \"this\" conditional \n> again, which I ripped out because I do not need it any more.\n> \n\nOf course, missed it. Like this, then.\n\n help.c |   20 ++++++++++++++------\n 1 files changed, 14 insertions(+), 6 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 480befe..cf6b459 100644\n--- a/help.c\n+++ b/help.c\n@@ -28,6 +28,7 @@ enum help_format {\n \tHELP_FORMAT_WEB,\n };\n \n+static int autocorrect;\n static int show_all = 0;\n static enum help_format help_format = HELP_FORMAT_MAN;\n static struct option builtin_help_options[] = {\n@@ -269,6 +270,8 @@ static int git_help_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!prefixcmp(var, \"man.\"))\n \t\treturn add_man_viewer_info(var, value);\n+\tif (!strcmp(var, \"help.autocorrect\"))\n+\t\tautocorrect = git_config_bool(var,value);\n \n \treturn git_default_config(var, value, cb);\n }\n@@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd)\n \n \tif (!main_cmds.cnt)\n \t\tdie (\"Uh oh.  Your system reports no Git commands at all.\");\n+\tgit_config(git_help_config, NULL);\n \tbest_similarity = similarity(main_cmds.names[0]->name);\n-\tif (main_cmds.cnt < 2 || best_similarity <\n-\t\t\tsimilarity(main_cmds.names[1]->name)) {\n+\tif (autocorrect && (main_cmds.cnt < 2 ||\n+\t\tbest_similarity < similarity(main_cmds.names[1]->name))) {\n \t\tif (!*cwd)\n \t\t\texit(1);\n \t\tif (chdir(cwd))\n@@ -721,10 +725,14 @@ const char *help_unknown_cmd(const char *cmd)\n \tfprintf(stderr, \"git: '%s' is not a git-command. See 'git --help'.\\n\", cmd);\n \n \tif (best_similarity < 6) {\n-\t\tfprintf(stderr, \"\\nDid you mean one of these?\\n\");\n-\n-\t\tfor (i = 0; i < main_cmds.cnt && best_similarity ==\n-\t\t\t\tsimilarity(main_cmds.names[i]->name); i++)\n+\t\tint n = 0;\n+\t\twhile (n < main_cmds.cnt &&\n+\t\t\tbest_similarity == similarity(main_cmds.names[n]->name))\n+\t\t\t++n;\n+\t\tfprintf(stderr, \"\\nDid you mean %s?\\n\",\n+\t\t\tn < 2 ? \"this\": \"one of these\");\n+\n+\t\tfor (i = 0; i < n; i++)\n \t\t\tfprintf(stderr, \"\\t%s\\n\", main_cmds.names[i]->name);\n \t}\n \n-- \n1.6.0.rc0.48.ga184\n"},{"id":"84383","messageId":"alpine.DEB.1.00.0807222242160.8986@racer","threadId":"14595","inReplyTo":"20080722212633.GF5113@blimp.local","subject":"Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-22T21:44:50Z","receivedAt":"2008-07-22T21:44:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 22 Jul 2008, Alex Riesen wrote:\n\n> @@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd)\n>  \n>  \tif (!main_cmds.cnt)\n>  \t\tdie (\"Uh oh.  Your system reports no Git commands at all.\");\n> +\tgit_config(git_help_config, NULL);\n>  \tbest_similarity = similarity(main_cmds.names[0]->name);\n> -\tif (main_cmds.cnt < 2 || best_similarity <\n> -\t\t\tsimilarity(main_cmds.names[1]->name)) {\n> +\tif (autocorrect && (main_cmds.cnt < 2 ||\n> +\t\tbest_similarity < similarity(main_cmds.names[1]->name))) {\n>  \t\tif (!*cwd)\n>  \t\t\texit(1);\n>  \t\tif (chdir(cwd))\n\nThis \"if\" already checks if there is only one candidate.  So you should \njust add an inner \"if (autocorrect) ... else single = 1;\" or some such.\n\nHowever, I think that the intention of this patch is too much DWIMery, \nwhich might be good for me (just like my \"git add remote\" patch), but not \nfor the general audience.\n\nCiao,\nDscho\n"},{"id":"84538","messageId":"20080722222510.GA4474@blimp.local","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807222242160.8986@racer","subject":"[PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-07-22T22:25:10Z","receivedAt":"2008-07-22T22:25:10Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"It is off by default, to avoid scaring people unless they asked to.\n---\n\nJohannes Schindelin, Tue, Jul 22, 2008 23:44:50 +0200:\n> On Tue, 22 Jul 2008, Alex Riesen wrote:\n> \n> > @@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd)\n> >  \n> >  \tif (!main_cmds.cnt)\n> >  \t\tdie (\"Uh oh.  Your system reports no Git commands at all.\");\n> > +\tgit_config(git_help_config, NULL);\n> >  \tbest_similarity = similarity(main_cmds.names[0]->name);\n> > -\tif (main_cmds.cnt < 2 || best_similarity <\n> > -\t\t\tsimilarity(main_cmds.names[1]->name)) {\n> > +\tif (autocorrect && (main_cmds.cnt < 2 ||\n> > +\t\tbest_similarity < similarity(main_cmds.names[1]->name))) {\n> >  \t\tif (!*cwd)\n> >  \t\t\texit(1);\n> >  \t\tif (chdir(cwd))\n> \n> This \"if\" already checks if there is only one candidate.  So you should \n> just add an inner \"if (autocorrect) ... else single = 1;\" or some such.\n\nOh right, stupid me.\n\n> However, I think that the intention of this patch is too much DWIMery, \n> which might be good for me (just like my \"git add remote\" patch), but not \n> for the general audience.\n\nMustn't be good for all (for the \"general audience\" it is even common\npractice to forget to thank. It may be even a sign of bad manners for\nit). It is good for me though. And thanks for sharing.\n\nMoved git_config before the calls where current directory is changed:\nso that it has the same filesystem context as in normal case. Less\nsurprises.\n\n help.c |   19 +++++++++++++------\n 1 files changed, 13 insertions(+), 6 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 480befe..8b25a55 100644\n--- a/help.c\n+++ b/help.c\n@@ -28,6 +28,7 @@ enum help_format {\n \tHELP_FORMAT_WEB,\n };\n \n+static int autocorrect;\n static int show_all = 0;\n static enum help_format help_format = HELP_FORMAT_MAN;\n static struct option builtin_help_options[] = {\n@@ -269,6 +270,8 @@ static int git_help_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!prefixcmp(var, \"man.\"))\n \t\treturn add_man_viewer_info(var, value);\n+\tif (!strcmp(var, \"help.autocorrect\"))\n+\t\tautocorrect = git_config_bool(var,value);\n \n \treturn git_default_config(var, value, cb);\n }\n@@ -683,7 +686,7 @@ static int levenshtein_compare(const void *p1, const void *p2)\n \n const char *help_unknown_cmd(const char *cmd)\n {\n-\tint i, best_similarity = 0;\n+\tint i, best_similarity = 0, n;\n \tchar cwd[PATH_MAX];\n \n \tif (!getcwd(cwd, sizeof(cwd))) {\n@@ -691,6 +694,7 @@ const char *help_unknown_cmd(const char *cmd)\n \t\tcwd[0] = '\\0';\n \t}\n \n+\tgit_config(git_help_config, NULL);\n \tload_command_list();\n \tALLOC_GROW(main_cmds.names, main_cmds.cnt + other_cmds.cnt,\n \t\t\tmain_cmds.alloc);\n@@ -705,8 +709,11 @@ const char *help_unknown_cmd(const char *cmd)\n \tif (!main_cmds.cnt)\n \t\tdie (\"Uh oh.  Your system reports no Git commands at all.\");\n \tbest_similarity = similarity(main_cmds.names[0]->name);\n-\tif (main_cmds.cnt < 2 || best_similarity <\n-\t\t\tsimilarity(main_cmds.names[1]->name)) {\n+\tn = 1;\n+\twhile (n < main_cmds.cnt &&\n+\t\tbest_similarity == similarity(main_cmds.names[n]->name))\n+\t\t++n;\n+\tif (autocorrect && n == 1) {\n \t\tif (!*cwd)\n \t\t\texit(1);\n \t\tif (chdir(cwd))\n@@ -721,10 +728,10 @@ const char *help_unknown_cmd(const char *cmd)\n \tfprintf(stderr, \"git: '%s' is not a git-command. See 'git --help'.\\n\", cmd);\n \n \tif (best_similarity < 6) {\n-\t\tfprintf(stderr, \"\\nDid you mean one of these?\\n\");\n+\t\tfprintf(stderr, \"\\nDid you mean %s?\\n\",\n+\t\t\tn < 2 ? \"this\": \"one of these\");\n \n-\t\tfor (i = 0; i < main_cmds.cnt && best_similarity ==\n-\t\t\t\tsimilarity(main_cmds.names[i]->name); i++)\n+\t\tfor (i = 0; i < n; i++)\n \t\t\tfprintf(stderr, \"\\t%s\\n\", main_cmds.names[i]->name);\n \t}\n \n-- \n1.6.0.rc0.48.ga184\n"},{"id":"84392","messageId":"7vwsjdjz9e.fsf@gitster.siamese.dyndns.org","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807222100150.8986@racer","subject":"Re: [FYI PATCH] git wrapper: DWIM mistyped commands","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-07-22T23:05:33Z","receivedAt":"2008-07-22T23:05:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> \tSo I mistyped 'reabse' for the hundred trillionth time, but I\n> \twill never have to correct my mistakes again.\n>\n> \tNote: this patch is _not_ meant for inclusion.\n\nHeh, I do \"git emrge\" all the time ;-)\n\nTime to start a \"gitster private edition\" branch for my own use, I guess.\n"},{"id":"84393","messageId":"7vsku1jz4u.fsf@gitster.siamese.dyndns.org","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807222242160.8986@racer","subject":"Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-07-22T23:08:17Z","receivedAt":"2008-07-22T23:08:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi,\n>\n> On Tue, 22 Jul 2008, Alex Riesen wrote:\n>\n>> @@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd)\n>>  \n>>  \tif (!main_cmds.cnt)\n>>  \t\tdie (\"Uh oh.  Your system reports no Git commands at all.\");\n>> +\tgit_config(git_help_config, NULL);\n>>  \tbest_similarity = similarity(main_cmds.names[0]->name);\n>> -\tif (main_cmds.cnt < 2 || best_similarity <\n>> -\t\t\tsimilarity(main_cmds.names[1]->name)) {\n>> +\tif (autocorrect && (main_cmds.cnt < 2 ||\n>> +\t\tbest_similarity < similarity(main_cmds.names[1]->name))) {\n>>  \t\tif (!*cwd)\n>>  \t\t\texit(1);\n>>  \t\tif (chdir(cwd))\n>\n> This \"if\" already checks if there is only one candidate.  So you should \n> just add an inner \"if (autocorrect) ... else single = 1;\" or some such.\n>\n> However, I think that the intention of this patch is too much DWIMery, \n> which might be good for me (just like my \"git add remote\" patch), but not \n> for the general audience.\n\nPlease make autocorrect not a binary but optionally the number of\ndeciseconds before it continues, so that I have a chance to hit ^C ;-)\n"},{"id":"84395","messageId":"bd6139dc0807221610o7da154f9r9011753a9dc7588f@mail.gmail.com","threadId":"14595","inReplyTo":"7vwsjdjz9e.fsf@gitster.siamese.dyndns.org","subject":"Re: [FYI PATCH] git wrapper: DWIM mistyped commands","fromName":"Sverre Rabbelier","fromEmail":"alturin@gmail.com","sentAt":"2008-07-22T23:10:22Z","receivedAt":"2008-07-22T23:10:22Z","isPatch":true,"sender":{"key":"alturin@gmail.com","avatar":null},"body":"On Wed, Jul 23, 2008 at 1:05 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n>>       So I mistyped 'reabse' for the hundred trillionth time, but I\n>>       will never have to correct my mistakes again.\n>>\n>>       Note: this patch is _not_ meant for inclusion.\n>\n> Heh, I do \"git emrge\" all the time ;-)\n>\n> Time to start a \"gitster private edition\" branch for my own use, I guess.\n\nIf it is the same typo all the time, wouldn't aliasses do the trick?\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"84540","messageId":"20080723164109.GA5283@blimp.local","threadId":"14595","inReplyTo":"7vsku1jz4u.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] Wait help.autocorrect deciseconds before running corrected command","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-07-23T16:41:10Z","receivedAt":"2008-07-23T16:41:10Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Suggested by Junio, so he has a chance to hit Ctrl-C.\n---\nJunio C Hamano, Wed, Jul 23, 2008 01:08:17 +0200:\n> \n> Please make autocorrect not a binary but optionally the number of\n> deciseconds before it continues, so that I have a chance to hit ^C ;-)\n\non top of the last patch.\n\n Documentation/config.txt |    9 +++++++++\n help.c                   |    7 ++++++-\n 2 files changed, 15 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex e784805..5bf1d0d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -771,6 +771,15 @@ help.format::\n \tValues 'man', 'info', 'web' and 'html' are supported. 'man' is\n \tthe default. 'web' and 'html' are the same.\n \n+help.autocorrect::\n+\tAutomatically correct and execute mistyped commands after\n+\twaiting for the given number of deciseconds (0.1 sec). If more\n+\tthan one command can be deduced from the entered text, nothing\n+\twill be executed.  If the value of this option is negative,\n+\tthe corrected command will be executed immediately. If the\n+\tvalue is 0 - the command will be just shown but not executed.\n+\tThis is the default.\n+\n http.proxy::\n \tOverride the HTTP proxy, normally configured using the 'http_proxy'\n \tenvironment variable (see linkgit:curl[1]).  This can be overridden\ndiff --git a/help.c b/help.c\nindex 8b25a55..4d52781 100644\n--- a/help.c\n+++ b/help.c\n@@ -271,7 +271,7 @@ static int git_help_config(const char *var, const char *value, void *cb)\n \tif (!prefixcmp(var, \"man.\"))\n \t\treturn add_man_viewer_info(var, value);\n \tif (!strcmp(var, \"help.autocorrect\"))\n-\t\tautocorrect = git_config_bool(var,value);\n+\t\tautocorrect = git_config_int(var,value);\n \n \treturn git_default_config(var, value, cb);\n }\n@@ -722,6 +722,11 @@ const char *help_unknown_cmd(const char *cmd)\n \t\t\t\"which does not exist.\\n\"\n \t\t\t\"Continuing under the assumption that you meant '%s'\\n\",\n \t\t\tcmd, main_cmds.names[0]->name);\n+\t\tif (autocorrect > 0) {\n+\t\t\tfprintf(stderr, \"in %0.1f seconds automatically...\\n\",\n+\t\t\t\t(float)autocorrect/10.0);\n+\t\t\tpoll(NULL, 0, autocorrect * 100);\n+\t\t}\n \t\treturn main_cmds.names[0]->name;\n \t}\n \n-- \n1.6.0.rc0.50.g9c23\n"},{"id":"84542","messageId":"alpine.DEB.1.00.0807231743030.8986@racer","threadId":"14595","inReplyTo":"20080722222510.GA4474@blimp.local","subject":"Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-23T16:44:49Z","receivedAt":"2008-07-23T16:44:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 23 Jul 2008, Alex Riesen wrote:\n\n> Johannes Schindelin, Tue, Jul 22, 2008 23:44:50 +0200:\n> > However, I think that the intention of this patch is too much DWIMery, \n> > which might be good for me (just like my \"git add remote\" patch), but \n> > not for the general audience.\n> \n> Mustn't be good for all\n\nYou meant \"needn't\"?  It is good for me ;-)\n\n> And thanks for sharing.\n\nYou're welcome.\n\n> +\tn = 1;\n> +\twhile (n < main_cmds.cnt &&\n> +\t\tbest_similarity == similarity(main_cmds.names[n]->name))\n> +\t\t++n;\n\nMini-nit: you never ask for the value of n, only if it is 1 or larger.  So \nyou do not need to count...\n\nCiao,\nDscho\n"},{"id":"84547","messageId":"alpine.DEB.1.00.0807231756380.8986@racer","threadId":"14595","inReplyTo":"20080723164109.GA5283@blimp.local","subject":"Re: [PATCH] Wait help.autocorrect deciseconds before running corrected command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-23T16:57:05Z","receivedAt":"2008-07-23T16:57:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 23 Jul 2008, Alex Riesen wrote:\n\n> +\t\tif (autocorrect > 0) {\n> +\t\t\tfprintf(stderr, \"in %0.1f seconds automatically...\\n\",\n> +\t\t\t\t(float)autocorrect/10.0);\n> +\t\t\tpoll(NULL, 0, autocorrect * 100);\n> +\t\t}\n\nWhat?  No countdown?  No fancy sounds when the time ran up?\n\nCiao,\nDscho\n"},{"id":"84570","messageId":"20080723184443.GD5283@blimp.local","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807231743030.8986@racer","subject":"Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-07-23T18:44:43Z","receivedAt":"2008-07-23T18:44:43Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Johannes Schindelin, Wed, Jul 23, 2008 18:44:49 +0200:\n> > +\tn = 1;\n> > +\twhile (n < main_cmds.cnt &&\n> > +\t\tbest_similarity == similarity(main_cmds.names[n]->name))\n> > +\t\t++n;\n> \n> Mini-nit: you never ask for the value of n, only if it is 1 or larger.  So \n> you do not need to count...\n\nBut I do, don't I? AFAICS, I use 0, 1 and >1 (this-these).\n"},{"id":"84571","messageId":"20080723184540.GE5283@blimp.local","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807231756380.8986@racer","subject":"Re: [PATCH] Wait help.autocorrect deciseconds before running corrected command","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-07-23T18:45:41Z","receivedAt":"2008-07-23T18:45:41Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Johannes Schindelin, Wed, Jul 23, 2008 18:57:05 +0200:\n> On Wed, 23 Jul 2008, Alex Riesen wrote:\n> \n> > +\t\tif (autocorrect > 0) {\n> > +\t\t\tfprintf(stderr, \"in %0.1f seconds automatically...\\n\",\n> > +\t\t\t\t(float)autocorrect/10.0);\n> > +\t\t\tpoll(NULL, 0, autocorrect * 100);\n> > +\t\t}\n> \n> What?  No countdown?  No fancy sounds when the time ran up?\n> \n\nHate them fancies... :)\n"},{"id":"84574","messageId":"alpine.DEB.1.00.0807231958210.8986@racer","threadId":"14595","inReplyTo":"20080723184443.GD5283@blimp.local","subject":"Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-23T19:00:58Z","receivedAt":"2008-07-23T19:00:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 23 Jul 2008, Alex Riesen wrote:\n\n> Johannes Schindelin, Wed, Jul 23, 2008 18:44:49 +0200:\n> > > +\tn = 1;\n> > > +\twhile (n < main_cmds.cnt &&\n> > > +\t\tbest_similarity == similarity(main_cmds.names[n]->name))\n> > > +\t\t++n;\n> > \n> > Mini-nit: you never ask for the value of n, only if it is 1 or larger.  So \n> > you do not need to count...\n> \n> But I do, don't I? AFAICS, I use 0, 1 and >1 (this-these).\n\nYes.  So check cnt > 0 && best_similarity > 5 says if it is 0, and \ncnt > 1 && best_similarity < similarity(...[1]...) says if it is 1.\n\nErgo: no need to count,\nDscho\n"},{"id":"84578","messageId":"alpine.DEB.1.00.0807232003420.8986@racer","threadId":"14595","inReplyTo":"alpine.DEB.1.00.0807231958210.8986@racer","subject":"Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-07-23T19:04:40Z","receivedAt":"2008-07-23T19:04:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 23 Jul 2008, Johannes Schindelin wrote:\n\n> On Wed, 23 Jul 2008, Alex Riesen wrote:\n> \n> > Johannes Schindelin, Wed, Jul 23, 2008 18:44:49 +0200:\n> > > > +\tn = 1;\n> > > > +\twhile (n < main_cmds.cnt &&\n> > > > +\t\tbest_similarity == similarity(main_cmds.names[n]->name))\n> > > > +\t\t++n;\n> > > \n> > > Mini-nit: you never ask for the value of n, only if it is 1 or larger.  So \n> > > you do not need to count...\n> > \n> > But I do, don't I? AFAICS, I use 0, 1 and >1 (this-these).\n> \n> Yes.  So check cnt > 0 && best_similarity > 5 says if it is 0,\n\nOh, I just realized that my patch is bogus anyway.  It only checks for \nbest_similarity > 5 in the case that the first two commands have equal \nsimilarity.  D'oh.\n\nCiao,\nDscho\n"}]}