help.c 19.6 KB
Newer Older
1
#include "cache.h"
2
#include "config.h"
3
#include "builtin.h"
4
#include "exec-cmd.h"
5
#include "run-command.h"
6
#include "levenshtein.h"
7
#include "help.h"
8
#include "command-list.h"
9 10
#include "string-list.h"
#include "column.h"
11
#include "version.h"
12
#include "refs.h"
13
#include "parse-options.h"
14

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
struct category_description {
	uint32_t category;
	const char *desc;
};
static uint32_t common_mask =
	CAT_init | CAT_worktree | CAT_info |
	CAT_history | CAT_remote;
static struct category_description common_categories[] = {
	{ CAT_init, N_("start a working area (see also: git help tutorial)") },
	{ CAT_worktree, N_("work on the current change (see also: git help everyday)") },
	{ CAT_info, N_("examine the history and state (see also: git help revisions)") },
	{ CAT_history, N_("grow, mark and tweak your common history") },
	{ CAT_remote, N_("collaborate (see also: git help workflows)") },
	{ 0, NULL }
};
30 31 32 33 34 35 36 37 38 39 40
static struct category_description main_categories[] = {
	{ CAT_mainporcelain, N_("Main Porcelain Commands") },
	{ CAT_ancillarymanipulators, N_("Ancillary Commands / Manipulators") },
	{ CAT_ancillaryinterrogators, N_("Ancillary Commands / Interrogators") },
	{ CAT_foreignscminterface, N_("Interacting with Others") },
	{ CAT_plumbingmanipulators, N_("Low-level Commands / Manipulators") },
	{ CAT_plumbinginterrogators, N_("Low-level Commands / Interrogators") },
	{ CAT_synchingrepositories, N_("Low-level Commands / Synching Repositories") },
	{ CAT_purehelpers, N_("Low-level Commands / Internal Helpers") },
	{ 0, NULL }
};
41

42
static const char *drop_prefix(const char *name, uint32_t category)
43 44 45 46 47
{
	const char *new_name;

	if (skip_prefix(name, "git-", &new_name))
		return new_name;
48 49
	if (category == CAT_guide && skip_prefix(name, "git", &new_name))
		return new_name;
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
	return name;

}

static void extract_cmds(struct cmdname_help **p_cmds, uint32_t mask)
{
	int i, nr = 0;
	struct cmdname_help *cmds;

	if (ARRAY_SIZE(command_list) == 0)
		BUG("empty command_list[] is a sign of broken generate-cmdlist.sh");

	ALLOC_ARRAY(cmds, ARRAY_SIZE(command_list) + 1);

	for (i = 0; i < ARRAY_SIZE(command_list); i++) {
		const struct cmdname_help *cmd = command_list + i;

		if (!(cmd->category & mask))
			continue;

		cmds[nr] = *cmd;
71
		cmds[nr].name = drop_prefix(cmd->name, cmd->category);
72 73 74 75 76 77 78 79 80 81 82 83 84 85

		nr++;
	}
	cmds[nr].name = NULL;
	*p_cmds = cmds;
}

static void print_command_list(const struct cmdname_help *cmds,
			       uint32_t mask, int longest)
{
	int i;

	for (i = 0; cmds[i].name; i++) {
		if (cmds[i].category & mask) {
86
			size_t len = strlen(cmds[i].name);
87
			printf("   %s   ", cmds[i].name);
88 89
			if (longest > len)
				mput_char(' ', longest - len);
90 91 92 93 94 95 96 97 98 99 100 101 102
			puts(_(cmds[i].help));
		}
	}
}

static int cmd_name_cmp(const void *elem1, const void *elem2)
{
	const struct cmdname_help *e1 = elem1;
	const struct cmdname_help *e2 = elem2;

	return strcmp(e1->name, e2->name);
}

103 104
static void print_cmd_by_category(const struct category_description *catdesc,
				  int *longest_p)
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
{
	struct cmdname_help *cmds;
	int longest = 0;
	int i, nr = 0;
	uint32_t mask = 0;

	for (i = 0; catdesc[i].desc; i++)
		mask |= catdesc[i].category;

	extract_cmds(&cmds, mask);

	for (i = 0; cmds[i].name; i++, nr++) {
		if (longest < strlen(cmds[i].name))
			longest = strlen(cmds[i].name);
	}
	QSORT(cmds, nr, cmd_name_cmp);

	for (i = 0; catdesc[i].desc; i++) {
		uint32_t mask = catdesc[i].category;
		const char *desc = catdesc[i].desc;

		printf("\n%s\n", _(desc));
		print_command_list(cmds, mask, longest);
	}
	free(cmds);
130 131
	if (longest_p)
		*longest_p = longest;
132 133
}

134
void add_cmdname(struct cmdnames *cmds, const char *name, int len)
135
{
136 137
	struct cmdname *ent;
	FLEX_ALLOC_MEM(ent, name, name, len);
138
	ent->len = len;
139 140 141

	ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
	cmds->names[cmds->cnt++] = ent;
142 143
}

144 145 146 147 148 149 150 151 152 153
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;
}

154 155 156 157 158 159 160
static int cmdname_compare(const void *a_, const void *b_)
{
	struct cmdname *a = *(struct cmdname **)a_;
	struct cmdname *b = *(struct cmdname **)b_;
	return strcmp(a->name, b->name);
}

161 162 163 164 165 166 167
static void uniq(struct cmdnames *cmds)
{
	int i, j;

	if (!cmds->cnt)
		return;

Jeff King's avatar
Jeff King committed
168 169 170 171
	for (i = j = 1; i < cmds->cnt; i++) {
		if (!strcmp(cmds->names[i]->name, cmds->names[j-1]->name))
			free(cmds->names[i]);
		else
172
			cmds->names[j++] = cmds->names[i];
Jeff King's avatar
Jeff King committed
173
	}
174 175 176 177

	cmds->cnt = j;
}

178
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
179
{
180 181 182 183 184 185 186 187
	int ci, cj, ei;
	int cmp;

	ci = cj = ei = 0;
	while (ci < cmds->cnt && ei < excludes->cnt) {
		cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
		if (cmp < 0)
			cmds->names[cj++] = cmds->names[ci++];
188 189 190 191
		else if (cmp == 0) {
			ei++;
			free(cmds->names[ci++]);
		} else if (cmp > 0)
192 193 194 195 196 197 198 199 200
			ei++;
	}

	while (ci < cmds->cnt)
		cmds->names[cj++] = cmds->names[ci++];

	cmds->cnt = cj;
}

201
static void pretty_print_cmdnames(struct cmdnames *cmds, unsigned int colopts)
202
{
203 204 205
	struct string_list list = STRING_LIST_INIT_NODUP;
	struct column_options copts;
	int i;
206

207 208 209 210 211 212 213 214 215 216 217 218
	for (i = 0; i < cmds->cnt; i++)
		string_list_append(&list, cmds->names[i]->name);
	/*
	 * always enable column display, we only consult column.*
	 * about layout strategy and stuff
	 */
	colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
	memset(&copts, 0, sizeof(copts));
	copts.indent = "  ";
	copts.padding = 2;
	print_columns(&list, colopts, &copts);
	string_list_clear(&list, 0);
219 220
}

221
static void list_commands_in_dir(struct cmdnames *cmds,
222 223
					 const char *path,
					 const char *prefix)
224
{
225
	DIR *dir = opendir(path);
226
	struct dirent *de;
227 228
	struct strbuf buf = STRBUF_INIT;
	int len;
229

230
	if (!dir)
231
		return;
232 233
	if (!prefix)
		prefix = "git-";
234

235 236 237
	strbuf_addf(&buf, "%s/", path);
	len = buf.len;

238
	while ((de = readdir(dir)) != NULL) {
239
		const char *ent;
240
		size_t entlen;
241

242
		if (!skip_prefix(de->d_name, prefix, &ent))
243
			continue;
244

245 246 247
		strbuf_setlen(&buf, len);
		strbuf_addstr(&buf, de->d_name);
		if (!is_executable(buf.buf))
248 249
			continue;

250
		entlen = strlen(ent);
251
		strip_suffix(ent, ".exe", &entlen);
252

253
		add_cmdname(cmds, ent, entlen);
254 255
	}
	closedir(dir);
256
	strbuf_release(&buf);
257 258
}

259
void load_command_list(const char *prefix,
260 261
		struct cmdnames *main_cmds,
		struct cmdnames *other_cmds)
262 263 264 265
{
	const char *env_path = getenv("PATH");
	const char *exec_path = git_exec_path();

266
	if (exec_path) {
267
		list_commands_in_dir(main_cmds, exec_path, prefix);
René Scharfe's avatar
René Scharfe committed
268
		QSORT(main_cmds->names, main_cmds->cnt, cmdname_compare);
269
		uniq(main_cmds);
270 271
	}

272 273 274 275 276 277
	if (env_path) {
		char *paths, *path, *colon;
		path = paths = xstrdup(env_path);
		while (1) {
			if ((colon = strchr(path, PATH_SEP)))
				*colon = 0;
278 279
			if (!exec_path || strcmp(path, exec_path))
				list_commands_in_dir(other_cmds, path, prefix);
280

281 282 283 284 285
			if (!colon)
				break;
			path = colon + 1;
		}
		free(paths);
286

René Scharfe's avatar
René Scharfe committed
287
		QSORT(other_cmds->names, other_cmds->cnt, cmdname_compare);
288 289
		uniq(other_cmds);
	}
290
	exclude_cmds(other_cmds, main_cmds);
Jeff King's avatar
Jeff King committed
291 292
}

293
void list_commands(unsigned int colopts,
294
		   struct cmdnames *main_cmds, struct cmdnames *other_cmds)
Jeff King's avatar
Jeff King committed
295
{
296
	if (main_cmds->cnt) {
297
		const char *exec_path = git_exec_path();
298
		printf_ln(_("available git commands in '%s'"), exec_path);
299
		putchar('\n');
300
		pretty_print_cmdnames(main_cmds, colopts);
301 302 303
		putchar('\n');
	}

304
	if (other_cmds->cnt) {
305
		printf_ln(_("git commands available from elsewhere on your $PATH"));
306
		putchar('\n');
307
		pretty_print_cmdnames(other_cmds, colopts);
308 309
		putchar('\n');
	}
310 311
}

312
void list_common_cmds_help(void)
313 314
{
	puts(_("These are common Git commands used in various situations:"));
315
	print_cmd_by_category(common_categories, NULL);
316
}
317

318 319 320 321 322 323 324 325 326 327 328 329 330 331
void list_all_main_cmds(struct string_list *list)
{
	struct cmdnames main_cmds, other_cmds;
	int i;

	memset(&main_cmds, 0, sizeof(main_cmds));
	memset(&other_cmds, 0, sizeof(other_cmds));
	load_command_list("git-", &main_cmds, &other_cmds);

	for (i = 0; i < main_cmds.cnt; i++)
		string_list_append(list, main_cmds.names[i]->name);

	clean_cmdnames(&main_cmds);
	clean_cmdnames(&other_cmds);
332 333
}

334
void list_all_other_cmds(struct string_list *list)
335
{
336 337
	struct cmdnames main_cmds, other_cmds;
	int i;
338

339 340 341
	memset(&main_cmds, 0, sizeof(main_cmds));
	memset(&other_cmds, 0, sizeof(other_cmds));
	load_command_list("git-", &main_cmds, &other_cmds);
342

343 344
	for (i = 0; i < other_cmds.cnt; i++)
		string_list_append(list, other_cmds.names[i]->name);
345

346 347 348 349
	clean_cmdnames(&main_cmds);
	clean_cmdnames(&other_cmds);
}

350 351 352 353 354
void list_cmds_by_category(struct string_list *list,
			   const char *cat)
{
	int i, n = ARRAY_SIZE(command_list);
	uint32_t cat_id = 0;
355

356 357 358 359
	for (i = 0; category_names[i]; i++) {
		if (!strcmp(cat, category_names[i])) {
			cat_id = 1UL << i;
			break;
360
		}
361 362 363 364 365 366 367
	}
	if (!cat_id)
		die(_("unsupported command listing type '%s'"), cat);

	for (i = 0; i < n; i++) {
		struct cmdname_help *cmd = command_list + i;

368 369 370
		if (!(cmd->category & cat_id))
			continue;
		string_list_append(list, drop_prefix(cmd->name, cmd->category));
371 372 373
	}
}

374 375 376 377 378 379 380 381 382 383 384 385 386
void list_cmds_by_config(struct string_list *list)
{
	const char *cmd_list;

	if (git_config_get_string_const("completion.commands", &cmd_list))
		return;

	string_list_sort(list);
	string_list_remove_duplicates(list, 0);

	while (*cmd_list) {
		struct strbuf sb = STRBUF_INIT;
		const char *p = strchrnul(cmd_list, ' ');
387

388
		strbuf_add(&sb, cmd_list, p - cmd_list);
389 390
		if (sb.buf[0] == '-')
			string_list_remove(list, sb.buf + 1, 0);
391 392 393 394 395 396
		else
			string_list_insert(list, sb.buf);
		strbuf_release(&sb);
		while (*p == ' ')
			p++;
		cmd_list = p;
397 398 399
	}
}

400 401 402 403 404 405
void list_common_guides_help(void)
{
	struct category_description catdesc[] = {
		{ CAT_guide, N_("The common Git guides are:") },
		{ 0, NULL }
	};
406
	print_cmd_by_category(catdesc, NULL);
407 408 409
	putchar('\n');
}

410 411 412 413 414 415 416
struct slot_expansion {
	const char *prefix;
	const char *placeholder;
	void (*fn)(struct string_list *list, const char *prefix);
	int found;
};

417
void list_config_help(int for_human)
418 419 420 421 422 423 424 425
{
	struct slot_expansion slot_expansions[] = {
		{ "advice", "*", list_config_advices },
		{ "color.branch", "<slot>", list_config_color_branch_slots },
		{ "color.decorate", "<slot>", list_config_color_decorate_slots },
		{ "color.diff", "<slot>", list_config_color_diff_slots },
		{ "color.grep", "<slot>", list_config_color_grep_slots },
		{ "color.interactive", "<slot>", list_config_color_interactive_slots },
426
		{ "color.remote", "<slot>", list_config_color_sideband_slots },
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
		{ "color.status", "<slot>", list_config_color_status_slots },
		{ "fsck", "<msg-id>", list_config_fsck_msg_ids },
		{ "receive.fsck", "<msg-id>", list_config_fsck_msg_ids },
		{ NULL, NULL, NULL }
	};
	const char **p;
	struct slot_expansion *e;
	struct string_list keys = STRING_LIST_INIT_DUP;
	int i;

	for (p = config_name_list; *p; p++) {
		const char *var = *p;
		struct strbuf sb = STRBUF_INIT;

		for (e = slot_expansions; e->prefix; e++) {

			strbuf_reset(&sb);
			strbuf_addf(&sb, "%s.%s", e->prefix, e->placeholder);
			if (!strcasecmp(var, sb.buf)) {
				e->fn(&keys, e->prefix);
				e->found++;
				break;
			}
		}
		strbuf_release(&sb);
		if (!e->prefix)
			string_list_append(&keys, var);
	}

	for (e = slot_expansions; e->prefix; e++)
		if (!e->found)
			BUG("slot_expansion %s.%s is not used",
			    e->prefix, e->placeholder);

	string_list_sort(&keys);
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
	for (i = 0; i < keys.nr; i++) {
		const char *var = keys.items[i].string;
		const char *wildcard, *tag, *cut;

		if (for_human) {
			puts(var);
			continue;
		}

		wildcard = strchr(var, '*');
		tag = strchr(var, '<');

		if (!wildcard && !tag) {
			puts(var);
			continue;
		}

		if (wildcard && !tag)
			cut = wildcard;
		else if (!wildcard && tag)
			cut = tag;
		else
			cut = wildcard < tag ? wildcard : tag;

		/*
		 * We may produce duplicates, but that's up to
		 * git-completion.bash to handle
		 */
		printf("%.*s\n", (int)(cut - var), var);
	}
492 493 494
	string_list_clear(&keys, 0);
}

495 496 497 498 499 500 501 502 503 504
static int get_alias(const char *var, const char *value, void *data)
{
	struct string_list *list = data;

	if (skip_prefix(var, "alias.", &var))
		string_list_append(list, var)->util = xstrdup(value);

	return 0;
}

505 506
void list_all_cmds_help(void)
{
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
	struct string_list others = STRING_LIST_INIT_DUP;
	struct string_list alias_list = STRING_LIST_INIT_DUP;
	struct cmdname_help *aliases;
	int i, longest;

	printf_ln(_("See 'git help <command>' to read about a specific subcommand"));
	print_cmd_by_category(main_categories, &longest);

	list_all_other_cmds(&others);
	if (others.nr)
		printf("\n%s\n", _("External commands"));
	for (i = 0; i < others.nr; i++)
		printf("   %s\n", others.items[i].string);
	string_list_clear(&others, 0);

	git_config(get_alias, &alias_list);
	string_list_sort(&alias_list);
524 525 526 527 528 529 530

	for (i = 0; i < alias_list.nr; i++) {
		size_t len = strlen(alias_list.items[i].string);
		if (longest < len)
			longest = len;
	}

531 532 533 534 535 536 537 538 539 540 541 542 543
	if (alias_list.nr) {
		printf("\n%s\n", _("Command aliases"));
		ALLOC_ARRAY(aliases, alias_list.nr + 1);
		for (i = 0; i < alias_list.nr; i++) {
			aliases[i].name = alias_list.items[i].string;
			aliases[i].help = alias_list.items[i].util;
			aliases[i].category = 1;
		}
		aliases[alias_list.nr].name = NULL;
		print_command_list(aliases, 1, longest);
		free(aliases);
	}
	string_list_clear(&alias_list, 1);
544 545
}

546
int is_in_cmdlist(struct cmdnames *c, const char *s)
Jeff King's avatar
Jeff King committed
547 548 549 550 551 552 553 554
{
	int i;
	for (i = 0; i < c->cnt; i++)
		if (!strcmp(s, c->names[i]->name))
			return 1;
	return 0;
}

555
static int autocorrect;
556
static struct cmdnames aliases;
557 558 559

static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
{
560 561
	const char *p;

562 563
	if (!strcmp(var, "help.autocorrect"))
		autocorrect = git_config_int(var,value);
564
	/* Also use aliases for command lookup */
565 566
	if (skip_prefix(var, "alias.", &p))
		add_cmdname(&aliases, p, strlen(p));
567 568 569 570

	return git_default_config(var, value, cb);
}

571
static int levenshtein_compare(const void *p1, const void *p2)
572
{
573 574 575 576 577 578 579
	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);
}

580 581 582 583 584 585 586
static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
{
	int i;
	ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);

	for (i = 0; i < old->cnt; i++)
		cmds->names[cmds->cnt++] = old->names[i];
587
	FREE_AND_NULL(old->names);
588 589 590
	old->cnt = 0;
}

591
/* An empirically derived magic number */
592 593
#define SIMILARITY_FLOOR 7
#define SIMILAR_ENOUGH(x) ((x) < SIMILARITY_FLOOR)
594

595 596 597 598
static const char bad_interpreter_advice[] =
	N_("'%s' appears to be a git command, but we were not\n"
	"able to execute it. Maybe git-%s is broken?");

599
const char *help_unknown_cmd(const char *cmd)
600
{
601 602
	int i, n, best_similarity = 0;
	struct cmdnames main_cmds, other_cmds;
603
	struct cmdname_help *common_cmds;
604 605

	memset(&main_cmds, 0, sizeof(main_cmds));
606
	memset(&other_cmds, 0, sizeof(other_cmds));
607
	memset(&aliases, 0, sizeof(aliases));
608

609
	read_early_config(git_unknown_cmd_config, NULL);
610

611 612
	load_command_list("git-", &main_cmds, &other_cmds);

613 614
	add_cmd_list(&main_cmds, &aliases);
	add_cmd_list(&main_cmds, &other_cmds);
René Scharfe's avatar
René Scharfe committed
615
	QSORT(main_cmds.names, main_cmds.cnt, cmdname_compare);
616
	uniq(&main_cmds);
617

618 619
	extract_cmds(&common_cmds, common_mask);

620 621 622 623 624
	/* This abuses cmdname->len for levenshtein distance */
	for (i = 0, n = 0; i < main_cmds.cnt; i++) {
		int cmp = 0; /* avoid compiler stupidity */
		const char *candidate = main_cmds.names[i]->name;

625 626 627 628 629 630 631 632
		/*
		 * An exact match means we have the command, but
		 * for some reason exec'ing it gave us ENOENT; probably
		 * it's a bad interpreter in the #! line.
		 */
		if (!strcmp(candidate, cmd))
			die(_(bad_interpreter_advice), cmd, cmd);

633
		/* Does the candidate appear in common_cmds list? */
634
		while (common_cmds[n].name &&
635 636
		       (cmp = strcmp(common_cmds[n].name, candidate)) < 0)
			n++;
637
		if (common_cmds[n].name && !cmp) {
638 639
			/* Yes, this is one of the common commands */
			n++; /* use the entry from common_cmds[] */
640
			if (starts_with(candidate, cmd)) {
641 642 643 644 645 646
				/* Give prefix match a very good score */
				main_cmds.names[i]->len = 0;
				continue;
			}
		}

647
		main_cmds.names[i]->len =
648
			levenshtein(cmd, candidate, 0, 2, 1, 3) + 1;
649
	}
650
	FREE_AND_NULL(common_cmds);
651

René Scharfe's avatar
René Scharfe committed
652
	QSORT(main_cmds.names, main_cmds.cnt, levenshtein_compare);
653 654

	if (!main_cmds.cnt)
655
		die(_("Uh oh. Your system reports no Git commands at all."));
656

657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
	/* skip and count prefix matches */
	for (n = 0; n < main_cmds.cnt && !main_cmds.names[n]->len; n++)
		; /* still counting */

	if (main_cmds.cnt <= n) {
		/* prefix matches with everything? that is too ambiguous */
		best_similarity = SIMILARITY_FLOOR + 1;
	} else {
		/* count all the most similar ones */
		for (best_similarity = main_cmds.names[n++]->len;
		     (n < main_cmds.cnt &&
		      best_similarity == main_cmds.names[n]->len);
		     n++)
			; /* still counting */
	}
672
	if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
673 674 675
		const char *assumed = main_cmds.names[0]->name;
		main_cmds.names[0] = NULL;
		clean_cmdnames(&main_cmds);
676 677
		fprintf_ln(stderr,
			   _("WARNING: You called a Git command named '%s', "
678 679 680 681 682 683 684 685 686 687 688 689
			     "which does not exist."),
			   cmd);
		if (autocorrect < 0)
			fprintf_ln(stderr,
				   _("Continuing under the assumption that "
				     "you meant '%s'."),
				   assumed);
		else {
			fprintf_ln(stderr,
				   _("Continuing in %0.1f seconds, "
				     "assuming that you meant '%s'."),
				   (float)autocorrect/10.0, assumed);
690
			sleep_millisec(autocorrect * 100);
691
		}
692 693 694
		return assumed;
	}

695
	fprintf_ln(stderr, _("git: '%s' is not a git command. See 'git --help'."), cmd);
696

697
	if (SIMILAR_ENOUGH(best_similarity)) {
698
		fprintf_ln(stderr,
699 700
			   Q_("\nThe most similar command is",
			      "\nThe most similar commands are",
701
			   n));
702 703 704 705 706

		for (i = 0; i < n; i++)
			fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
	}

707 708 709
	exit(1);
}

710
int cmd_version(int argc, const char **argv, const char *prefix)
711
{
712 713 714 715 716 717 718 719 720 721 722 723 724
	int build_options = 0;
	const char * const usage[] = {
		N_("git version [<options>]"),
		NULL
	};
	struct option options[] = {
		OPT_BOOL(0, "build-options", &build_options,
			 "also print build options"),
		OPT_END()
	};

	argc = parse_options(argc, argv, prefix, options, usage, 0);

725 726 727
	/*
	 * The format of this string should be kept stable for compatibility
	 * with external projects that rely on the output of "git version".
728 729
	 *
	 * Always show the version, even if other options are given.
730
	 */
731
	printf("git version %s\n", git_version_string);
732 733

	if (build_options) {
734
		printf("cpu: %s\n", GIT_HOST_CPU);
735 736 737 738 739
		if (git_built_from_commit_string[0])
			printf("built from commit: %s\n",
			       git_built_from_commit_string);
		else
			printf("no commit associated with this build\n");
740
		printf("sizeof-long: %d\n", (int)sizeof(long));
741
		printf("sizeof-size_t: %d\n", (int)sizeof(size_t));
742
		/* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */
743
	}
744 745
	return 0;
}
746 747 748 749 750 751

struct similar_ref_cb {
	const char *base_ref;
	struct string_list *similar_refs;
};

752
static int append_similar_ref(const char *refname, const struct object_id *oid,
753 754 755 756
			      int flags, void *cb_data)
{
	struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
	char *branch = strrchr(refname, '/') + 1;
757 758
	const char *remote;

759
	/* A remote branch of the same name is deemed similar */
760
	if (skip_prefix(refname, "refs/remotes/", &remote) &&
761
	    !strcmp(branch, cb->base_ref))
762
		string_list_append(cb->similar_refs, remote);
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
	return 0;
}

static struct string_list guess_refs(const char *ref)
{
	struct similar_ref_cb ref_cb;
	struct string_list similar_refs = STRING_LIST_INIT_NODUP;

	ref_cb.base_ref = ref;
	ref_cb.similar_refs = &similar_refs;
	for_each_ref(append_similar_ref, &ref_cb);
	return similar_refs;
}

void help_unknown_ref(const char *ref, const char *cmd, const char *error)
{
	int i;
	struct string_list suggested_refs = guess_refs(ref);

	fprintf_ln(stderr, _("%s: %s - %s"), cmd, ref, error);

	if (suggested_refs.nr > 0) {
		fprintf_ln(stderr,
			   Q_("\nDid you mean this?",
			      "\nDid you mean one of these?",
			      suggested_refs.nr));
		for (i = 0; i < suggested_refs.nr; i++)
			fprintf(stderr, "\t%s\n", suggested_refs.items[i].string);
	}

	string_list_clear(&suggested_refs, 0);
	exit(1);
}