33#include "exec_cmd.h"
44#include "levenshtein.h"
55#include "help.h"
6+ #include "common-cmds.h"
67
78/* most GUI terminals set COLUMNS (although some don't export it) */
89static int term_columns (void )
@@ -298,7 +299,8 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
298299}
299300
300301/* An empirically derived magic number */
301- #define SIMILAR_ENOUGH (x ) ((x) < 6)
302+ #define SIMILARITY_FLOOR 7
303+ #define SIMILAR_ENOUGH (x ) ((x) < SIMILARITY_FLOOR)
302304
303305const char * help_unknown_cmd (const char * cmd )
304306{
@@ -319,21 +321,50 @@ const char *help_unknown_cmd(const char *cmd)
319321 sizeof (main_cmds .names ), cmdname_compare );
320322 uniq (& main_cmds );
321323
322- /* This reuses cmdname->len for similarity index */
323- for (i = 0 ; i < main_cmds .cnt ; ++ i )
324+ /* This abuses cmdname->len for levenshtein distance */
325+ for (i = 0 , n = 0 ; i < main_cmds .cnt ; i ++ ) {
326+ int cmp = 0 ; /* avoid compiler stupidity */
327+ const char * candidate = main_cmds .names [i ]-> name ;
328+
329+ /* Does the candidate appear in common_cmds list? */
330+ while (n < ARRAY_SIZE (common_cmds ) &&
331+ (cmp = strcmp (common_cmds [n ].name , candidate )) < 0 )
332+ n ++ ;
333+ if ((n < ARRAY_SIZE (common_cmds )) && !cmp ) {
334+ /* Yes, this is one of the common commands */
335+ n ++ ; /* use the entry from common_cmds[] */
336+ if (!prefixcmp (candidate , cmd )) {
337+ /* Give prefix match a very good score */
338+ main_cmds .names [i ]-> len = 0 ;
339+ continue ;
340+ }
341+ }
342+
324343 main_cmds .names [i ]-> len =
325- levenshtein (cmd , main_cmds .names [i ]-> name , 0 , 2 , 1 , 4 );
344+ levenshtein (cmd , candidate , 0 , 2 , 1 , 4 ) + 1 ;
345+ }
326346
327347 qsort (main_cmds .names , main_cmds .cnt ,
328348 sizeof (* main_cmds .names ), levenshtein_compare );
329349
330350 if (!main_cmds .cnt )
331351 die ("Uh oh. Your system reports no Git commands at all." );
332352
333- best_similarity = main_cmds .names [0 ]-> len ;
334- n = 1 ;
335- while (n < main_cmds .cnt && best_similarity == main_cmds .names [n ]-> len )
336- ++ n ;
353+ /* skip and count prefix matches */
354+ for (n = 0 ; n < main_cmds .cnt && !main_cmds .names [n ]-> len ; n ++ )
355+ ; /* still counting */
356+
357+ if (main_cmds .cnt <= n ) {
358+ /* prefix matches with everything? that is too ambiguous */
359+ best_similarity = SIMILARITY_FLOOR + 1 ;
360+ } else {
361+ /* count all the most similar ones */
362+ for (best_similarity = main_cmds .names [n ++ ]-> len ;
363+ (n < main_cmds .cnt &&
364+ best_similarity == main_cmds .names [n ]-> len );
365+ n ++ )
366+ ; /* still counting */
367+ }
337368 if (autocorrect && n == 1 && SIMILAR_ENOUGH (best_similarity )) {
338369 const char * assumed = main_cmds .names [0 ]-> name ;
339370 main_cmds .names [0 ] = NULL ;
0 commit comments