* [PATCH] Remove calculation of the longest command name from where it is not used @ 2008-08-28 17:15 Alex Riesen, Alex Riesen 2008-08-28 21:27 ` [PATCH updated] git wrapper: DWIM mistyped commands Alex Riesen 0 siblings, 1 reply; 29+ messages in thread From: Alex Riesen, Alex Riesen @ 2008-08-28 17:15 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Johannes Schindelin Just calculate it where it is needed - it is cheap and trivial, as all the lengths are already there (stored when creating the command lists). Signed-off-by: Alex Riesen <raa.lkml@gmail.com> --- And it less code than before (it is even more deletions than insertions). BTW, the Johannesses typo-guesser conflicts heavily in recent master. Pity. I'm going to rebase it and send out the rebased version. builtin-help.c | 4 ++-- builtin-merge.c | 8 ++++---- help.c | 34 +++++++++++++++------------------- help.h | 6 +++--- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/builtin-help.c b/builtin-help.c index 391f749..9225102 100644 --- a/builtin-help.c +++ b/builtin-help.c @@ -418,7 +418,7 @@ int cmd_help(int argc, const char **argv, const char *prefix) { int nongit; const char *alias; - unsigned int longest = load_command_list("git-", &main_cmds, &other_cmds); + load_command_list("git-", &main_cmds, &other_cmds); setup_git_directory_gently(&nongit); git_config(git_help_config, NULL); @@ -428,7 +428,7 @@ int cmd_help(int argc, const char **argv, const char *prefix) if (show_all) { printf("usage: %s\n\n", git_usage_string); - list_commands("git commands", longest, &main_cmds, &other_cmds); + list_commands("git commands", &main_cmds, &other_cmds); printf("%s\n", git_more_info_string); return 0; } diff --git a/builtin-merge.c b/builtin-merge.c index d6bcbec..dcd08f7 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -80,7 +80,7 @@ static struct strategy *get_strategy(const char *name) int i; struct strategy *ret; static struct cmdnames main_cmds, other_cmds; - static int longest; + static int loaded; if (!name) return NULL; @@ -89,14 +89,14 @@ static struct strategy *get_strategy(const char *name) if (!strcmp(name, all_strategy[i].name)) return &all_strategy[i]; - if (!longest) { + if (!loaded) { struct cmdnames not_strategies; + loaded = 1; memset(&main_cmds, 0, sizeof(struct cmdnames)); memset(&other_cmds, 0, sizeof(struct cmdnames)); memset(¬_strategies, 0, sizeof(struct cmdnames)); - longest = load_command_list("git-merge-", &main_cmds, - &other_cmds); + load_command_list("git-merge-", &main_cmds, &other_cmds); for (i = 0; i < main_cmds.cnt; i++) { int j, found = 0; struct cmdname *ent = main_cmds.names[i]; diff --git a/help.c b/help.c index 1afbac0..a17a746 100644 --- a/help.c +++ b/help.c @@ -133,11 +133,10 @@ static int is_executable(const char *name) return st.st_mode & S_IXUSR; } -static unsigned int list_commands_in_dir(struct cmdnames *cmds, +static void list_commands_in_dir(struct cmdnames *cmds, const char *path, const char *prefix) { - unsigned int longest = 0; int prefix_len; DIR *dir = opendir(path); struct dirent *de; @@ -145,7 +144,7 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds, int len; if (!dir) - return 0; + return; if (!prefix) prefix = "git-"; prefix_len = strlen(prefix); @@ -168,29 +167,22 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds, if (has_extension(de->d_name, ".exe")) entlen -= 4; - if (longest < entlen) - longest = entlen; - add_cmdname(cmds, de->d_name + prefix_len, entlen); } closedir(dir); strbuf_release(&buf); - - return longest; } -unsigned int load_command_list(const char *prefix, +void load_command_list(const char *prefix, struct cmdnames *main_cmds, struct cmdnames *other_cmds) { - unsigned int longest = 0; - unsigned int len; const char *env_path = getenv("PATH"); char *paths, *path, *colon; const char *exec_path = git_exec_path(); if (exec_path) - longest = list_commands_in_dir(main_cmds, exec_path, prefix); + list_commands_in_dir(main_cmds, exec_path, prefix); if (!env_path) { fprintf(stderr, "PATH not set\n"); @@ -202,9 +194,7 @@ unsigned int load_command_list(const char *prefix, if ((colon = strchr(path, PATH_SEP))) *colon = 0; - len = list_commands_in_dir(other_cmds, path, prefix); - if (len > longest) - longest = len; + list_commands_in_dir(other_cmds, path, prefix); if (!colon) break; @@ -220,14 +210,20 @@ unsigned int load_command_list(const char *prefix, sizeof(*other_cmds->names), cmdname_compare); uniq(other_cmds); exclude_cmds(other_cmds, main_cmds); - - return longest; } -void list_commands(const char *title, unsigned int longest, - struct cmdnames *main_cmds, struct cmdnames *other_cmds) +void list_commands(const char *title, struct cmdnames *main_cmds, + struct cmdnames *other_cmds) { const char *exec_path = git_exec_path(); + int i, longest = 0; + + for (i = 0; i < main_cmds->cnt; i++) + if (longest < main_cmds->names[i]->len) + longest = main_cmds->names[i]->len; + for (i = 0; i < other_cmds->cnt; i++) + if (longest < other_cmds->names[i]->len) + longest = other_cmds->names[i]->len; if (main_cmds->cnt) { printf("available %s in '%s'\n", title, exec_path); diff --git a/help.h b/help.h index 3f1ae89..2733433 100644 --- a/help.h +++ b/help.h @@ -16,14 +16,14 @@ static inline void mput_char(char c, unsigned int num) putchar(c); } -unsigned int load_command_list(const char *prefix, +void load_command_list(const char *prefix, struct cmdnames *main_cmds, struct cmdnames *other_cmds); void add_cmdname(struct cmdnames *cmds, const char *name, int len); /* Here we require that excludes is a sorted list. */ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); int is_in_cmdlist(struct cmdnames *c, const char *s); -void list_commands(const char *title, unsigned int longest, - struct cmdnames *main_cmds, struct cmdnames *other_cmds); +void list_commands(const char *title, struct cmdnames *main_cmds, + struct cmdnames *other_cmds); #endif /* HELP_H */ -- 1.6.0.1.150.g5966 ^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH updated] git wrapper: DWIM mistyped commands 2008-08-28 17:15 [PATCH] Remove calculation of the longest command name from where it is not used Alex Riesen, Alex Riesen @ 2008-08-28 21:27 ` Alex Riesen 2008-08-28 21:28 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen ` (3 more replies) 0 siblings, 4 replies; 29+ messages in thread From: Alex Riesen @ 2008-08-28 21:27 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Johannes Schindelin From: Johannes Schindelin <Johannes.Schindelin@gmx.de> This patch introduces a modified Damerau-Levenshtein algorithm into Git's code base, and uses it with the following penalties to show some similar commands when an unknown command was encountered: swap = 0, insertion = 1, substitution = 2, deletion = 4 A typical output would now look like this: $ git sm git: 'sm' is not a git-command. See 'git --help'. Did you mean one of these? am rm The cut-off is at similarity rating 6, which was empirically determined to give sensible results. As a convenience, if there is only one candidate, Git continues under the assumption that the user mistyped it. Example: $ git reabse WARNING: You called a Git program named 'reabse', which does not exist. Continuing under the assumption that you meant 'rebase' [...] Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alex Riesen <raa.lkml@gmail.com> --- Alex Riesen, Thu, Aug 28, 2008 19:15:33 +0200: > > BTW, the Johannesses typo-guesser conflicts heavily in recent master. > Pity. I'm going to rebase it and send out the rebased version. > As promised. Johannes, I remember you talking about some problem with that code (it should be the latest we talked about on the list), but I am not sure I have updated the algorithm accordingly. Could you please check? Makefile | 2 + builtin.h | 2 +- git.c | 4 ++- help.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- levenshtein.c | 47 +++++++++++++++++++++++++++++++++++++++++ levenshtein.h | 8 +++++++ 6 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 levenshtein.c create mode 100644 levenshtein.h diff --git a/Makefile b/Makefile index bf400e6..3daa6dc 100644 --- a/Makefile +++ b/Makefile @@ -358,6 +358,7 @@ LIB_H += graph.h LIB_H += grep.h LIB_H += hash.h LIB_H += help.h +LIB_H += levenshtein.h LIB_H += list-objects.h LIB_H += ll-merge.h LIB_H += log-tree.h @@ -433,6 +434,7 @@ LIB_OBJS += hash.o LIB_OBJS += help.o LIB_OBJS += ident.o LIB_OBJS += interpolate.o +LIB_OBJS += levenshtein.o LIB_OBJS += list-objects.o LIB_OBJS += ll-merge.o LIB_OBJS += lockfile.o diff --git a/builtin.h b/builtin.h index f3502d3..e67cb20 100644 --- a/builtin.h +++ b/builtin.h @@ -11,7 +11,7 @@ extern const char git_usage_string[]; extern const char git_more_info_string[]; extern void list_common_cmds_help(void); -extern void help_unknown_cmd(const char *cmd); +extern const char *help_unknown_cmd(const char *cmd); extern void prune_packed_objects(int); extern int read_line_with_nul(char *buf, int size, FILE *file); extern int fmt_merge_msg(int merge_summary, struct strbuf *in, diff --git a/git.c b/git.c index 37b1d76..54c5bfa 100644 --- a/git.c +++ b/git.c @@ -499,7 +499,9 @@ int main(int argc, const char **argv) cmd, argv[0]); exit(1); } - help_unknown_cmd(cmd); + argv[0] = help_unknown_cmd(cmd); + handle_internal_command(argc, argv); + execv_dashed_external(argv); } fprintf(stderr, "Failed to run command '%s': %s\n", diff --git a/help.c b/help.c index 1afbac0..981fb02 100644 --- a/help.c +++ b/help.c @@ -1,6 +1,7 @@ #include "cache.h" #include "builtin.h" #include "exec_cmd.h" +#include "levenshtein.h" #include "help.h" /* most GUI terminals set COLUMNS (although some don't export it) */ @@ -257,9 +258,70 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) return 0; } -void help_unknown_cmd(const char *cmd) +static const char *levenshtein_cmd; +static int similarity(const char *cmd) { + return levenshtein(levenshtein_cmd, cmd, 0, 2, 1, 4); +} + +static int levenshtein_compare(const void *p1, const void *p2) { + const struct cmdname *const *c1 = p1, *const *c2 = p2; + const char *s1 = (*c1)->name, *s2 = (*c2)->name; + int l1 = similarity(s1); + int l2 = similarity(s2); + return l1 != l2 ? l1 - l2 : strcmp(s1, s2); +} + +const char *help_unknown_cmd(const char *cmd) +{ + int i, best_similarity = 0; + char cwd[PATH_MAX]; + static struct cmdnames main_cmds, other_cmds; + + if (!getcwd(cwd, sizeof(cwd))) { + error("Could not get current working directory"); + cwd[0] = '\0'; + } + + if (!main_cmds.cnt && !other_cmds.cnt) + load_command_list("git-", &main_cmds, &other_cmds); + + ALLOC_GROW(main_cmds.names, main_cmds.cnt + other_cmds.cnt, + main_cmds.alloc); + memcpy(main_cmds.names + main_cmds.cnt, other_cmds.names, + other_cmds.cnt * sizeof(other_cmds.names[0])); + main_cmds.cnt += other_cmds.cnt; + + levenshtein_cmd = cmd; + qsort(main_cmds.names, main_cmds.cnt, + sizeof(*main_cmds.names), levenshtein_compare); + + if (!main_cmds.cnt) + die ("Uh oh. Your system reports no Git commands at all."); + best_similarity = similarity(main_cmds.names[0]->name); + if (main_cmds.cnt < 2 || + best_similarity < similarity(main_cmds.names[1]->name)) { + if (!*cwd) + exit(1); + if (chdir(cwd)) + die ("Could not change directory back to '%s'", cwd); + fprintf(stderr, "WARNING: You called a Git program named '%s', " + "which does not exist.\n" + "Continuing under the assumption that you meant '%s'\n", + cmd, main_cmds.names[0]->name); + return main_cmds.names[0]->name; + } + fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd); + + if (best_similarity < 6) { + fprintf(stderr, "\nDid you mean one of these?\n"); + + for (i = 0; i < main_cmds.cnt && best_similarity == + similarity(main_cmds.names[i]->name); i++) + fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); + } + exit(1); } diff --git a/levenshtein.c b/levenshtein.c new file mode 100644 index 0000000..db52f2c --- /dev/null +++ b/levenshtein.c @@ -0,0 +1,47 @@ +#include "cache.h" +#include "levenshtein.h" + +int levenshtein(const char *string1, const char *string2, + int w, int s, int a, int d) +{ + int len1 = strlen(string1), len2 = strlen(string2); + int *row0 = xmalloc(sizeof(int) * (len2 + 1)); + int *row1 = xmalloc(sizeof(int) * (len2 + 1)); + int *row2 = xmalloc(sizeof(int) * (len2 + 1)); + int i, j; + + for (j = 0; j <= len2; j++) + row1[j] = j * a; + for (i = 0; i < len1; i++) { + int *dummy; + + row2[0] = (i + 1) * d; + for (j = 0; j < len2; j++) { + /* substitution */ + row2[j + 1] = row1[j] + s * (string1[i] != string2[j]); + /* swap */ + if (i > 0 && j > 0 && string1[i - 1] == string2[j] && + string1[i] == string2[j - 1] && + row2[j + 1] > row0[j - 1] + w) + row2[j + 1] = row0[j - 1] + w; + /* deletion */ + if (j + 1 < len2 && row2[j + 1] > row1[j + 1] + d) + row2[j + 1] = row1[j + 1] + d; + /* insertion */ + if (row2[j + 1] > row2[j] + a) + row2[j + 1] = row2[j] + a; + } + + dummy = row0; + row0 = row1; + row1 = row2; + row2 = dummy; + } + + i = row1[len2]; + free(row0); + free(row1); + free(row2); + + return i; +} diff --git a/levenshtein.h b/levenshtein.h new file mode 100644 index 0000000..0173abe --- /dev/null +++ b/levenshtein.h @@ -0,0 +1,8 @@ +#ifndef LEVENSHTEIN_H +#define LEVENSHTEIN_H + +int levenshtein(const char *string1, const char *string2, + int swap_penalty, int substition_penalty, + int insertion_penalty, int deletion_penalty); + +#endif -- 1.6.0.106.g97c8 ^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-08-28 21:27 ` [PATCH updated] git wrapper: DWIM mistyped commands Alex Riesen @ 2008-08-28 21:28 ` Alex Riesen 2008-08-29 10:11 ` Andreas Ericsson 2008-09-08 6:50 ` Junio C Hamano 2008-08-29 14:58 ` [PATCH updated] git wrapper: DWIM mistyped commands Mikael Magnusson ` (2 subsequent siblings) 3 siblings, 2 replies; 29+ messages in thread From: Alex Riesen @ 2008-08-28 21:28 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Johannes Schindelin It is off(0) by default, to avoid scaring people unless they asked to. If set to a non-0 value, wait for that amount of deciseconds before running the corrected command. Suggested by Junio, so he has a chance to hit Ctrl-C. Signed-off-by: Alex Riesen <raa.lkml@gmail.com> --- Documentation/config.txt | 9 ++++++++ help.c | 50 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index af57d94..8c644ab 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -790,6 +790,15 @@ help.format:: Values 'man', 'info', 'web' and 'html' are supported. 'man' is the default. 'web' and 'html' are the same. +help.autocorrect:: + Automatically correct and execute mistyped commands after + waiting for the given number of deciseconds (0.1 sec). If more + than one command can be deduced from the entered text, nothing + will be executed. If the value of this option is negative, + the corrected command will be executed immediately. If the + value is 0 - the command will be just shown but not executed. + This is the default. + http.proxy:: Override the HTTP proxy, normally configured using the 'http_proxy' environment variable (see linkgit:curl[1]). This can be overridden diff --git a/help.c b/help.c index 981fb02..7bfbbcd 100644 --- a/help.c +++ b/help.c @@ -38,6 +38,16 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len) cmds->names[cmds->cnt++] = ent; } +static void clean_cmdnames(struct cmdnames *cmds) +{ + int i; + for (i = 0; i < cmds->cnt; ++i) + free(cmds->names[i]); + free(cmds->names); + cmds->cnt = 0; + cmds->alloc = 0; +} + static int cmdname_compare(const void *a_, const void *b_) { struct cmdname *a = *(struct cmdname **)a_; @@ -258,6 +268,16 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) return 0; } +static int autocorrect; + +static int git_unknown_cmd_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "help.autocorrect")) + autocorrect = git_config_int(var,value); + + return git_default_config(var, value, cb); +} + static const char *levenshtein_cmd; static int similarity(const char *cmd) { return levenshtein(levenshtein_cmd, cmd, 0, 2, 1, 4); @@ -274,7 +294,7 @@ static int levenshtein_compare(const void *p1, const void *p2) const char *help_unknown_cmd(const char *cmd) { - int i, best_similarity = 0; + int i, best_similarity = 0, n; char cwd[PATH_MAX]; static struct cmdnames main_cmds, other_cmds; @@ -283,6 +303,7 @@ const char *help_unknown_cmd(const char *cmd) cwd[0] = '\0'; } + git_config(git_unknown_cmd_config, NULL); if (!main_cmds.cnt && !other_cmds.cnt) load_command_list("git-", &main_cmds, &other_cmds); @@ -299,26 +320,39 @@ const char *help_unknown_cmd(const char *cmd) if (!main_cmds.cnt) die ("Uh oh. Your system reports no Git commands at all."); best_similarity = similarity(main_cmds.names[0]->name); - if (main_cmds.cnt < 2 || - best_similarity < similarity(main_cmds.names[1]->name)) { + n = 1; + while (n < main_cmds.cnt && + best_similarity == similarity(main_cmds.names[n]->name)) + ++n; + if (autocorrect && n == 1) { + const char *assumed; if (!*cwd) exit(1); if (chdir(cwd)) die ("Could not change directory back to '%s'", cwd); + assumed = main_cmds.names[0]->name; + main_cmds.names[0] = NULL; + clean_cmdnames(&other_cmds); + clean_cmdnames(&main_cmds); fprintf(stderr, "WARNING: You called a Git program named '%s', " "which does not exist.\n" "Continuing under the assumption that you meant '%s'\n", - cmd, main_cmds.names[0]->name); - return main_cmds.names[0]->name; + cmd, assumed); + if (autocorrect > 0) { + fprintf(stderr, "in %0.1f seconds automatically...\n", + (float)autocorrect/10.0); + poll(NULL, 0, autocorrect * 100); + } + return assumed; } fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd); if (best_similarity < 6) { - fprintf(stderr, "\nDid you mean one of these?\n"); + fprintf(stderr, "\nDid you mean %s?\n", + n < 2 ? "this": "one of these"); - for (i = 0; i < main_cmds.cnt && best_similarity == - similarity(main_cmds.names[i]->name); i++) + for (i = 0; i < n; i++) fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); } -- 1.6.0.106.g97c8 ^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-08-28 21:28 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen @ 2008-08-29 10:11 ` Andreas Ericsson 2008-09-08 6:50 ` Junio C Hamano 1 sibling, 0 replies; 29+ messages in thread From: Andreas Ericsson @ 2008-08-29 10:11 UTC (permalink / raw) To: Alex Riesen; +Cc: git, Junio C Hamano, Johannes Schindelin Alex Riesen wrote: > It is off(0) by default, to avoid scaring people unless they asked to. > If set to a non-0 value, wait for that amount of deciseconds before > running the corrected command. > > Suggested by Junio, so he has a chance to hit Ctrl-C. > I'm just plain loving this :) Liked-by: Andreas Ericsson <ae@op5.se> -- Andreas Ericsson andreas.ericsson@op5.se OP5 AB www.op5.se Tel: +46 8-230225 Fax: +46 8-230231 ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-08-28 21:28 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen 2008-08-29 10:11 ` Andreas Ericsson @ 2008-09-08 6:50 ` Junio C Hamano 1 sibling, 0 replies; 29+ messages in thread From: Junio C Hamano @ 2008-09-08 6:50 UTC (permalink / raw) To: Alex Riesen; +Cc: git, Johannes Schindelin Alex Riesen <raa.lkml@gmail.com> writes: > It is off(0) by default, to avoid scaring people unless they asked to. I do not think this is off by default, by the way. "off by default" means that you would not waste extra cycles to compute the list of suggestions. I am not suggesting that it should be "off" by default in that sense, though. I am just pointing out that it is not described correctly. I also noticed that this does not seem to pay attention to mistyped aliases. Is it by design, oversight, or lazyness? ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH updated] git wrapper: DWIM mistyped commands 2008-08-28 21:27 ` [PATCH updated] git wrapper: DWIM mistyped commands Alex Riesen 2008-08-28 21:28 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen @ 2008-08-29 14:58 ` Mikael Magnusson 2008-08-30 10:12 ` Alex Riesen 2008-08-30 15:36 ` Junio C Hamano [not found] ` <a2075f4c0808301510g1af01b14kd58da12dc2e80f93@mail.gmail.com> 3 siblings, 1 reply; 29+ messages in thread From: Mikael Magnusson @ 2008-08-29 14:58 UTC (permalink / raw) To: Alex Riesen; +Cc: git, Junio C Hamano, Johannes Schindelin 2008/8/28 Alex Riesen <raa.lkml@gmail.com>: > From: Johannes Schindelin <Johannes.Schindelin@gmx.de> > > This patch introduces a modified Damerau-Levenshtein algorithm into > Git's code base, and uses it with the following penalties to show some > similar commands when an unknown command was encountered: > > swap = 0, insertion = 1, substitution = 2, deletion = 4 > > A typical output would now look like this: > > $ git sm > git: 'sm' is not a git-command. See 'git --help'. > > Did you mean one of these? > am > rm > > The cut-off is at similarity rating 6, which was empirically determined > to give sensible results. I merged the branch in pu into next, which I think should work, but I get these segfaults for some commands... I tried running in gdb but even with -g3 I only get nonsense backtraces, not sure why. % git puhs WARNING: You called a Git program named 'puhs', which does not exist. Continuing under the assumption that you meant 'push' in 2.0 seconds automatically... zsh: segmentation fault git puhs % git ma WARNING: You called a Git program named 'ma', which does not exist. Continuing under the assumption that you meant 'am' in 2.0 seconds automatically... Nothing to do. At this point I thought builtins crashed and scripts were fine, but... % git ada WARNING: You called a Git program named 'ada', which does not exist. Continuing under the assumption that you meant 'add' in 2.0 seconds automatically... Nothing specified, nothing added. Maybe you wanted to say 'git add .'? However, % git ada git.c WARNING: You called a Git program named 'ada', which does not exist. Continuing under the assumption that you meant 'add' in 2.0 seconds automatically... zsh: segmentation fault git ada git.c -- Mikael Magnusson ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH updated] git wrapper: DWIM mistyped commands 2008-08-29 14:58 ` [PATCH updated] git wrapper: DWIM mistyped commands Mikael Magnusson @ 2008-08-30 10:12 ` Alex Riesen 2008-08-30 10:33 ` Mikael Magnusson 0 siblings, 1 reply; 29+ messages in thread From: Alex Riesen @ 2008-08-30 10:12 UTC (permalink / raw) To: Mikael Magnusson; +Cc: git, Junio C Hamano, Johannes Schindelin 2008/8/29 Mikael Magnusson <mikachu@gmail.com>: > I merged the branch in pu into next, which I think should work, but I get > these segfaults for some commands... I tried running in gdb but even with Can't reproduce in master, will try with next later, am busy right now, sorry. > -g3 I only get nonsense backtraces, not sure why. Remove -O2 from CFLAGS: $ make CFLAGS="-O0 -ggdb" ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH updated] git wrapper: DWIM mistyped commands 2008-08-30 10:12 ` Alex Riesen @ 2008-08-30 10:33 ` Mikael Magnusson 2008-08-31 13:50 ` [PATCH] " Alex Riesen 2008-08-31 13:57 ` [PATCH updated] " Alex Riesen 0 siblings, 2 replies; 29+ messages in thread From: Mikael Magnusson @ 2008-08-30 10:33 UTC (permalink / raw) To: Alex Riesen; +Cc: git, Junio C Hamano, Johannes Schindelin 2008/8/30 Alex Riesen <raa.lkml@gmail.com>: > 2008/8/29 Mikael Magnusson <mikachu@gmail.com>: >> I merged the branch in pu into next, which I think should work, but I get >> these segfaults for some commands... I tried running in gdb but even with > > Can't reproduce in master, will try with next later, am busy right now, sorry. > >> -g3 I only get nonsense backtraces, not sure why. > > Remove -O2 from CFLAGS: > > $ make CFLAGS="-O0 -ggdb" Hm, I only had CFLAGS set in the env, maybe that's not enough, or maybe it was that I had it set to -gdwarf-2 -g3 instead of -ggdb3. At any rate, I got this now: % git fotch Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0xa7ce56c0 (LWP 13043)] 0x41b61490 in ?? () from /lib/libc.so.6 (gdb) bt #0 0x41b61490 in ?? () from /lib/libc.so.6 #1 0x41b634b0 in realloc () from /lib/libc.so.6 #2 0x41b62fdb in malloc () from /lib/libc.so.6 #3 0x080f63e6 in xmalloc (size=24) at wrapper.c:20 #4 0x080f6471 in xmemdupz (data=0x816a44f, len=23) at wrapper.c:45 #5 0x080f64e9 in xstrndup (str=0x816a44f "refs/remotes/origin/man", len=23) at wrapper.c:54 #6 0x080dc05c in parse_refspec_internal (nr_refspec=6, refspec=0x816a010, fetch=1, verify=0) at remote.c:505 #7 0x080dc3ca in parse_fetch_refspec (nr_refspec=6, refspec=0x816a010) at remote.c:613 #8 0x080dc512 in remote_get (name=0x8169458 "origin") at remote.c:649 #9 0x0806cd4d in cmd_fetch (argc=0, argv=0xafc2d578, prefix=0x0) at builtin-fetch.c:620 #10 0x0804bb3c in run_command (p=0x8123498, argc=1, argv=0xafc2d578) at git.c:238 #11 0x0804bccc in handle_internal_command (argc=1, argv=0xafc2d578) at git.c:380 #12 0x0804bfea in main (argc=1, argv=0xafc2d578) at git.c:500 (gdb) bt full #0 0x41b61490 in ?? () from /lib/libc.so.6 No symbol table info available. #1 0x41b634b0 in realloc () from /lib/libc.so.6 No symbol table info available. #2 0x41b62fdb in malloc () from /lib/libc.so.6 No symbol table info available. #3 0x080f63e6 in xmalloc (size=24) at wrapper.c:20 ret = (void *) 0x0 #4 0x080f6471 in xmemdupz (data=0x816a44f, len=23) at wrapper.c:45 p = 0x0 #5 0x080f64e9 in xstrndup (str=0x816a44f "refs/remotes/origin/man", len=23) at wrapper.c:54 p = 0x0 #6 0x080dc05c in parse_refspec_internal (nr_refspec=6, refspec=0x816a010, fetch=1, verify=0) at remote.c:505 rlen = 23 llen = 0 is_glob = 0 lhs = 0x816a440 "refs/heads/man:refs/remotes/origin/man" rhs = 0x816a44f "refs/remotes/origin/man" i = 4 st = 0 rs = (struct refspec *) 0x8169d88 #7 0x080dc3ca in parse_fetch_refspec (nr_refspec=6, refspec=0x816a010) at remote.c:613 No locals. #8 0x080dc512 in remote_get (name=0x8169458 "origin") at remote.c:649 ret = (struct remote *) 0x8169ef0 #9 0x0806cd4d in cmd_fetch (argc=0, argv=0xafc2d578, prefix=0x0) at builtin-fetch.c:620 remote = (struct remote *) 0x804b2d4 i = 1 ---Type <return> to continue, or q <return> to quit--- ref_nr = 0 exit_code = 0 refs = (const char **) 0x0 #10 0x0804bb3c in run_command (p=0x8123498, argc=1, argv=0xafc2d578) at git.c:238 status = -1346186104 st = {st_dev = 582794214293012544, __pad1 = 0, __st_ino = 1103151906, st_mode = 5, st_nlink = 0, st_uid = 0, st_gid = 135692352, st_rdev = 582794423264539853, __pad2 = 54328, st_size = 579290465871466043, st_blksize = 0, st_blocks = 577791495034295448, st_atim = { tv_sec = -1346180215, tv_nsec = 1103274272}, st_mtim = {tv_sec = 135692296, tv_nsec = 1103269876}, st_ctim = {tv_sec = 1090632864, tv_nsec = 0}, st_ino = 580626572881613928} prefix = 0x0 #11 0x0804bccc in handle_internal_command (argc=1, argv=0xafc2d578) at git.c:380 p = (struct cmd_struct *) 0x8123498 cmd = 0x8169a3c "fetch" i = 26 commands = {{cmd = 0x8104ebd "add", fn = 0x804c905 <cmd_add>, option = 5}, { cmd = 0x8104ec1 "annotate", fn = 0x804cc70 <cmd_annotate>, option = 1}, { cmd = 0x8104eca "apply", fn = 0x805362c <cmd_apply>, option = 0}, { cmd = 0x8104ed0 "archive", fn = 0x80542e5 <cmd_archive>, option = 0}, { cmd = 0x8104ed8 "blame", fn = 0x805889e <cmd_blame>, option = 1}, {cmd = 0x8104ede "branch", fn = 0x805a62f <cmd_branch>, option = 1}, {cmd = 0x8104ee5 "bundle", fn = 0x805adb8 <cmd_bundle>, option = 0}, {cmd = 0x8104eec "cat-file", fn = 0x805b6b1 <cmd_cat_file>, option = 1}, {cmd = 0x8104ef5 "checkout", fn = 0x805d865 <cmd_checkout>, option = 5}, {cmd = 0x8104efe "checkout-index", fn = 0x805c123 <cmd_checkout_index>, option = 5}, {cmd = 0x8104f0d "check-ref-format", fn = 0x805bb84 <cmd_check_ref_format>, option = 0}, {cmd = 0x8104f1e "check-attr", fn = 0x805b94c <cmd_check_attr>, option = 1}, {cmd = 0x8104f29 "cherry", fn = 0x8078c23 <cmd_cherry>, option = 1}, {cmd = 0x8104f30 "cherry-pick", ---Type <return> to continue, or q <return> to quit--- fn = 0x8094c96 <cmd_cherry_pick>, option = 5}, {cmd = 0x8104f3c "clone", fn = 0x805ef85 <cmd_clone>, option = 0}, {cmd = 0x8104f42 "clean", fn = 0x805ddb3 <cmd_clean>, option = 5}, {cmd = 0x8104f48 "commit", fn = 0x8062115 <cmd_commit>, option = 5}, {cmd = 0x8104f4f "commit-tree", fn = 0x805fcbd <cmd_commit_tree>, option = 1}, {cmd = 0x8104f5b "config", fn = 0x80632c5 <cmd_config>, option = 0}, {cmd = 0x8104f62 "count-objects", fn = 0x8063f14 <cmd_count_objects>, option = 1}, {cmd = 0x8104f70 "describe", fn = 0x8064ce4 <cmd_describe>, option = 1}, {cmd = 0x8104f79 "diff", fn = 0x80663da <cmd_diff>, option = 0}, {cmd = 0x8104f7e "diff-files", fn = 0x80650d8 <cmd_diff_files>, option = 1}, {cmd = 0x8104f89 "diff-index", fn = 0x806530c <cmd_diff_index>, option = 1}, {cmd = 0x8104f94 "diff-tree", fn = 0x80657f0 <cmd_diff_tree>, option = 1}, {cmd = 0x8104f9e "fast-export", fn = 0x8067c10 <cmd_fast_export>, option = 1}, {cmd = 0x8104faa "fetch", fn = 0x806ccb5 <cmd_fetch>, option = 1}, {cmd = 0x8104fb0 "fetch-pack", fn = 0x806adeb <cmd_fetch_pack>, option = 1}, {cmd = 0x8104fbb "fetch--tool", fn = 0x80692be <cmd_fetch__tool>, option = 1}, {cmd = 0x8104fc7 "fmt-merge-msg", fn = 0x806defb <cmd_fmt_merge_msg>, option = 1}, {cmd = 0x8104fd5 "for-each-ref", fn = 0x806fc5b <cmd_for_each_ref>, option = 1}, {cmd = 0x8104fe2 "format-patch", fn = 0x8077b7e <cmd_format_patch>, option = 1}, {cmd = 0x8104fef "fsck", fn = 0x8071106 <cmd_fsck>, option = 1}, {cmd = 0x8104ff4 "fsck-objects", fn = 0x8071106 <cmd_fsck>, option = 1}, {cmd = 0x8105001 "gc", fn = 0x80719b9 <cmd_gc>, option = 1}, {cmd = 0x8105004 "get-tar-commit-id", fn = 0x809be66 <cmd_get_tar_commit_id>, option = 0}, {cmd = 0x8105016 "grep", fn = 0x807308e <cmd_grep>, option = 3}, { cmd = 0x810501b "help", fn = 0x8074bd2 <cmd_help>, option = 0}, { cmd = 0x8105020 "http-fetch", fn = 0x80a057c <cmd_http_fetch>, option = 1}, { cmd = 0x810502b "init", fn = 0x8075a8d <cmd_init_db>, option = 0}, { cmd = 0x8105030 "init-db", fn = 0x8075a8d <cmd_init_db>, option = 0}, { cmd = 0x8105038 "log", fn = 0x8076c36 <cmd_log>, option = 3}, {cmd = 0x810503c "ls-files", fn = 0x8079e63 <cmd_ls_files>, option = 1}, {cmd = 0x8105045 "ls-tree", ---Type <return> to continue, or q <return> to quit--- fn = 0x807ae13 <cmd_ls_tree>, option = 1}, {cmd = 0x810504d "ls-remote", fn = 0x807a797 <cmd_ls_remote>, option = 0}, {cmd = 0x8105057 "mailinfo", fn = 0x807d1ef <cmd_mailinfo>, option = 0}, {cmd = 0x8105060 "mailsplit", fn = 0x807db6e <cmd_mailsplit>, option = 0}, {cmd = 0x810506a "merge", fn = 0x808016f <cmd_merge>, option = 5}, {cmd = 0x8105070 "merge-base", fn = 0x8080fe6 <cmd_merge_base>, option = 1}, {cmd = 0x810507b "merge-file", fn = 0x80810f0 <cmd_merge_file>, option = 0}, {cmd = 0x8105086 "merge-ours", fn = 0x8081438 <cmd_merge_ours>, option = 1}, {cmd = 0x8105091 "merge-recursive", fn = 0x8084c44 <cmd_merge_recursive>, option = 5}, {cmd = 0x81050a1 "merge-subtree", fn = 0x8084c44 <cmd_merge_recursive>, option = 5}, {cmd = 0x81050af "mv", fn = 0x808509d <cmd_mv>, option = 5}, {cmd = 0x81050b2 "name-rev", fn = 0x8086090 <cmd_name_rev>, option = 1}, {cmd = 0x81050bb "pack-objects", fn = 0x808aa1a <cmd_pack_objects>, option = 1}, {cmd = 0x81050c8 "peek-remote", fn = 0x807a797 <cmd_ls_remote>, option = 0}, {cmd = 0x81050d4 "pickaxe", fn = 0x805889e <cmd_blame>, option = 1}, {cmd = 0x81050dc "prune", fn = 0x808badd <cmd_prune>, option = 1}, {cmd = 0x81050e2 "prune-packed", fn = 0x808b60a <cmd_prune_packed>, option = 1}, {cmd = 0x81050ef "push", fn = 0x808bf9f <cmd_push>, option = 1}, {cmd = 0x81050f4 "read-tree", fn = 0x808c418 <cmd_read_tree>, option = 1}, {cmd = 0x81050fe "reflog", fn = 0x808e264 <cmd_reflog>, option = 1}, {cmd = 0x8105105 "remote", fn = 0x8090076 <cmd_remote>, option = 1}, {cmd = 0x810510c "repo-config", fn = 0x80632c5 <cmd_config>, option = 0}, {cmd = 0x8105118 "rerere", fn = 0x8090669 <cmd_rerere>, option = 1}, {cmd = 0x810511f "reset", fn = 0x8090e44 <cmd_reset>, option = 1}, {cmd = 0x8105125 "rev-list", fn = 0x8092096 <cmd_rev_list>, option = 1}, {cmd = 0x810512e "rev-parse", fn = 0x80932a0 <cmd_rev_parse>, option = 0}, {cmd = 0x8105138 "revert", fn = 0x8094c4e <cmd_revert>, option = 5}, {cmd = 0x810513f "rm", fn = 0x80950b5 <cmd_rm>, option = 1}, {cmd = 0x8105142 "send-pack", fn = 0x8096982 <cmd_send_pack>, option = 1}, { cmd = 0x810514c "shortlog", fn = 0x809769a <cmd_shortlog>, option = 2}, { ---Type <return> to continue, or q <return> to quit--- cmd = 0x8105155 "show-branch", fn = 0x8098c0c <cmd_show_branch>, option = 1}, { cmd = 0x8105161 "show", fn = 0x80767e8 <cmd_show>, option = 3}, {cmd = 0x8105166 "status", fn = 0x8061d60 <cmd_status>, option = 5}, {cmd = 0x810516d "stripspace", fn = 0x809a78d <cmd_stripspace>, option = 0}, {cmd = 0x8105178 "symbolic-ref", fn = 0x809a8db <cmd_symbolic_ref>, option = 1}, {cmd = 0x8105185 "tag", fn = 0x809b51a <cmd_tag>, option = 1}, {cmd = 0x8105189 "tar-tree", fn = 0x809bca4 <cmd_tar_tree>, option = 0}, {cmd = 0x8105192 "unpack-objects", fn = 0x809d17c <cmd_unpack_objects>, option = 1}, {cmd = 0x81051a1 "update-index", fn = 0x809e85c <cmd_update_index>, option = 1}, {cmd = 0x81051ae "update-ref", fn = 0x809f1d8 <cmd_update_ref>, option = 1}, {cmd = 0x81051b9 "upload-archive", fn = 0x809f7a8 <cmd_upload_archive>, option = 0}, {cmd = 0x81051c8 "verify-tag", fn = 0x80a0353 <cmd_verify_tag>, option = 1}, {cmd = 0x81051d3 "version", fn = 0x80c5376 <cmd_version>, option = 0}, {cmd = 0x81051db "whatchanged", fn = 0x80764c0 <cmd_whatchanged>, option = 3}, {cmd = 0x81051e7 "write-tree", fn = 0x80a0434 <cmd_write_tree>, option = 1}, {cmd = 0x81051f2 "verify-pack", fn = 0x809ff24 <cmd_verify_pack>, option = 0}, {cmd = 0x81051fe "show-ref", fn = 0x809a0cc <cmd_show_ref>, option = 1}, {cmd = 0x8105207 "pack-refs", fn = 0x808b2e8 <cmd_pack_refs>, option = 1}} ext = "" #12 0x0804bfea in main (argc=1, argv=0xafc2d578) at git.c:500 cmd = 0xafc2eb89 "fotch" slash = 0xafc2eb85 "git" done_alias = 0 -- Mikael Magnusson ^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH] git wrapper: DWIM mistyped commands 2008-08-30 10:33 ` Mikael Magnusson @ 2008-08-31 13:50 ` Alex Riesen 2008-08-31 13:54 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen 2008-09-01 14:42 ` [PATCH] git wrapper: DWIM mistyped commands Mikael Magnusson 2008-08-31 13:57 ` [PATCH updated] " Alex Riesen 1 sibling, 2 replies; 29+ messages in thread From: Alex Riesen @ 2008-08-31 13:50 UTC (permalink / raw) To: Junio C Hamano; +Cc: git, Johannes Schindelin, Mikael Magnusson From: Johannes Schindelin <Johannes.Schindelin@gmx.de> This patch introduces a modified Damerau-Levenshtein algorithm into Git's code base, and uses it with the following penalties to show some similar commands when an unknown command was encountered: swap = 0, insertion = 1, substitution = 2, deletion = 4 A typical output would now look like this: $ git sm git: 'sm' is not a git-command. See 'git --help'. Did you mean one of these? am rm The cut-off is at similarity rating 6, which was empirically determined to give sensible results. As a convenience, if there is only one candidate, Git continues under the assumption that the user mistyped it. Example: $ git reabse WARNING: You called a Git program named 'reabse', which does not exist. Continuing under the assumption that you meant 'rebase' [...] Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alex Riesen <raa.lkml@gmail.com> --- Junio C Hamano, Sat, Aug 30, 2008 19:26:17 +0200: > I think you do not need the file-scope static levenshtein_cmd anymore with > this change, if you make similarity() take two command names. No? Yes :) > Please reroll the whole f66dd34 (git wrapper: DWIM mistyped commands, > 2008-08-28), as it is not part of any solid integration branch yet. I think I better reroll (now) both > You might also want to update the commit log message to talk about the > "len" reuse hack, but you already have in-code comment which might be > sufficient. I believe it is (and I added one against the member in the declaration) Mikael, this also might fix the crash you're seeing: the heap was corrupted by clean_cmdnames(&other_cmds) names members of which were moved to main_cmds. Makefile | 2 + builtin.h | 2 +- git.c | 4 ++- help.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- help.h | 2 +- levenshtein.c | 47 +++++++++++++++++++++++++++++++++++++ levenshtein.h | 8 ++++++ 7 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 levenshtein.c create mode 100644 levenshtein.h diff --git a/Makefile b/Makefile index bf400e6..3daa6dc 100644 --- a/Makefile +++ b/Makefile @@ -358,6 +358,7 @@ LIB_H += graph.h LIB_H += grep.h LIB_H += hash.h LIB_H += help.h +LIB_H += levenshtein.h LIB_H += list-objects.h LIB_H += ll-merge.h LIB_H += log-tree.h @@ -433,6 +434,7 @@ LIB_OBJS += hash.o LIB_OBJS += help.o LIB_OBJS += ident.o LIB_OBJS += interpolate.o +LIB_OBJS += levenshtein.o LIB_OBJS += list-objects.o LIB_OBJS += ll-merge.o LIB_OBJS += lockfile.o diff --git a/builtin.h b/builtin.h index f3502d3..e67cb20 100644 --- a/builtin.h +++ b/builtin.h @@ -11,7 +11,7 @@ extern const char git_usage_string[]; extern const char git_more_info_string[]; extern void list_common_cmds_help(void); -extern void help_unknown_cmd(const char *cmd); +extern const char *help_unknown_cmd(const char *cmd); extern void prune_packed_objects(int); extern int read_line_with_nul(char *buf, int size, FILE *file); extern int fmt_merge_msg(int merge_summary, struct strbuf *in, diff --git a/git.c b/git.c index 37b1d76..54c5bfa 100644 --- a/git.c +++ b/git.c @@ -499,7 +499,9 @@ int main(int argc, const char **argv) cmd, argv[0]); exit(1); } - help_unknown_cmd(cmd); + argv[0] = help_unknown_cmd(cmd); + handle_internal_command(argc, argv); + execv_dashed_external(argv); } fprintf(stderr, "Failed to run command '%s': %s\n", diff --git a/help.c b/help.c index b278257..aaba809 100644 --- a/help.c +++ b/help.c @@ -1,6 +1,7 @@ #include "cache.h" #include "builtin.h" #include "exec_cmd.h" +#include "levenshtein.h" #include "help.h" /* most GUI terminals set COLUMNS (although some don't export it) */ @@ -37,6 +38,16 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len) cmds->names[cmds->cnt++] = ent; } +static void clean_cmdnames(struct cmdnames *cmds) +{ + int i; + for (i = 0; i < cmds->cnt; ++i) + free(cmds->names[i]); + free(cmds->names); + cmds->cnt = 0; + cmds->alloc = 0; +} + static int cmdname_compare(const void *a_, const void *b_) { struct cmdname *a = *(struct cmdname **)a_; @@ -250,9 +261,68 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) return 0; } -void help_unknown_cmd(const char *cmd) +static int levenshtein_compare(const void *p1, const void *p2) +{ + const struct cmdname *const *c1 = p1, *const *c2 = p2; + const char *s1 = (*c1)->name, *s2 = (*c2)->name; + int l1 = (*c1)->len; + int l2 = (*c2)->len; + return l1 != l2 ? l1 - l2 : strcmp(s1, s2); +} + +const char *help_unknown_cmd(const char *cmd) { + int i, n, best_similarity = 0; + struct cmdnames main_cmds, other_cmds; + + memset(&main_cmds, 0, sizeof(main_cmds)); + memset(&other_cmds, 0, sizeof(main_cmds)); + + load_command_list("git-", &main_cmds, &other_cmds); + + ALLOC_GROW(main_cmds.names, main_cmds.cnt + other_cmds.cnt, + main_cmds.alloc); + memcpy(main_cmds.names + main_cmds.cnt, other_cmds.names, + other_cmds.cnt * sizeof(other_cmds.names[0])); + main_cmds.cnt += other_cmds.cnt; + free(other_cmds.names); + + /* This reuses cmdname->len for similarity index */ + for (i = 0; i < main_cmds.cnt; ++i) + main_cmds.names[i]->len = + levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4); + + qsort(main_cmds.names, main_cmds.cnt, + sizeof(*main_cmds.names), levenshtein_compare); + + if (!main_cmds.cnt) + die ("Uh oh. Your system reports no Git commands at all."); + + best_similarity = main_cmds.names[0]->len; + n = 1; + while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len) + ++n; + if (n == 1) { + const char *assumed = main_cmds.names[0]->name; + main_cmds.names[0] = NULL; + clean_cmdnames(&main_cmds); + fprintf(stderr, "WARNING: You called a Git program named '%s', " + "which does not exist.\n" + "Continuing under the assumption that you meant '%s'\n", + cmd, assumed); + return assumed; + } + fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd); + + if (best_similarity < 6) { + fprintf(stderr, "\nDid you mean %s?\n", + n < 2 ? "this": "one of these"); + + 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 2733433..56bc154 100644 --- a/help.h +++ b/help.h @@ -5,7 +5,7 @@ struct cmdnames { int alloc; int cnt; struct cmdname { - size_t len; + size_t len; /* also used for similarity index in help.c */ char name[FLEX_ARRAY]; } **names; }; diff --git a/levenshtein.c b/levenshtein.c new file mode 100644 index 0000000..db52f2c --- /dev/null +++ b/levenshtein.c @@ -0,0 +1,47 @@ +#include "cache.h" +#include "levenshtein.h" + +int levenshtein(const char *string1, const char *string2, + int w, int s, int a, int d) +{ + int len1 = strlen(string1), len2 = strlen(string2); + int *row0 = xmalloc(sizeof(int) * (len2 + 1)); + int *row1 = xmalloc(sizeof(int) * (len2 + 1)); + int *row2 = xmalloc(sizeof(int) * (len2 + 1)); + int i, j; + + for (j = 0; j <= len2; j++) + row1[j] = j * a; + for (i = 0; i < len1; i++) { + int *dummy; + + row2[0] = (i + 1) * d; + for (j = 0; j < len2; j++) { + /* substitution */ + row2[j + 1] = row1[j] + s * (string1[i] != string2[j]); + /* swap */ + if (i > 0 && j > 0 && string1[i - 1] == string2[j] && + string1[i] == string2[j - 1] && + row2[j + 1] > row0[j - 1] + w) + row2[j + 1] = row0[j - 1] + w; + /* deletion */ + if (j + 1 < len2 && row2[j + 1] > row1[j + 1] + d) + row2[j + 1] = row1[j + 1] + d; + /* insertion */ + if (row2[j + 1] > row2[j] + a) + row2[j + 1] = row2[j] + a; + } + + dummy = row0; + row0 = row1; + row1 = row2; + row2 = dummy; + } + + i = row1[len2]; + free(row0); + free(row1); + free(row2); + + return i; +} diff --git a/levenshtein.h b/levenshtein.h new file mode 100644 index 0000000..0173abe --- /dev/null +++ b/levenshtein.h @@ -0,0 +1,8 @@ +#ifndef LEVENSHTEIN_H +#define LEVENSHTEIN_H + +int levenshtein(const char *string1, const char *string2, + int swap_penalty, int substition_penalty, + int insertion_penalty, int deletion_penalty); + +#endif -- 1.6.0.1.168.gdf6f0 ^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-08-31 13:50 ` [PATCH] " Alex Riesen @ 2008-08-31 13:54 ` Alex Riesen 2008-08-31 14:49 ` Matthieu Moy 2008-09-01 14:42 ` [PATCH] git wrapper: DWIM mistyped commands Mikael Magnusson 1 sibling, 1 reply; 29+ messages in thread From: Alex Riesen @ 2008-08-31 13:54 UTC (permalink / raw) To: Junio C Hamano; +Cc: git, Johannes Schindelin It is off(0) by default, to avoid scaring people unless they asked to. If set to a non-0 value, wait for that amount of deciseconds before running the corrected command. Suggested by Junio, so he has a chance to hit Ctrl-C. Signed-off-by: Alex Riesen <raa.lkml@gmail.com> --- Alex Riesen, Sun, Aug 31, 2008 15:50:23 +0200: > Junio C Hamano, Sat, Aug 30, 2008 19:26:17 +0200: > > Please reroll the whole f66dd34 (git wrapper: DWIM mistyped commands, > > 2008-08-28), as it is not part of any solid integration branch yet. > > I think I better reroll (now) both > Documentation/config.txt | 9 +++++++++ help.c | 19 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index af57d94..8c644ab 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -790,6 +790,15 @@ help.format:: Values 'man', 'info', 'web' and 'html' are supported. 'man' is the default. 'web' and 'html' are the same. +help.autocorrect:: + Automatically correct and execute mistyped commands after + waiting for the given number of deciseconds (0.1 sec). If more + than one command can be deduced from the entered text, nothing + will be executed. If the value of this option is negative, + the corrected command will be executed immediately. If the + value is 0 - the command will be just shown but not executed. + This is the default. + http.proxy:: Override the HTTP proxy, normally configured using the 'http_proxy' environment variable (see linkgit:curl[1]). This can be overridden diff --git a/help.c b/help.c index aaba809..300cd38 100644 --- a/help.c +++ b/help.c @@ -261,6 +261,16 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) return 0; } +static int autocorrect; + +static int git_unknown_cmd_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "help.autocorrect")) + autocorrect = git_config_int(var,value); + + return git_default_config(var, value, cb); +} + static int levenshtein_compare(const void *p1, const void *p2) { const struct cmdname *const *c1 = p1, *const *c2 = p2; @@ -278,6 +288,8 @@ const char *help_unknown_cmd(const char *cmd) memset(&main_cmds, 0, sizeof(main_cmds)); memset(&other_cmds, 0, sizeof(main_cmds)); + git_config(git_unknown_cmd_config, NULL); + load_command_list("git-", &main_cmds, &other_cmds); ALLOC_GROW(main_cmds.names, main_cmds.cnt + other_cmds.cnt, @@ -302,7 +314,7 @@ const char *help_unknown_cmd(const char *cmd) n = 1; while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len) ++n; - if (n == 1) { + if (autocorrect && n == 1) { const char *assumed = main_cmds.names[0]->name; main_cmds.names[0] = NULL; clean_cmdnames(&main_cmds); @@ -310,6 +322,11 @@ const char *help_unknown_cmd(const char *cmd) "which does not exist.\n" "Continuing under the assumption that you meant '%s'\n", cmd, assumed); + if (autocorrect > 0) { + fprintf(stderr, "in %0.1f seconds automatically...\n", + (float)autocorrect/10.0); + poll(NULL, 0, autocorrect * 100); + } return assumed; } -- 1.6.0.1.168.gdf6f0 ^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-08-31 13:54 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen @ 2008-08-31 14:49 ` Matthieu Moy 2008-08-31 16:33 ` Junio C Hamano 0 siblings, 1 reply; 29+ messages in thread From: Matthieu Moy @ 2008-08-31 14:49 UTC (permalink / raw) To: Alex Riesen; +Cc: Junio C Hamano, git, Johannes Schindelin Alex Riesen <raa.lkml@gmail.com> writes: > It is off(0) by default, to avoid scaring people unless they asked to. > If set to a non-0 value, wait for that amount of deciseconds before > running the corrected command. Perhaps off should be -1 (or, say, anything negative), so that the value 0 can be used to mean "execute the corrected command without waiting". (not that I really care personnally, I'd probably keep it off anyway). -- Matthieu ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-08-31 14:49 ` Matthieu Moy @ 2008-08-31 16:33 ` Junio C Hamano 0 siblings, 0 replies; 29+ messages in thread From: Junio C Hamano @ 2008-08-31 16:33 UTC (permalink / raw) To: Matthieu Moy; +Cc: Alex Riesen, git, Johannes Schindelin Matthieu Moy <Matthieu.Moy@imag.fr> writes: > Alex Riesen <raa.lkml@gmail.com> writes: > >> It is off(0) by default, to avoid scaring people unless they asked to. >> If set to a non-0 value, wait for that amount of deciseconds before >> running the corrected command. > > Perhaps off should be -1 (or, say, anything negative), so that the > value 0 can be used to mean "execute the corrected command without > waiting". > > (not that I really care personnally, I'd probably keep it off anyway). I do not want to suggest this because I do not think of a good way to implement it myself, but it would be very nice if this feature can be enabled by default for interactive session and disabled for scripts. Well, actually I think I do ;-) Perhaps have a bash alias that does: alias git="git --dwim" only in interactive session (ensuring "only in interactive" is the responsibility of the end user), with the global option similar to the "--paginate" that "git" wrapper itself takes? ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH] git wrapper: DWIM mistyped commands 2008-08-31 13:50 ` [PATCH] " Alex Riesen 2008-08-31 13:54 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen @ 2008-09-01 14:42 ` Mikael Magnusson 1 sibling, 0 replies; 29+ messages in thread From: Mikael Magnusson @ 2008-09-01 14:42 UTC (permalink / raw) To: Alex Riesen; +Cc: Junio C Hamano, git, Johannes Schindelin 2008/8/31 Alex Riesen <raa.lkml@gmail.com>: > From: Johannes Schindelin <Johannes.Schindelin@gmx.de> > > This patch introduces a modified Damerau-Levenshtein algorithm into > Git's code base, and uses it with the following penalties to show some > similar commands when an unknown command was encountered: > > swap = 0, insertion = 1, substitution = 2, deletion = 4 > > A typical output would now look like this: > > $ git sm > git: 'sm' is not a git-command. See 'git --help'. > > Did you mean one of these? > am > rm > > The cut-off is at similarity rating 6, which was empirically determined > to give sensible results. > > As a convenience, if there is only one candidate, Git continues under > the assumption that the user mistyped it. Example: > > $ git reabse > WARNING: You called a Git program named 'reabse', which does > not exist. > Continuing under the assumption that you meant 'rebase' > [...] > Mikael, this also might fix the crash you're seeing: the heap was > corrupted by clean_cmdnames(&other_cmds) names members of which were > moved to main_cmds. It doesn't crash now, but the cut-off appears ineffective: % git aaaaaaaaaaaaaaa WARNING: You called a Git program named 'aaaaaaaaaaaaaaa', which does not exist. Continuing under the assumption that you meant 'add--interactive' in 2.0 seconds automatically... By my estimate, that should have a score above 6 :). -- Mikael Magnusson ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH updated] git wrapper: DWIM mistyped commands 2008-08-30 10:33 ` Mikael Magnusson 2008-08-31 13:50 ` [PATCH] " Alex Riesen @ 2008-08-31 13:57 ` Alex Riesen 1 sibling, 0 replies; 29+ messages in thread From: Alex Riesen @ 2008-08-31 13:57 UTC (permalink / raw) To: Mikael Magnusson; +Cc: git, Junio C Hamano, Johannes Schindelin Mikael Magnusson, Sat, Aug 30, 2008 12:33:26 +0200: > 2008/8/30 Alex Riesen <raa.lkml@gmail.com>: > > 2008/8/29 Mikael Magnusson <mikachu@gmail.com>: > >> I merged the branch in pu into next, which I think should work, but I get > >> these segfaults for some commands... I tried running in gdb but even with > > > > Can't reproduce in master, will try with next later, am busy right now, sorry. > > > >> -g3 I only get nonsense backtraces, not sure why. > > > > Remove -O2 from CFLAGS: > > > > $ make CFLAGS="-O0 -ggdb" > > Hm, I only had CFLAGS set in the env, maybe that's not enough, or maybe it The make variables set in Makefile take precedence over the variables with same names in environment (see make(1), "-e" option). > was that I had it set to -gdwarf-2 -g3 instead of -ggdb3. At any rate, I got > this now: > > % git fotch > Program received signal SIGSEGV, Segmentation fault. > [Switching to Thread 0xa7ce56c0 (LWP 13043)] > 0x41b61490 in ?? () from /lib/libc.so.6 > (gdb) bt > #0 0x41b61490 in ?? () from /lib/libc.so.6 > #1 0x41b634b0 in realloc () from /lib/libc.so.6 > #2 0x41b62fdb in malloc () from /lib/libc.so.6 > #3 0x080f63e6 in xmalloc (size=24) at wrapper.c:20 > #4 0x080f6471 in xmemdupz (data=0x816a44f, len=23) at wrapper.c:45 > #5 0x080f64e9 in xstrndup (str=0x816a44f "refs/remotes/origin/man", > len=23) at wrapper.c:54 > #6 0x080dc05c in parse_refspec_internal (nr_refspec=6, > refspec=0x816a010, fetch=1, verify=0) > at remote.c:505 Looks like heap corruption. See that patch I sent today. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH updated] git wrapper: DWIM mistyped commands 2008-08-28 21:27 ` [PATCH updated] git wrapper: DWIM mistyped commands Alex Riesen 2008-08-28 21:28 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen 2008-08-29 14:58 ` [PATCH updated] git wrapper: DWIM mistyped commands Mikael Magnusson @ 2008-08-30 15:36 ` Junio C Hamano 2008-08-30 16:44 ` Alex Riesen [not found] ` <a2075f4c0808301510g1af01b14kd58da12dc2e80f93@mail.gmail.com> 3 siblings, 1 reply; 29+ messages in thread From: Junio C Hamano @ 2008-08-30 15:36 UTC (permalink / raw) To: Alex Riesen; +Cc: git, Johannes Schindelin Alex Riesen <raa.lkml@gmail.com> writes: > @@ -257,9 +258,70 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) > ... > +static const char *levenshtein_cmd; > +static int similarity(const char *cmd) { > + return levenshtein(levenshtein_cmd, cmd, 0, 2, 1, 4); > +} > + > +static int levenshtein_compare(const void *p1, const void *p2) > { > + const struct cmdname *const *c1 = p1, *const *c2 = p2; > + const char *s1 = (*c1)->name, *s2 = (*c2)->name; > + int l1 = similarity(s1); > + int l2 = similarity(s2); > + return l1 != l2 ? l1 - l2 : strcmp(s1, s2); > +} > ... > + levenshtein_cmd = cmd; > + qsort(main_cmds.names, main_cmds.cnt, > + sizeof(*main_cmds.names), levenshtein_compare); Isn't this awfully inefficient? You have one mistyped command name to compute distance against, and want to sort the available 100+ command names by that distance. In qsort(), levenshtein_compare() will be called O(N log N) times (depending on your qsort implementation)? I wonder if it makes sense to give an otherwise unused "score" member to the "struct cmdname", compute the distance only once per each command, and use that as the sort key (alternatively you can have a separate int[N] array to store similarity values for each item in the cmdnames list, only used inside this codepath). ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH updated] git wrapper: DWIM mistyped commands 2008-08-30 15:36 ` Junio C Hamano @ 2008-08-30 16:44 ` Alex Riesen 2008-08-30 17:13 ` [PATCH] Reuse cmdname->len to store pre-calculated similarity indexes Alex Riesen 0 siblings, 1 reply; 29+ messages in thread From: Alex Riesen @ 2008-08-30 16:44 UTC (permalink / raw) To: Junio C Hamano; +Cc: git, Johannes Schindelin 2008/8/30 Junio C Hamano <gitster@pobox.com>: >> +static int levenshtein_compare(const void *p1, const void *p2) >> { >> + const struct cmdname *const *c1 = p1, *const *c2 = p2; >> + const char *s1 = (*c1)->name, *s2 = (*c2)->name; >> + int l1 = similarity(s1); >> + int l2 = similarity(s2); >> + return l1 != l2 ? l1 - l2 : strcmp(s1, s2); >> +} >> ... >> + levenshtein_cmd = cmd; >> + qsort(main_cmds.names, main_cmds.cnt, >> + sizeof(*main_cmds.names), levenshtein_compare); > > Isn't this awfully inefficient? > > You have one mistyped command name to compute distance against, and want > to sort the available 100+ command names by that distance. In qsort(), > levenshtein_compare() will be called O(N log N) times (depending on your > qsort implementation)? not only similarity, but strcmp as well. > I wonder if it makes sense to give an otherwise unused "score" member to Hmm, it is a _non-existing_ member of cmdname, isn't it? > the "struct cmdname", compute the distance only once per each command, and > use that as the sort key (alternatively you can have a separate int[N] > array to store similarity values for each item in the cmdnames list, only > used inside this codepath). I think I'll take the struct cmdname->len over. ^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH] Reuse cmdname->len to store pre-calculated similarity indexes 2008-08-30 16:44 ` Alex Riesen @ 2008-08-30 17:13 ` Alex Riesen 2008-08-30 17:26 ` Junio C Hamano 0 siblings, 1 reply; 29+ messages in thread From: Alex Riesen @ 2008-08-30 17:13 UTC (permalink / raw) To: Junio C Hamano; +Cc: git, Johannes Schindelin To avoid doing that while sorting Signed-off-by: Alex Riesen <raa.lkml@gmail.com> --- Alex Riesen, Sat, Aug 30, 2008 18:44:15 +0200: > 2008/8/30 Junio C Hamano <gitster@pobox.com>: > > I wonder if it makes sense to give an otherwise unused "score" member to > > Hmm, it is a _non-existing_ member of cmdname, isn't it? > > > the "struct cmdname", compute the distance only once per each command, and > > use that as the sort key (alternatively you can have a separate int[N] > > array to store similarity values for each item in the cmdnames list, only > > used inside this codepath). > > I think I'll take the struct cmdname->len over. help.c | 12 +++++++----- 1 files changed, 7 insertions(+), 5 deletions(-) diff --git a/help.c b/help.c index 7bfbbcd..70d57a3 100644 --- a/help.c +++ b/help.c @@ -287,8 +287,8 @@ static int levenshtein_compare(const void *p1, const void *p2) { const struct cmdname *const *c1 = p1, *const *c2 = p2; const char *s1 = (*c1)->name, *s2 = (*c2)->name; - int l1 = similarity(s1); - int l2 = similarity(s2); + int l1 = (*c1)->len; + int l2 = (*c2)->len; return l1 != l2 ? l1 - l2 : strcmp(s1, s2); } @@ -312,6 +312,9 @@ const char *help_unknown_cmd(const char *cmd) memcpy(main_cmds.names + main_cmds.cnt, other_cmds.names, other_cmds.cnt * sizeof(other_cmds.names[0])); main_cmds.cnt += other_cmds.cnt; + /* This reuses cmdname->len for similarity index */ + for (i = 0; i < main_cmds.cnt; ++i) + main_cmds.names[i]->len = similarity(main_cmds.names[i]->name); levenshtein_cmd = cmd; qsort(main_cmds.names, main_cmds.cnt, @@ -319,10 +322,9 @@ const char *help_unknown_cmd(const char *cmd) if (!main_cmds.cnt) die ("Uh oh. Your system reports no Git commands at all."); - best_similarity = similarity(main_cmds.names[0]->name); + best_similarity = main_cmds.names[0]->len; n = 1; - while (n < main_cmds.cnt && - best_similarity == similarity(main_cmds.names[n]->name)) + while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len) ++n; if (autocorrect && n == 1) { const char *assumed; -- 1.6.0.1.149.g9ecb0 ^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [PATCH] Reuse cmdname->len to store pre-calculated similarity indexes 2008-08-30 17:13 ` [PATCH] Reuse cmdname->len to store pre-calculated similarity indexes Alex Riesen @ 2008-08-30 17:26 ` Junio C Hamano 0 siblings, 0 replies; 29+ messages in thread From: Junio C Hamano @ 2008-08-30 17:26 UTC (permalink / raw) To: Alex Riesen; +Cc: git, Johannes Schindelin Alex Riesen <raa.lkml@gmail.com> writes: > To avoid doing that while sorting > > Signed-off-by: Alex Riesen <raa.lkml@gmail.com> > --- > > Alex Riesen, Sat, Aug 30, 2008 18:44:15 +0200: >> 2008/8/30 Junio C Hamano <gitster@pobox.com>: >> > I wonder if it makes sense to give an otherwise unused "score" member to >> >> Hmm, it is a _non-existing_ member of cmdname, isn't it? >> >> > the "struct cmdname", compute the distance only once per each command, and >> > use that as the sort key (alternatively you can have a separate int[N] >> > array to store similarity values for each item in the cmdnames list, only >> > used inside this codepath). >> >> I think I'll take the struct cmdname->len over. I think you do not need the file-scope static levenshtein_cmd anymore with this change, if you make similarity() take two command names. No? Please reroll the whole f66dd34 (git wrapper: DWIM mistyped commands, 2008-08-28), as it is not part of any solid integration branch yet. You might also want to update the commit log message to talk about the "len" reuse hack, but you already have in-code comment which might be sufficient. ^ permalink raw reply [flat|nested] 29+ messages in thread
[parent not found: <a2075f4c0808301510g1af01b14kd58da12dc2e80f93@mail.gmail.com>]
* Re: [PATCH updated] git wrapper: DWIM mistyped commands [not found] ` <a2075f4c0808301510g1af01b14kd58da12dc2e80f93@mail.gmail.com> @ 2008-08-30 22:17 ` Felipe Carvalho Oliveira 0 siblings, 0 replies; 29+ messages in thread From: Felipe Carvalho Oliveira @ 2008-08-30 22:17 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git > From: Johannes Schindelin <Johannes.Schindelin@gmx.de> > > This patch introduces a modified Damerau-Levenshtein algorithm into > Git's code base, and uses it with the following penalties to show some > similar commands when an unknown command was encountered: Thanks so much by implement this. Few weeks ago I had thought about this. Felipe(from Brazil) ^ permalink raw reply [flat|nested] 29+ messages in thread
* [FYI PATCH] git wrapper: DWIM mistyped commands @ 2008-07-22 20:01 Johannes Schindelin 2008-07-22 20:37 ` Alex Riesen 0 siblings, 1 reply; 29+ messages in thread From: Johannes Schindelin @ 2008-07-22 20:01 UTC (permalink / raw) To: git This patch introduces a modified Damerau-Levenshtein algorithm into Git's code base, and uses it with the following penalties to show some similar commands when an unknown command was encountered: swap = 0, insertion = 1, substitution = 2, deletion = 4 A typical output would now look like this: $ git sm git: 'sm' is not a git-command. See 'git --help'. Did you mean one of these? am rm The cut-off is at similarity rating 6, which was empirically determined to give sensible results. As a convenience, if there is only one candidate, Git continues under the assumption that the user mistyped it. Example: $ git reabse WARNING: You called a Git program named 'reabse', which does not exist. Continuing under the assumption that you meant 'rebase' [...] Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- So I mistyped 'reabse' for the hundred trillionth time, but I will never have to correct my mistakes again. Note: this patch is _not_ meant for inclusion. Makefile | 2 + builtin.h | 2 +- git.c | 4 ++- help.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- levenshtein.c | 47 +++++++++++++++++++++++++++++++++++++++++++ levenshtein.h | 8 +++++++ 6 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 levenshtein.c create mode 100644 levenshtein.h diff --git a/Makefile b/Makefile index 19bdd03..7e114e0 100644 --- a/Makefile +++ b/Makefile @@ -347,6 +347,7 @@ LIB_H += git-compat-util.h LIB_H += graph.h LIB_H += grep.h LIB_H += hash.h +LIB_H += levenshtein.h LIB_H += list-objects.h LIB_H += ll-merge.h LIB_H += log-tree.h @@ -421,6 +422,7 @@ LIB_OBJS += hash.o LIB_OBJS += help.o LIB_OBJS += ident.o LIB_OBJS += interpolate.o +LIB_OBJS += levenshtein.o LIB_OBJS += list-objects.o LIB_OBJS += ll-merge.o LIB_OBJS += lockfile.o diff --git a/builtin.h b/builtin.h index 0e605d4..fc5f108 100644 --- a/builtin.h +++ b/builtin.h @@ -11,7 +11,7 @@ extern const char git_usage_string[]; extern const char git_more_info_string[]; extern void list_common_cmds_help(void); -extern void help_unknown_cmd(const char *cmd); +extern const char *help_unknown_cmd(const char *cmd); extern void prune_packed_objects(int); extern int read_line_with_nul(char *buf, int size, FILE *file); extern int fmt_merge_msg(int merge_summary, struct strbuf *in, diff --git a/git.c b/git.c index 1bfd271..d7510ef 100644 --- a/git.c +++ b/git.c @@ -500,7 +500,9 @@ int main(int argc, const char **argv) cmd, argv[0]); exit(1); } - help_unknown_cmd(cmd); + argv[0] = help_unknown_cmd(cmd); + handle_internal_command(argc, argv); + execv_dashed_external(argv); } fprintf(stderr, "Failed to run command '%s': %s\n", diff --git a/help.c b/help.c index bfc84ae..480befe 100644 --- a/help.c +++ b/help.c @@ -9,6 +9,7 @@ #include "common-cmds.h" #include "parse-options.h" #include "run-command.h" +#include "levenshtein.h" static struct man_viewer_list { struct man_viewer_list *next; @@ -666,9 +667,67 @@ static void show_html_page(const char *git_cmd) open_html(page_path.buf); } -void help_unknown_cmd(const char *cmd) +static const char *levenshtein_cmd; +static int similarity(const char *cmd) { + return levenshtein(levenshtein_cmd, cmd, 0, 2, 1, 4); +} + +static int levenshtein_compare(const void *p1, const void *p2) +{ + const struct cmdname *const *c1 = p1, *const *c2 = p2; + const char *s1 = (*c1)->name, *s2 = (*c2)->name; + int l1 = similarity(s1); + int l2 = similarity(s2); + return l1 != l2 ? l1 - l2 : strcmp(s1, s2); +} + +const char *help_unknown_cmd(const char *cmd) { + int i, best_similarity = 0; + char cwd[PATH_MAX]; + + if (!getcwd(cwd, sizeof(cwd))) { + error("Could not get current working directory"); + cwd[0] = '\0'; + } + + load_command_list(); + ALLOC_GROW(main_cmds.names, main_cmds.cnt + other_cmds.cnt, + main_cmds.alloc); + memcpy(main_cmds.names + main_cmds.cnt, other_cmds.names, + other_cmds.cnt * sizeof(other_cmds.names[0])); + main_cmds.cnt += other_cmds.cnt; + + levenshtein_cmd = cmd; + qsort(main_cmds.names, main_cmds.cnt, + sizeof(*main_cmds.names), levenshtein_compare); + + if (!main_cmds.cnt) + die ("Uh oh. Your system reports no Git commands at all."); + best_similarity = similarity(main_cmds.names[0]->name); + if (main_cmds.cnt < 2 || best_similarity < + similarity(main_cmds.names[1]->name)) { + if (!*cwd) + exit(1); + if (chdir(cwd)) + die ("Could not change directory back to '%s'", cwd); + fprintf(stderr, "WARNING: You called a Git program named '%s', " + "which does not exist.\n" + "Continuing under the assumption that you meant '%s'\n", + cmd, main_cmds.names[0]->name); + return main_cmds.names[0]->name; + } + fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd); + + if (best_similarity < 6) { + fprintf(stderr, "\nDid you mean one of these?\n"); + + for (i = 0; i < main_cmds.cnt && best_similarity == + similarity(main_cmds.names[i]->name); i++) + fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); + } + exit(1); } diff --git a/levenshtein.c b/levenshtein.c new file mode 100644 index 0000000..db52f2c --- /dev/null +++ b/levenshtein.c @@ -0,0 +1,47 @@ +#include "cache.h" +#include "levenshtein.h" + +int levenshtein(const char *string1, const char *string2, + int w, int s, int a, int d) +{ + int len1 = strlen(string1), len2 = strlen(string2); + int *row0 = xmalloc(sizeof(int) * (len2 + 1)); + int *row1 = xmalloc(sizeof(int) * (len2 + 1)); + int *row2 = xmalloc(sizeof(int) * (len2 + 1)); + int i, j; + + for (j = 0; j <= len2; j++) + row1[j] = j * a; + for (i = 0; i < len1; i++) { + int *dummy; + + row2[0] = (i + 1) * d; + for (j = 0; j < len2; j++) { + /* substitution */ + row2[j + 1] = row1[j] + s * (string1[i] != string2[j]); + /* swap */ + if (i > 0 && j > 0 && string1[i - 1] == string2[j] && + string1[i] == string2[j - 1] && + row2[j + 1] > row0[j - 1] + w) + row2[j + 1] = row0[j - 1] + w; + /* deletion */ + if (j + 1 < len2 && row2[j + 1] > row1[j + 1] + d) + row2[j + 1] = row1[j + 1] + d; + /* insertion */ + if (row2[j + 1] > row2[j] + a) + row2[j + 1] = row2[j] + a; + } + + dummy = row0; + row0 = row1; + row1 = row2; + row2 = dummy; + } + + i = row1[len2]; + free(row0); + free(row1); + free(row2); + + return i; +} diff --git a/levenshtein.h b/levenshtein.h new file mode 100644 index 0000000..0173abe --- /dev/null +++ b/levenshtein.h @@ -0,0 +1,8 @@ +#ifndef LEVENSHTEIN_H +#define LEVENSHTEIN_H + +int levenshtein(const char *string1, const char *string2, + int swap_penalty, int substition_penalty, + int insertion_penalty, int deletion_penalty); + +#endif -- 1.6.0.rc0.21.g91175 ^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [FYI PATCH] git wrapper: DWIM mistyped commands 2008-07-22 20:01 [FYI PATCH] " Johannes Schindelin @ 2008-07-22 20:37 ` Alex Riesen 2008-07-22 21:03 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen 0 siblings, 1 reply; 29+ messages in thread From: Alex Riesen @ 2008-07-22 20:37 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git Johannes Schindelin, Tue, Jul 22, 2008 22:01:29 +0200: > As a convenience, if there is only one candidate, Git continues under > the assumption that the user mistyped it. Example: > > $ git reabse > WARNING: You called a Git program named 'reabse', which does > not exist. > Continuing under the assumption that you meant 'rebase' > [...] Oh, that would make me suspicios (and I hit Ctrl-C fast when I get suspicios about what happens to my precious data). Could it be configurable? For example, BASH's cdspell is configurable and even off by default. P.S. I'm still using your first patch and am forced to like it every day :) ^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-22 20:37 ` Alex Riesen @ 2008-07-22 21:03 ` Alex Riesen 2008-07-22 21:08 ` Johannes Schindelin 0 siblings, 1 reply; 29+ messages in thread From: Alex Riesen @ 2008-07-22 21:03 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git It is off by default, to avoid scaring people unless they asked to. --- Alex Riesen, Tue, Jul 22, 2008 22:37:30 +0200: > Johannes Schindelin, Tue, Jul 22, 2008 22:01:29 +0200: > > As a convenience, if there is only one candidate, Git continues under > > the assumption that the user mistyped it. Example: > > > > $ git reabse > > WARNING: You called a Git program named 'reabse', which does > > not exist. > > Continuing under the assumption that you meant 'rebase' > > [...] > > Oh, that would make me suspicios (and I hit Ctrl-C fast when I get > suspicios about what happens to my precious data). Could it be > configurable? For example, BASH's cdspell is configurable and even off > by default. > Like this, perhaps? help.c | 8 ++++++-- 1 files changed, 6 insertions(+), 2 deletions(-) diff --git a/help.c b/help.c index 480befe..f08eb9d 100644 --- a/help.c +++ b/help.c @@ -28,6 +28,7 @@ enum help_format { HELP_FORMAT_WEB, }; +static int autocorrect; static int show_all = 0; static enum help_format help_format = HELP_FORMAT_MAN; static struct option builtin_help_options[] = { @@ -269,6 +270,8 @@ static int git_help_config(const char *var, const char *value, void *cb) } if (!prefixcmp(var, "man.")) return add_man_viewer_info(var, value); + if (!strcmp(var, "help.autocorrect")) + autocorrect = git_config_bool(var,value); return git_default_config(var, value, cb); } @@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd) if (!main_cmds.cnt) die ("Uh oh. Your system reports no Git commands at all."); + git_config(git_help_config, NULL); best_similarity = similarity(main_cmds.names[0]->name); - if (main_cmds.cnt < 2 || best_similarity < - similarity(main_cmds.names[1]->name)) { + if (autocorrect && (main_cmds.cnt < 2 || + best_similarity < similarity(main_cmds.names[1]->name))) { if (!*cwd) exit(1); if (chdir(cwd)) -- 1.6.0.rc0.48.g6dda.dirty ^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-22 21:03 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen @ 2008-07-22 21:08 ` Johannes Schindelin 2008-07-22 21:26 ` Alex Riesen 0 siblings, 1 reply; 29+ messages in thread From: Johannes Schindelin @ 2008-07-22 21:08 UTC (permalink / raw) To: Alex Riesen; +Cc: git Hi, On Tue, 22 Jul 2008, Alex Riesen wrote: > + if (autocorrect && (main_cmds.cnt < 2 || > + best_similarity < similarity(main_cmds.names[1]->name))) { > if (!*cwd) > exit(1); > if (chdir(cwd)) In that case, you need to put in the "one of these" / "this" conditional again, which I ripped out because I do not need it any more. Ciao, Dscho ^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-22 21:08 ` Johannes Schindelin @ 2008-07-22 21:26 ` Alex Riesen 2008-07-22 21:44 ` Johannes Schindelin 0 siblings, 1 reply; 29+ messages in thread From: Alex Riesen @ 2008-07-22 21:26 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git It is off by default, to avoid scaring people unless they asked to. --- Johannes Schindelin, Tue, Jul 22, 2008 23:08:19 +0200: > On Tue, 22 Jul 2008, Alex Riesen wrote: > > > + if (autocorrect && (main_cmds.cnt < 2 || > > + best_similarity < similarity(main_cmds.names[1]->name))) { > > if (!*cwd) > > exit(1); > > if (chdir(cwd)) > > In that case, you need to put in the "one of these" / "this" conditional > again, which I ripped out because I do not need it any more. > Of course, missed it. Like this, then. help.c | 20 ++++++++++++++------ 1 files changed, 14 insertions(+), 6 deletions(-) diff --git a/help.c b/help.c index 480befe..cf6b459 100644 --- a/help.c +++ b/help.c @@ -28,6 +28,7 @@ enum help_format { HELP_FORMAT_WEB, }; +static int autocorrect; static int show_all = 0; static enum help_format help_format = HELP_FORMAT_MAN; static struct option builtin_help_options[] = { @@ -269,6 +270,8 @@ static int git_help_config(const char *var, const char *value, void *cb) } if (!prefixcmp(var, "man.")) return add_man_viewer_info(var, value); + if (!strcmp(var, "help.autocorrect")) + autocorrect = git_config_bool(var,value); return git_default_config(var, value, cb); } @@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd) if (!main_cmds.cnt) die ("Uh oh. Your system reports no Git commands at all."); + git_config(git_help_config, NULL); best_similarity = similarity(main_cmds.names[0]->name); - if (main_cmds.cnt < 2 || best_similarity < - similarity(main_cmds.names[1]->name)) { + if (autocorrect && (main_cmds.cnt < 2 || + best_similarity < similarity(main_cmds.names[1]->name))) { if (!*cwd) exit(1); if (chdir(cwd)) @@ -721,10 +725,14 @@ const char *help_unknown_cmd(const char *cmd) fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd); if (best_similarity < 6) { - fprintf(stderr, "\nDid you mean one of these?\n"); - - for (i = 0; i < main_cmds.cnt && best_similarity == - similarity(main_cmds.names[i]->name); i++) + int n = 0; + while (n < main_cmds.cnt && + best_similarity == similarity(main_cmds.names[n]->name)) + ++n; + fprintf(stderr, "\nDid you mean %s?\n", + n < 2 ? "this": "one of these"); + + for (i = 0; i < n; i++) fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); } -- 1.6.0.rc0.48.ga184 ^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-22 21:26 ` Alex Riesen @ 2008-07-22 21:44 ` Johannes Schindelin 2008-07-22 22:25 ` Alex Riesen 2008-07-22 23:08 ` Junio C Hamano 0 siblings, 2 replies; 29+ messages in thread From: Johannes Schindelin @ 2008-07-22 21:44 UTC (permalink / raw) To: Alex Riesen; +Cc: git Hi, On Tue, 22 Jul 2008, Alex Riesen wrote: > @@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd) > > if (!main_cmds.cnt) > die ("Uh oh. Your system reports no Git commands at all."); > + git_config(git_help_config, NULL); > best_similarity = similarity(main_cmds.names[0]->name); > - if (main_cmds.cnt < 2 || best_similarity < > - similarity(main_cmds.names[1]->name)) { > + if (autocorrect && (main_cmds.cnt < 2 || > + best_similarity < similarity(main_cmds.names[1]->name))) { > if (!*cwd) > exit(1); > if (chdir(cwd)) This "if" already checks if there is only one candidate. So you should just add an inner "if (autocorrect) ... else single = 1;" or some such. However, I think that the intention of this patch is too much DWIMery, which might be good for me (just like my "git add remote" patch), but not for the general audience. Ciao, Dscho ^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-22 21:44 ` Johannes Schindelin @ 2008-07-22 22:25 ` Alex Riesen 2008-07-23 16:44 ` Johannes Schindelin 2008-07-22 23:08 ` Junio C Hamano 1 sibling, 1 reply; 29+ messages in thread From: Alex Riesen @ 2008-07-22 22:25 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git It is off by default, to avoid scaring people unless they asked to. --- Johannes Schindelin, Tue, Jul 22, 2008 23:44:50 +0200: > On Tue, 22 Jul 2008, Alex Riesen wrote: > > > @@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd) > > > > if (!main_cmds.cnt) > > die ("Uh oh. Your system reports no Git commands at all."); > > + git_config(git_help_config, NULL); > > best_similarity = similarity(main_cmds.names[0]->name); > > - if (main_cmds.cnt < 2 || best_similarity < > > - similarity(main_cmds.names[1]->name)) { > > + if (autocorrect && (main_cmds.cnt < 2 || > > + best_similarity < similarity(main_cmds.names[1]->name))) { > > if (!*cwd) > > exit(1); > > if (chdir(cwd)) > > This "if" already checks if there is only one candidate. So you should > just add an inner "if (autocorrect) ... else single = 1;" or some such. Oh right, stupid me. > However, I think that the intention of this patch is too much DWIMery, > which might be good for me (just like my "git add remote" patch), but not > for the general audience. Mustn't be good for all (for the "general audience" it is even common practice to forget to thank. It may be even a sign of bad manners for it). It is good for me though. And thanks for sharing. Moved git_config before the calls where current directory is changed: so that it has the same filesystem context as in normal case. Less surprises. help.c | 19 +++++++++++++------ 1 files changed, 13 insertions(+), 6 deletions(-) diff --git a/help.c b/help.c index 480befe..8b25a55 100644 --- a/help.c +++ b/help.c @@ -28,6 +28,7 @@ enum help_format { HELP_FORMAT_WEB, }; +static int autocorrect; static int show_all = 0; static enum help_format help_format = HELP_FORMAT_MAN; static struct option builtin_help_options[] = { @@ -269,6 +270,8 @@ static int git_help_config(const char *var, const char *value, void *cb) } if (!prefixcmp(var, "man.")) return add_man_viewer_info(var, value); + if (!strcmp(var, "help.autocorrect")) + autocorrect = git_config_bool(var,value); return git_default_config(var, value, cb); } @@ -683,7 +686,7 @@ static int levenshtein_compare(const void *p1, const void *p2) const char *help_unknown_cmd(const char *cmd) { - int i, best_similarity = 0; + int i, best_similarity = 0, n; char cwd[PATH_MAX]; if (!getcwd(cwd, sizeof(cwd))) { @@ -691,6 +694,7 @@ const char *help_unknown_cmd(const char *cmd) cwd[0] = '\0'; } + git_config(git_help_config, NULL); load_command_list(); ALLOC_GROW(main_cmds.names, main_cmds.cnt + other_cmds.cnt, main_cmds.alloc); @@ -705,8 +709,11 @@ const char *help_unknown_cmd(const char *cmd) if (!main_cmds.cnt) die ("Uh oh. Your system reports no Git commands at all."); best_similarity = similarity(main_cmds.names[0]->name); - if (main_cmds.cnt < 2 || best_similarity < - similarity(main_cmds.names[1]->name)) { + n = 1; + while (n < main_cmds.cnt && + best_similarity == similarity(main_cmds.names[n]->name)) + ++n; + if (autocorrect && n == 1) { if (!*cwd) exit(1); if (chdir(cwd)) @@ -721,10 +728,10 @@ const char *help_unknown_cmd(const char *cmd) fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd); if (best_similarity < 6) { - fprintf(stderr, "\nDid you mean one of these?\n"); + fprintf(stderr, "\nDid you mean %s?\n", + n < 2 ? "this": "one of these"); - for (i = 0; i < main_cmds.cnt && best_similarity == - similarity(main_cmds.names[i]->name); i++) + for (i = 0; i < n; i++) fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); } -- 1.6.0.rc0.48.ga184 ^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-22 22:25 ` Alex Riesen @ 2008-07-23 16:44 ` Johannes Schindelin 2008-07-23 18:44 ` Alex Riesen 0 siblings, 1 reply; 29+ messages in thread From: Johannes Schindelin @ 2008-07-23 16:44 UTC (permalink / raw) To: Alex Riesen; +Cc: git Hi, On Wed, 23 Jul 2008, Alex Riesen wrote: > Johannes Schindelin, Tue, Jul 22, 2008 23:44:50 +0200: > > However, I think that the intention of this patch is too much DWIMery, > > which might be good for me (just like my "git add remote" patch), but > > not for the general audience. > > Mustn't be good for all You meant "needn't"? It is good for me ;-) > And thanks for sharing. You're welcome. > + n = 1; > + while (n < main_cmds.cnt && > + best_similarity == similarity(main_cmds.names[n]->name)) > + ++n; Mini-nit: you never ask for the value of n, only if it is 1 or larger. So you do not need to count... Ciao, Dscho ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-23 16:44 ` Johannes Schindelin @ 2008-07-23 18:44 ` Alex Riesen 2008-07-23 19:00 ` Johannes Schindelin 0 siblings, 1 reply; 29+ messages in thread From: Alex Riesen @ 2008-07-23 18:44 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git Johannes Schindelin, Wed, Jul 23, 2008 18:44:49 +0200: > > + n = 1; > > + while (n < main_cmds.cnt && > > + best_similarity == similarity(main_cmds.names[n]->name)) > > + ++n; > > Mini-nit: you never ask for the value of n, only if it is 1 or larger. So > you do not need to count... But I do, don't I? AFAICS, I use 0, 1 and >1 (this-these). ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-23 18:44 ` Alex Riesen @ 2008-07-23 19:00 ` Johannes Schindelin 2008-07-23 19:04 ` Johannes Schindelin 0 siblings, 1 reply; 29+ messages in thread From: Johannes Schindelin @ 2008-07-23 19:00 UTC (permalink / raw) To: Alex Riesen; +Cc: git Hi, On Wed, 23 Jul 2008, Alex Riesen wrote: > Johannes Schindelin, Wed, Jul 23, 2008 18:44:49 +0200: > > > + n = 1; > > > + while (n < main_cmds.cnt && > > > + best_similarity == similarity(main_cmds.names[n]->name)) > > > + ++n; > > > > Mini-nit: you never ask for the value of n, only if it is 1 or larger. So > > you do not need to count... > > But I do, don't I? AFAICS, I use 0, 1 and >1 (this-these). Yes. So check cnt > 0 && best_similarity > 5 says if it is 0, and cnt > 1 && best_similarity < similarity(...[1]...) says if it is 1. Ergo: no need to count, Dscho ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-23 19:00 ` Johannes Schindelin @ 2008-07-23 19:04 ` Johannes Schindelin 0 siblings, 0 replies; 29+ messages in thread From: Johannes Schindelin @ 2008-07-23 19:04 UTC (permalink / raw) To: Alex Riesen; +Cc: git Hi, On Wed, 23 Jul 2008, Johannes Schindelin wrote: > On Wed, 23 Jul 2008, Alex Riesen wrote: > > > Johannes Schindelin, Wed, Jul 23, 2008 18:44:49 +0200: > > > > + n = 1; > > > > + while (n < main_cmds.cnt && > > > > + best_similarity == similarity(main_cmds.names[n]->name)) > > > > + ++n; > > > > > > Mini-nit: you never ask for the value of n, only if it is 1 or larger. So > > > you do not need to count... > > > > But I do, don't I? AFAICS, I use 0, 1 and >1 (this-these). > > Yes. So check cnt > 0 && best_similarity > 5 says if it is 0, Oh, I just realized that my patch is bogus anyway. It only checks for best_similarity > 5 in the case that the first two commands have equal similarity. D'oh. Ciao, Dscho ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH] Add help.autocorrect to enable/disable autocorrecting 2008-07-22 21:44 ` Johannes Schindelin 2008-07-22 22:25 ` Alex Riesen @ 2008-07-22 23:08 ` Junio C Hamano 1 sibling, 0 replies; 29+ messages in thread From: Junio C Hamano @ 2008-07-22 23:08 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Alex Riesen, git Johannes Schindelin <Johannes.Schindelin@gmx.de> writes: > Hi, > > On Tue, 22 Jul 2008, Alex Riesen wrote: > >> @@ -704,9 +707,10 @@ const char *help_unknown_cmd(const char *cmd) >> >> if (!main_cmds.cnt) >> die ("Uh oh. Your system reports no Git commands at all."); >> + git_config(git_help_config, NULL); >> best_similarity = similarity(main_cmds.names[0]->name); >> - if (main_cmds.cnt < 2 || best_similarity < >> - similarity(main_cmds.names[1]->name)) { >> + if (autocorrect && (main_cmds.cnt < 2 || >> + best_similarity < similarity(main_cmds.names[1]->name))) { >> if (!*cwd) >> exit(1); >> if (chdir(cwd)) > > This "if" already checks if there is only one candidate. So you should > just add an inner "if (autocorrect) ... else single = 1;" or some such. > > However, I think that the intention of this patch is too much DWIMery, > which might be good for me (just like my "git add remote" patch), but not > for the general audience. Please make autocorrect not a binary but optionally the number of deciseconds before it continues, so that I have a chance to hit ^C ;-) ^ permalink raw reply [flat|nested] 29+ messages in thread
end of thread, other threads:[~2008-09-08 6:52 UTC | newest] Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-08-28 17:15 [PATCH] Remove calculation of the longest command name from where it is not used Alex Riesen, Alex Riesen 2008-08-28 21:27 ` [PATCH updated] git wrapper: DWIM mistyped commands Alex Riesen 2008-08-28 21:28 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen 2008-08-29 10:11 ` Andreas Ericsson 2008-09-08 6:50 ` Junio C Hamano 2008-08-29 14:58 ` [PATCH updated] git wrapper: DWIM mistyped commands Mikael Magnusson 2008-08-30 10:12 ` Alex Riesen 2008-08-30 10:33 ` Mikael Magnusson 2008-08-31 13:50 ` [PATCH] " Alex Riesen 2008-08-31 13:54 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen 2008-08-31 14:49 ` Matthieu Moy 2008-08-31 16:33 ` Junio C Hamano 2008-09-01 14:42 ` [PATCH] git wrapper: DWIM mistyped commands Mikael Magnusson 2008-08-31 13:57 ` [PATCH updated] " Alex Riesen 2008-08-30 15:36 ` Junio C Hamano 2008-08-30 16:44 ` Alex Riesen 2008-08-30 17:13 ` [PATCH] Reuse cmdname->len to store pre-calculated similarity indexes Alex Riesen 2008-08-30 17:26 ` Junio C Hamano [not found] ` <a2075f4c0808301510g1af01b14kd58da12dc2e80f93@mail.gmail.com> 2008-08-30 22:17 ` [PATCH updated] git wrapper: DWIM mistyped commands Felipe Carvalho Oliveira -- strict thread matches above, loose matches on Subject: below -- 2008-07-22 20:01 [FYI PATCH] " Johannes Schindelin 2008-07-22 20:37 ` Alex Riesen 2008-07-22 21:03 ` [PATCH] Add help.autocorrect to enable/disable autocorrecting Alex Riesen 2008-07-22 21:08 ` Johannes Schindelin 2008-07-22 21:26 ` Alex Riesen 2008-07-22 21:44 ` Johannes Schindelin 2008-07-22 22:25 ` Alex Riesen 2008-07-23 16:44 ` Johannes Schindelin 2008-07-23 18:44 ` Alex Riesen 2008-07-23 19:00 ` Johannes Schindelin 2008-07-23 19:04 ` Johannes Schindelin 2008-07-22 23:08 ` Junio C Hamano
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 NNTP newsgroup(s).