help.c 11.2 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"
Vikrant Varma's avatar
Vikrant Varma committed
10
#include "refs.h"
11

12
void add_cmdname(struct cmdnames *cmds, const char *name, int len)
13
{
14
	struct cmdname *ent = xmalloc(sizeof(*ent) + len + 1);
15

16 17 18
	ent->len = len;
	memcpy(ent->name, name, len);
	ent->name[len] = 0;
19 20 21

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

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

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

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

	if (!cmds->cnt)
		return;

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

	cmds->cnt = j;
}

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

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

	cmds->cnt = cj;
}

81 82
static void pretty_print_string_list(struct cmdnames *cmds,
				     unsigned int colopts)
83
{
84 85 86
	struct string_list list = STRING_LIST_INIT_NODUP;
	struct column_options copts;
	int i;
87

88 89 90 91 92 93 94 95 96 97 98 99
	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);
100 101
}

102 103 104 105 106 107 108 109
static int is_executable(const char *name)
{
	struct stat st;

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

110
#if defined(GIT_WINDOWS_NATIVE) || defined(__CYGWIN__)
111 112 113
#if defined(__CYGWIN__)
if ((st.st_mode & S_IXUSR) == 0)
#endif
李智's avatar
李智 committed
114
{	/* cannot trust the executable bit, peek into the file instead */
115 116 117 118 119 120 121 122 123 124 125 126
	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);
	}
李智's avatar
李智 committed
127
}
128 129 130 131
#endif
	return st.st_mode & S_IXUSR;
}

132
static void list_commands_in_dir(struct cmdnames *cmds,
133 134
					 const char *path,
					 const char *prefix)
135
{
136
	int prefix_len;
137
	DIR *dir = opendir(path);
138
	struct dirent *de;
139 140
	struct strbuf buf = STRBUF_INIT;
	int len;
141

142
	if (!dir)
143
		return;
144 145 146
	if (!prefix)
		prefix = "git-";
	prefix_len = strlen(prefix);
147

148 149 150
	strbuf_addf(&buf, "%s/", path);
	len = buf.len;

151 152 153
	while ((de = readdir(dir)) != NULL) {
		int entlen;

154
		if (prefixcmp(de->d_name, prefix))
155
			continue;
156

157 158 159
		strbuf_setlen(&buf, len);
		strbuf_addstr(&buf, de->d_name);
		if (!is_executable(buf.buf))
160 161
			continue;

162
		entlen = strlen(de->d_name) - prefix_len;
163
		if (has_extension(de->d_name, ".exe"))
164 165
			entlen -= 4;

166
		add_cmdname(cmds, de->d_name + prefix_len, entlen);
167 168
	}
	closedir(dir);
169
	strbuf_release(&buf);
170 171
}

172
void load_command_list(const char *prefix,
173 174
		struct cmdnames *main_cmds,
		struct cmdnames *other_cmds)
175 176 177 178
{
	const char *env_path = getenv("PATH");
	const char *exec_path = git_exec_path();

179
	if (exec_path) {
180
		list_commands_in_dir(main_cmds, exec_path, prefix);
181 182 183
		qsort(main_cmds->names, main_cmds->cnt,
		      sizeof(*main_cmds->names), cmdname_compare);
		uniq(main_cmds);
184 185
	}

186 187 188 189 190 191
	if (env_path) {
		char *paths, *path, *colon;
		path = paths = xstrdup(env_path);
		while (1) {
			if ((colon = strchr(path, PATH_SEP)))
				*colon = 0;
192 193
			if (!exec_path || strcmp(path, exec_path))
				list_commands_in_dir(other_cmds, path, prefix);
194

195 196 197 198 199
			if (!colon)
				break;
			path = colon + 1;
		}
		free(paths);
200

201 202 203 204
		qsort(other_cmds->names, other_cmds->cnt,
		      sizeof(*other_cmds->names), cmdname_compare);
		uniq(other_cmds);
	}
205
	exclude_cmds(other_cmds, main_cmds);
Jeff King's avatar
Jeff King committed
206 207
}

Junio C Hamano's avatar
Junio C Hamano committed
208
void list_commands(unsigned int colopts,
209
		   struct cmdnames *main_cmds, struct cmdnames *other_cmds)
Jeff King's avatar
Jeff King committed
210
{
211
	if (main_cmds->cnt) {
212
		const char *exec_path = git_exec_path();
213
		printf_ln(_("available git commands in '%s'"), exec_path);
214
		putchar('\n');
215
		pretty_print_string_list(main_cmds, colopts);
216 217 218
		putchar('\n');
	}

219
	if (other_cmds->cnt) {
220
		printf_ln(_("git commands available from elsewhere on your $PATH"));
221
		putchar('\n');
222
		pretty_print_string_list(other_cmds, colopts);
223 224
		putchar('\n');
	}
225 226
}

227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
void list_common_cmds_help(void)
{
	int i, longest = 0;

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

	puts(_("The most commonly used git commands are:"));
	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
		printf("   %s   ", common_cmds[i].name);
		mput_char(' ', longest - strlen(common_cmds[i].name));
		puts(_(common_cmds[i].help));
	}
}

244
int is_in_cmdlist(struct cmdnames *c, const char *s)
Jeff King's avatar
Jeff King committed
245 246 247 248 249 250 251 252
{
	int i;
	for (i = 0; i < c->cnt; i++)
		if (!strcmp(s, c->names[i]->name))
			return 1;
	return 0;
}

253
static int autocorrect;
254
static struct cmdnames aliases;
255 256 257 258 259

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);
260 261 262
	/* Also use aliases for command lookup */
	if (!prefixcmp(var, "alias."))
		add_cmdname(&aliases, var + 6, strlen(var + 6));
263 264 265 266

	return git_default_config(var, value, cb);
}

267
static int levenshtein_compare(const void *p1, const void *p2)
268
{
269 270 271 272 273 274 275
	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);
}

276 277 278 279 280 281 282 283 284 285 286 287
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;
}

288
/* An empirically derived magic number */
289 290
#define SIMILARITY_FLOOR 7
#define SIMILAR_ENOUGH(x) ((x) < SIMILARITY_FLOOR)
291

292 293 294 295
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?");

296
const char *help_unknown_cmd(const char *cmd)
297
{
298 299 300 301
	int i, n, best_similarity = 0;
	struct cmdnames main_cmds, other_cmds;

	memset(&main_cmds, 0, sizeof(main_cmds));
302
	memset(&other_cmds, 0, sizeof(other_cmds));
303
	memset(&aliases, 0, sizeof(aliases));
304

305 306
	git_config(git_unknown_cmd_config, NULL);

307 308
	load_command_list("git-", &main_cmds, &other_cmds);

309 310 311 312 313
	add_cmd_list(&main_cmds, &aliases);
	add_cmd_list(&main_cmds, &other_cmds);
	qsort(main_cmds.names, main_cmds.cnt,
	      sizeof(main_cmds.names), cmdname_compare);
	uniq(&main_cmds);
314

315 316 317 318 319
	/* 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;

320 321 322 323 324 325 326 327
		/*
		 * 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);

328 329 330 331 332 333 334 335 336 337 338 339 340 341
		/* 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[] */
			if (!prefixcmp(candidate, cmd)) {
				/* Give prefix match a very good score */
				main_cmds.names[i]->len = 0;
				continue;
			}
		}

342
		main_cmds.names[i]->len =
343
			levenshtein(cmd, candidate, 0, 2, 1, 3) + 1;
344
	}
345 346 347 348 349

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

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

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
	/* 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 */
	}
367
	if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
368 369 370
		const char *assumed = main_cmds.names[0]->name;
		main_cmds.names[0] = NULL;
		clean_cmdnames(&main_cmds);
371 372 373 374
		fprintf_ln(stderr,
			   _("WARNING: You called a Git command named '%s', "
			     "which does not exist.\n"
			     "Continuing under the assumption that you meant '%s'"),
375
			cmd, assumed);
376
		if (autocorrect > 0) {
377
			fprintf_ln(stderr, _("in %0.1f seconds automatically..."),
378 379 380
				(float)autocorrect/10.0);
			poll(NULL, 0, autocorrect * 100);
		}
381 382 383
		return assumed;
	}

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

386
	if (SIMILAR_ENOUGH(best_similarity)) {
387 388 389 390
		fprintf_ln(stderr,
			   Q_("\nDid you mean this?",
			      "\nDid you mean one of these?",
			   n));
391 392 393 394 395

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

396 397 398
	exit(1);
}

399
int cmd_version(int argc, const char **argv, const char *prefix)
400
{
401 402 403 404
	/*
	 * The format of this string should be kept stable for compatibility
	 * with external projects that rely on the output of "git version".
	 */
405 406 407
	printf("git version %s\n", git_version_string);
	return 0;
}
Vikrant Varma's avatar
Vikrant Varma committed
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 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

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

static int append_similar_ref(const char *refname, const unsigned char *sha1,
			      int flags, void *cb_data)
{
	struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
	char *branch = strrchr(refname, '/') + 1;
	/* A remote branch of the same name is deemed similar */
	if (!prefixcmp(refname, "refs/remotes/") &&
	    !strcmp(branch, cb->base_ref))
		string_list_append(cb->similar_refs,
				   refname + strlen("refs/remotes/"));
	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);
}