help.c 11.5 KB
Newer Older
1 2 3
#include "cache.h"
#include "builtin.h"
#include "exec_cmd.h"
4
#include "levenshtein.h"
5
#include "help.h"
6
#include "common-cmds.h"
7 8
#include "string-list.h"
#include "column.h"
9
#include "version.h"
10
#include "refs.h"
11

12
void add_cmdname(struct cmdnames *cmds, const char *name, int len)
13
{
14 15
	struct cmdname *ent;
	FLEX_ALLOC_MEM(ent, name, name, len);
16
	ent->len = len;
17 18 19

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

22 23 24 25 26 27 28 29 30 31
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;
}

32 33 34 35 36 37 38
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);
}

39 40 41 42 43 44 45
static void uniq(struct cmdnames *cmds)
{
	int i, j;

	if (!cmds->cnt)
		return;

Jeff King's avatar
Jeff King committed
46 47 48 49
	for (i = j = 1; i < cmds->cnt; i++) {
		if (!strcmp(cmds->names[i]->name, cmds->names[j-1]->name))
			free(cmds->names[i]);
		else
50
			cmds->names[j++] = cmds->names[i];
Jeff King's avatar
Jeff King committed
51
	}
52 53 54 55

	cmds->cnt = j;
}

56
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
57
{
58 59 60 61 62 63 64 65
	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++];
66 67 68 69
		else if (cmp == 0) {
			ei++;
			free(cmds->names[ci++]);
		} else if (cmp > 0)
70 71 72 73 74 75 76 77 78
			ei++;
	}

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

	cmds->cnt = cj;
}

79
static void pretty_print_cmdnames(struct cmdnames *cmds, unsigned int colopts)
80
{
81 82 83
	struct string_list list = STRING_LIST_INIT_NODUP;
	struct column_options copts;
	int i;
84

85 86 87 88 89 90 91 92 93 94 95 96
	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);
97 98
}

99 100 101 102 103 104 105 106
static int is_executable(const char *name)
{
	struct stat st;

	if (stat(name, &st) || /* stat, not lstat */
	    !S_ISREG(st.st_mode))
		return 0;

107
#if defined(GIT_WINDOWS_NATIVE)
108
{	/* cannot trust the executable bit, peek into the file instead */
109 110 111 112 113 114 115 116 117 118 119 120
	char buf[3] = { 0 };
	int n;
	int fd = open(name, O_RDONLY);
	st.st_mode &= ~S_IXUSR;
	if (fd >= 0) {
		n = read(fd, buf, 2);
		if (n == 2)
			/* DOS executables start with "MZ" */
			if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
				st.st_mode |= S_IXUSR;
		close(fd);
	}
121
}
122 123 124 125
#endif
	return st.st_mode & S_IXUSR;
}

126
static void list_commands_in_dir(struct cmdnames *cmds,
127 128
					 const char *path,
					 const char *prefix)
129
{
130
	DIR *dir = opendir(path);
131
	struct dirent *de;
132 133
	struct strbuf buf = STRBUF_INIT;
	int len;
134

135
	if (!dir)
136
		return;
137 138
	if (!prefix)
		prefix = "git-";
139

140 141 142
	strbuf_addf(&buf, "%s/", path);
	len = buf.len;

143
	while ((de = readdir(dir)) != NULL) {
144
		const char *ent;
145
		size_t entlen;
146

147
		if (!skip_prefix(de->d_name, prefix, &ent))
148
			continue;
149

150 151 152
		strbuf_setlen(&buf, len);
		strbuf_addstr(&buf, de->d_name);
		if (!is_executable(buf.buf))
153 154
			continue;

155
		entlen = strlen(ent);
156
		strip_suffix(ent, ".exe", &entlen);
157

158
		add_cmdname(cmds, ent, entlen);
159 160
	}
	closedir(dir);
161
	strbuf_release(&buf);
162 163
}

164
void load_command_list(const char *prefix,
165 166
		struct cmdnames *main_cmds,
		struct cmdnames *other_cmds)
167 168 169 170
{
	const char *env_path = getenv("PATH");
	const char *exec_path = git_exec_path();

171
	if (exec_path) {
172
		list_commands_in_dir(main_cmds, exec_path, prefix);
173 174 175
		qsort(main_cmds->names, main_cmds->cnt,
		      sizeof(*main_cmds->names), cmdname_compare);
		uniq(main_cmds);
176 177
	}

178 179 180 181 182 183
	if (env_path) {
		char *paths, *path, *colon;
		path = paths = xstrdup(env_path);
		while (1) {
			if ((colon = strchr(path, PATH_SEP)))
				*colon = 0;
184 185
			if (!exec_path || strcmp(path, exec_path))
				list_commands_in_dir(other_cmds, path, prefix);
186

187 188 189 190 191
			if (!colon)
				break;
			path = colon + 1;
		}
		free(paths);
192

193 194 195 196
		qsort(other_cmds->names, other_cmds->cnt,
		      sizeof(*other_cmds->names), cmdname_compare);
		uniq(other_cmds);
	}
197
	exclude_cmds(other_cmds, main_cmds);
Jeff King's avatar
Jeff King committed
198 199
}

200
void list_commands(unsigned int colopts,
201
		   struct cmdnames *main_cmds, struct cmdnames *other_cmds)
Jeff King's avatar
Jeff King committed
202
{
203
	if (main_cmds->cnt) {
204
		const char *exec_path = git_exec_path();
205
		printf_ln(_("available git commands in '%s'"), exec_path);
206
		putchar('\n');
207
		pretty_print_cmdnames(main_cmds, colopts);
208 209 210
		putchar('\n');
	}

211
	if (other_cmds->cnt) {
212
		printf_ln(_("git commands available from elsewhere on your $PATH"));
213
		putchar('\n');
214
		pretty_print_cmdnames(other_cmds, colopts);
215 216
		putchar('\n');
	}
217 218
}

219 220 221 222 223 224 225 226 227 228 229 230
static int cmd_group_cmp(const void *elem1, const void *elem2)
{
	const struct cmdname_help *e1 = elem1;
	const struct cmdname_help *e2 = elem2;

	if (e1->group < e2->group)
		return -1;
	if (e1->group > e2->group)
		return 1;
	return strcmp(e1->name, e2->name);
}

231 232 233
void list_common_cmds_help(void)
{
	int i, longest = 0;
234
	int current_grp = -1;
235 236 237 238 239 240

	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
		if (longest < strlen(common_cmds[i].name))
			longest = strlen(common_cmds[i].name);
	}

241 242 243 244 245
	qsort(common_cmds, ARRAY_SIZE(common_cmds),
		sizeof(common_cmds[0]), cmd_group_cmp);

	puts(_("These are common Git commands used in various situations:"));

246
	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
247 248 249 250 251
		if (common_cmds[i].group != current_grp) {
			printf("\n%s\n", _(common_cmd_groups[common_cmds[i].group]));
			current_grp = common_cmds[i].group;
		}

252 253 254 255 256 257
		printf("   %s   ", common_cmds[i].name);
		mput_char(' ', longest - strlen(common_cmds[i].name));
		puts(_(common_cmds[i].help));
	}
}

258
int is_in_cmdlist(struct cmdnames *c, const char *s)
Jeff King's avatar
Jeff King committed
259 260 261 262 263 264 265 266
{
	int i;
	for (i = 0; i < c->cnt; i++)
		if (!strcmp(s, c->names[i]->name))
			return 1;
	return 0;
}

267
static int autocorrect;
268
static struct cmdnames aliases;
269 270 271

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

274 275
	if (!strcmp(var, "help.autocorrect"))
		autocorrect = git_config_int(var,value);
276
	/* Also use aliases for command lookup */
277 278
	if (skip_prefix(var, "alias.", &p))
		add_cmdname(&aliases, p, strlen(p));
279 280 281 282

	return git_default_config(var, value, cb);
}

283
static int levenshtein_compare(const void *p1, const void *p2)
284
{
285 286 287 288 289 290 291
	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);
}

292 293 294 295 296 297 298 299 300 301 302 303
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];
	free(old->names);
	old->cnt = 0;
	old->names = NULL;
}

304
/* An empirically derived magic number */
305 306
#define SIMILARITY_FLOOR 7
#define SIMILAR_ENOUGH(x) ((x) < SIMILARITY_FLOOR)
307

308 309 310 311
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?");

312
const char *help_unknown_cmd(const char *cmd)
313
{
314 315 316 317
	int i, n, best_similarity = 0;
	struct cmdnames main_cmds, other_cmds;

	memset(&main_cmds, 0, sizeof(main_cmds));
318
	memset(&other_cmds, 0, sizeof(other_cmds));
319
	memset(&aliases, 0, sizeof(aliases));
320

321 322
	git_config(git_unknown_cmd_config, NULL);

323 324
	load_command_list("git-", &main_cmds, &other_cmds);

325 326 327
	add_cmd_list(&main_cmds, &aliases);
	add_cmd_list(&main_cmds, &other_cmds);
	qsort(main_cmds.names, main_cmds.cnt,
328
	      sizeof(*main_cmds.names), cmdname_compare);
329
	uniq(&main_cmds);
330

331 332 333 334 335
	/* 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;

336 337 338 339 340 341 342 343
		/*
		 * 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);

344 345 346 347 348 349 350
		/* Does the candidate appear in common_cmds list? */
		while (n < ARRAY_SIZE(common_cmds) &&
		       (cmp = strcmp(common_cmds[n].name, candidate)) < 0)
			n++;
		if ((n < ARRAY_SIZE(common_cmds)) && !cmp) {
			/* Yes, this is one of the common commands */
			n++; /* use the entry from common_cmds[] */
351
			if (starts_with(candidate, cmd)) {
352 353 354 355 356 357
				/* Give prefix match a very good score */
				main_cmds.names[i]->len = 0;
				continue;
			}
		}

358
		main_cmds.names[i]->len =
359
			levenshtein(cmd, candidate, 0, 2, 1, 3) + 1;
360
	}
361 362 363 364 365

	qsort(main_cmds.names, main_cmds.cnt,
	      sizeof(*main_cmds.names), levenshtein_compare);

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

368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
	/* 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 */
	}
383
	if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
384 385 386
		const char *assumed = main_cmds.names[0]->name;
		main_cmds.names[0] = NULL;
		clean_cmdnames(&main_cmds);
387 388 389 390
		fprintf_ln(stderr,
			   _("WARNING: You called a Git command named '%s', "
			     "which does not exist.\n"
			     "Continuing under the assumption that you meant '%s'"),
391
			cmd, assumed);
392
		if (autocorrect > 0) {
393
			fprintf_ln(stderr, _("in %0.1f seconds automatically..."),
394
				(float)autocorrect/10.0);
395
			sleep_millisec(autocorrect * 100);
396
		}
397 398 399
		return assumed;
	}

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

402
	if (SIMILAR_ENOUGH(best_similarity)) {
403 404 405 406
		fprintf_ln(stderr,
			   Q_("\nDid you mean this?",
			      "\nDid you mean one of these?",
			   n));
407 408 409 410 411

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

412 413 414
	exit(1);
}

415
int cmd_version(int argc, const char **argv, const char *prefix)
416
{
417 418 419 420
	/*
	 * The format of this string should be kept stable for compatibility
	 * with external projects that rely on the output of "git version".
	 */
421 422 423
	printf("git version %s\n", git_version_string);
	return 0;
}
424 425 426 427 428 429

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

430
static int append_similar_ref(const char *refname, const struct object_id *oid,
431 432 433 434
			      int flags, void *cb_data)
{
	struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
	char *branch = strrchr(refname, '/') + 1;
435 436
	const char *remote;

437
	/* A remote branch of the same name is deemed similar */
438
	if (skip_prefix(refname, "refs/remotes/", &remote) &&
439
	    !strcmp(branch, cb->base_ref))
440
		string_list_append(cb->similar_refs, remote);
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
	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);
}