1
#include "cache.h"
2
#include "builtin.h"
3
#include "exec_cmd.h"
4
+#include "levenshtein.h"
5
#include "help.h"
6
7
/* most GUI terminals set COLUMNS (although some don't export it) */
38
cmds->names[cmds->cnt++] = ent;
39
}
40
41
+static void clean_cmdnames(struct cmdnames *cmds)
42
+{
43
+ int i;
44
+ for (i = 0; i < cmds->cnt; ++i)
45
+ free(cmds->names[i]);
46
+ free(cmds->names);
47
+ cmds->cnt = 0;
48
+ cmds->alloc = 0;
49
+}
50
+
51
static int cmdname_compare(const void *a_, const void *b_)
52
{
53
struct cmdname *a = *(struct cmdname **)a_;
268
return 0;
269
}
270
260
-void help_unknown_cmd(const char *cmd)
271
+static int levenshtein_compare(const void *p1, const void *p2)
272
+{
273
+ const struct cmdname *const *c1 = p1, *const *c2 = p2;
274
+ const char *s1 = (*c1)->name, *s2 = (*c2)->name;
275
+ int l1 = (*c1)->len;
276
+ int l2 = (*c2)->len;
277
+ return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
278
+}
279
+
280
+const char *help_unknown_cmd(const char *cmd)
281
{
282
+ int i, n, best_similarity = 0;
283
+ struct cmdnames main_cmds, other_cmds;
284
+
285
+ memset(&main_cmds, 0, sizeof(main_cmds));
286
+ memset(&other_cmds, 0, sizeof(main_cmds));
287
+
288
+ load_command_list("git-", &main_cmds, &other_cmds);
289
+
290
+ ALLOC_GROW(main_cmds.names, main_cmds.cnt + other_cmds.cnt,
291
+ main_cmds.alloc);
292
+ memcpy(main_cmds.names + main_cmds.cnt, other_cmds.names,
293
+ other_cmds.cnt * sizeof(other_cmds.names[0]));
294
+ main_cmds.cnt += other_cmds.cnt;
295
+ free(other_cmds.names);
296
+
297
+ /* This reuses cmdname->len for similarity index */
298
+ for (i = 0; i < main_cmds.cnt; ++i)
299
+ main_cmds.names[i]->len =
300
+ levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
301
+
302
+ qsort(main_cmds.names, main_cmds.cnt,
303
+ sizeof(*main_cmds.names), levenshtein_compare);
304
+
305
+ if (!main_cmds.cnt)
306
+ die ("Uh oh. Your system reports no Git commands at all.");
307
+
308
+ best_similarity = main_cmds.names[0]->len;
309
+ n = 1;
310
+ while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
311
+ ++n;
312
+ if (n == 1) {
313
+ const char *assumed = main_cmds.names[0]->name;
314
+ main_cmds.names[0] = NULL;
315
+ clean_cmdnames(&main_cmds);
316
+ fprintf(stderr, "WARNING: You called a Git program named '%s', "
317
+ "which does not exist.\n"
318
+ "Continuing under the assumption that you meant '%s'\n",
319
+ cmd, assumed);
320
+ return assumed;
321
+ }
322
+
323
fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd);
324
+
325
+ if (best_similarity < 6) {
326
+ fprintf(stderr, "\nDid you mean %s?\n",
327
+ n < 2 ? "this": "one of these");
328
+
329
+ for (i = 0; i < n; i++)
330
+ fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
331
+ }
332
+
333
exit(1);
334
}
335