git.c 15.7 KB
Newer Older
1
#include "builtin.h"
2
#include "exec_cmd.h"
3
#include "cache.h"
4
#include "quote.h"
5
#include "run-command.h"
6

7
const char git_usage_string[] =
M
Matthieu Moy 已提交
8
	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
9
	"           [-p|--paginate|--no-pager] [--no-replace-objects]\n"
M
Matthieu Moy 已提交
10
	"           [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
T
Thomas Rast 已提交
11 12
	"           [-c name=value] [--help]\n"
	"           COMMAND [ARGS]";
13

14 15 16
const char git_more_info_string[] =
	"See 'git help COMMAND' for more information on a specific command.";

17
static struct startup_info git_startup_info;
J
Jeff King 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
static int use_pager = -1;
struct pager_config {
	const char *cmd;
	int val;
};

static int pager_command_config(const char *var, const char *value, void *data)
{
	struct pager_config *c = data;
	if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd))
		c->val = git_config_bool(var, value);
	return 0;
}

/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
int check_pager_config(const char *cmd)
{
	struct pager_config c;
	c.cmd = cmd;
	c.val = -1;
	git_config(pager_command_config, &c);
	return c.val;
}

static void commit_pager_choice(void) {
	switch (use_pager) {
	case 0:
		setenv("GIT_PAGER", "cat", 1);
		break;
	case 1:
		setup_pager();
		break;
	default:
		break;
	}
}

55
static int handle_options(const char ***argv, int *argc, int *envchanged)
56 57 58
{
	int handled = 0;

59 60 61
	if (!getenv("GIT_ASKPASS") && getenv("SSH_ASKPASS"))
		setenv("GIT_ASKPASS", getenv("SSH_ASKPASS"), 1);

62 63 64 65 66
	while (*argc > 0) {
		const char *cmd = (*argv)[0];
		if (cmd[0] != '-')
			break;

67 68 69 70 71 72 73 74 75 76 77
		/*
		 * For legacy reasons, the "version" and "help"
		 * commands can be written with "--" prepended
		 * to make them look like flags.
		 */
		if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
			break;

		/*
		 * Check remaining flags.
		 */
78
		if (!prefixcmp(cmd, "--exec-path")) {
79 80
			cmd += 11;
			if (*cmd == '=')
81
				git_set_argv_exec_path(cmd + 1);
82 83 84 85
			else {
				puts(git_exec_path());
				exit(0);
			}
86 87 88
		} else if (!strcmp(cmd, "--html-path")) {
			puts(system_path(GIT_HTML_PATH));
			exit(0);
89
		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
J
Jeff King 已提交
90
			use_pager = 1;
91
		} else if (!strcmp(cmd, "--no-pager")) {
J
Jeff King 已提交
92
			use_pager = 0;
93 94
			if (envchanged)
				*envchanged = 1;
95 96
		} else if (!strcmp(cmd, "--no-replace-objects")) {
			read_replace_refs = 0;
97 98 99
			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
100
		} else if (!strcmp(cmd, "--git-dir")) {
101 102 103 104
			if (*argc < 2) {
				fprintf(stderr, "No directory given for --git-dir.\n" );
				usage(git_usage_string);
			}
105
			setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
106 107
			if (envchanged)
				*envchanged = 1;
108 109
			(*argv)++;
			(*argc)--;
110
			handled++;
111
		} else if (!prefixcmp(cmd, "--git-dir=")) {
112
			setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
113 114
			if (envchanged)
				*envchanged = 1;
115 116 117 118 119 120
		} else if (!strcmp(cmd, "--work-tree")) {
			if (*argc < 2) {
				fprintf(stderr, "No directory given for --work-tree.\n" );
				usage(git_usage_string);
			}
			setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
121 122
			if (envchanged)
				*envchanged = 1;
123 124 125 126
			(*argv)++;
			(*argc)--;
		} else if (!prefixcmp(cmd, "--work-tree=")) {
			setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
127 128
			if (envchanged)
				*envchanged = 1;
129
		} else if (!strcmp(cmd, "--bare")) {
130
			static char git_dir[PATH_MAX+1];
131
			is_bare_repository_cfg = 1;
132
			setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
133 134
			if (envchanged)
				*envchanged = 1;
135 136 137 138 139 140 141 142
		} else if (!strcmp(cmd, "-c")) {
			if (*argc < 2) {
				fprintf(stderr, "-c expects a configuration string\n" );
				usage(git_usage_string);
			}
			git_config_parse_parameter((*argv)[1]);
			(*argv)++;
			(*argc)--;
143 144
		} else {
			fprintf(stderr, "Unknown option: %s\n", cmd);
145
			usage(git_usage_string);
146
		}
147 148 149 150 151 152 153 154

		(*argv)++;
		(*argc)--;
		handled++;
	}
	return handled;
}

155 156
static int handle_alias(int *argcp, const char ***argv)
{
157
	int envchanged = 0, ret = 0, saved_errno = errno;
158
	const char *subdir;
159
	int count, option_count;
160
	const char **new_argv;
161 162
	const char *alias_command;
	char *alias_string;
163
	int unused_nongit;
164

165
	subdir = setup_git_directory_gently(&unused_nongit);
166 167

	alias_command = (*argv)[0];
168
	alias_string = alias_lookup(alias_command);
169
	if (alias_string) {
170
		if (alias_string[0] == '!') {
171
			commit_pager_choice();
172
			if (*argcp > 1) {
173
				struct strbuf buf;
174

175 176
				strbuf_init(&buf, PATH_MAX);
				strbuf_addstr(&buf, alias_string);
177
				sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
178
				free(alias_string);
179
				alias_string = buf.buf;
180
			}
181 182 183 184 185 186
			trace_printf("trace: alias to shell cmd: %s => %s\n",
				     alias_command, alias_string + 1);
			ret = system(alias_string + 1);
			if (ret >= 0 && WIFEXITED(ret) &&
			    WEXITSTATUS(ret) != 127)
				exit(WEXITSTATUS(ret));
187
			die("Failed to run '%s' when expanding alias '%s'",
188 189
			    alias_string + 1, alias_command);
		}
190
		count = split_cmdline(alias_string, &new_argv);
191 192
		if (count < 0)
			die("Bad alias.%s string", alias_command);
193 194 195 196 197
		option_count = handle_options(&new_argv, &count, &envchanged);
		if (envchanged)
			die("alias '%s' changes environment variables\n"
				 "You can use '!git' in the alias to do this.",
				 alias_command);
198 199 200 201 202 203 204 205 206 207
		memmove(new_argv - option_count, new_argv,
				count * sizeof(char *));
		new_argv -= option_count;

		if (count < 1)
			die("empty alias for %s", alias_command);

		if (!strcmp(alias_command, new_argv[0]))
			die("recursive alias: %s", alias_command);

208
		trace_argv_printf(new_argv,
209 210
				  "trace: alias expansion: %s =>",
				  alias_command);
211

212
		new_argv = xrealloc(new_argv, sizeof(char *) *
213
				    (count + *argcp));
214
		/* insert after command name */
215
		memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
216

217 218
		*argv = new_argv;
		*argcp += count - 1;
219

220
		ret = 1;
221 222
	}

A
Alex Riesen 已提交
223
	if (subdir && chdir(subdir))
224
		die_errno("Cannot change to '%s'", subdir);
225

226 227
	errno = saved_errno;

228 229 230
	return ret;
}

231
const char git_version_string[] = GIT_VERSION;
J
Junio C Hamano 已提交
232

233 234 235
#define RUN_SETUP		(1<<0)
#define RUN_SETUP_GENTLY	(1<<1)
#define USE_PAGER		(1<<2)
236 237 238 239
/*
 * require working tree to be present -- anything uses this needs
 * RUN_SETUP for reading from the configuration file.
 */
240
#define NEED_WORK_TREE		(1<<3)
241

242 243 244 245 246 247
struct cmd_struct {
	const char *cmd;
	int (*fn)(int, const char **, const char *);
	int option;
};

J
Jeff King 已提交
248
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
249
{
250
	int status, help;
251
	struct stat st;
252 253 254
	const char *prefix;

	prefix = NULL;
255 256 257 258
	help = argc == 2 && !strcmp(argv[1], "-h");
	if (!help) {
		if (p->option & RUN_SETUP)
			prefix = setup_git_directory();
259 260 261 262
		if (p->option & RUN_SETUP_GENTLY) {
			int nongit_ok;
			prefix = setup_git_directory_gently(&nongit_ok);
		}
263

264
		if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
265 266 267 268
			use_pager = check_pager_config(p->cmd);
		if (use_pager == -1 && p->option & USE_PAGER)
			use_pager = 1;
	}
J
Jeff King 已提交
269 270
	commit_pager_choice();

271
	if (!help && p->option & NEED_WORK_TREE)
M
Mike Hommey 已提交
272 273
		setup_work_tree();

274
	trace_argv_printf(argv, "trace: built-in: git");
275

276 277
	status = p->fn(argc, argv, prefix);
	if (status)
278
		return status;
279 280 281 282 283 284 285 286 287

	/* Somebody closed stdout? */
	if (fstat(fileno(stdout), &st))
		return 0;
	/* Ignore write errors for pipes and sockets.. */
	if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
		return 0;

	/* Check for ENOSPC and EIO errors.. */
288
	if (fflush(stdout))
289
		die_errno("write failure on standard output");
290 291 292
	if (ferror(stdout))
		die("unknown write failure on standard output");
	if (fclose(stdout))
293
		die_errno("close failed on standard output");
294
	return 0;
295 296 297
}

static void handle_internal_command(int argc, const char **argv)
298 299
{
	const char *cmd = argv[0];
300
	static struct cmd_struct commands[] = {
301
		{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
302
		{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
303
		{ "annotate", cmd_annotate, RUN_SETUP },
304
		{ "apply", cmd_apply, RUN_SETUP_GENTLY },
305
		{ "archive", cmd_archive },
306
		{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
307
		{ "blame", cmd_blame, RUN_SETUP },
J
Junio C Hamano 已提交
308
		{ "branch", cmd_branch, RUN_SETUP },
309
		{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
310
		{ "cat-file", cmd_cat_file, RUN_SETUP },
D
Daniel Barkalow 已提交
311
		{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
312 313
		{ "checkout-index", cmd_checkout_index,
			RUN_SETUP | NEED_WORK_TREE},
P
Peter Eriksen 已提交
314
		{ "check-ref-format", cmd_check_ref_format },
315
		{ "check-attr", cmd_check_attr, RUN_SETUP },
R
Rene Scharfe 已提交
316
		{ "cherry", cmd_cherry, RUN_SETUP },
317
		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
D
Daniel Barkalow 已提交
318
		{ "clone", cmd_clone },
S
Shawn Bohrer 已提交
319
		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
K
Kristian Høgsberg 已提交
320
		{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
321
		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
322
		{ "config", cmd_config, RUN_SETUP_GENTLY },
323
		{ "count-objects", cmd_count_objects, RUN_SETUP },
S
Shawn O. Pearce 已提交
324
		{ "describe", cmd_describe, RUN_SETUP },
325
		{ "diff", cmd_diff },
326
		{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
327 328
		{ "diff-index", cmd_diff_index, RUN_SETUP },
		{ "diff-tree", cmd_diff_tree, RUN_SETUP },
329
		{ "fast-export", cmd_fast_export, RUN_SETUP },
D
Daniel Barkalow 已提交
330
		{ "fetch", cmd_fetch, RUN_SETUP },
331
		{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
332
		{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
333
		{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
334
		{ "format-patch", cmd_format_patch, RUN_SETUP },
335 336
		{ "fsck", cmd_fsck, RUN_SETUP },
		{ "fsck-objects", cmd_fsck, RUN_SETUP },
J
James Bowes 已提交
337
		{ "gc", cmd_gc, RUN_SETUP },
J
Junio C Hamano 已提交
338
		{ "get-tar-commit-id", cmd_get_tar_commit_id },
339
		{ "grep", cmd_grep, RUN_SETUP_GENTLY },
340
		{ "hash-object", cmd_hash_object },
J
Junio C Hamano 已提交
341
		{ "help", cmd_help },
342
		{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
343
		{ "init", cmd_init_db },
J
Junio C Hamano 已提交
344
		{ "init-db", cmd_init_db },
345 346 347
		{ "log", cmd_log, RUN_SETUP | USE_PAGER },
		{ "ls-files", cmd_ls_files, RUN_SETUP },
		{ "ls-tree", cmd_ls_tree, RUN_SETUP },
348
		{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
J
Junio C Hamano 已提交
349 350
		{ "mailinfo", cmd_mailinfo },
		{ "mailsplit", cmd_mailsplit },
M
Miklos Vajna 已提交
351
		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
352
		{ "merge-base", cmd_merge_base, RUN_SETUP },
353
		{ "merge-file", cmd_merge_file },
L
Linus Torvalds 已提交
354
		{ "merge-index", cmd_merge_index, RUN_SETUP },
355
		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
D
Daniel Barkalow 已提交
356
		{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
A
Avery Pennarun 已提交
357 358
		{ "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
		{ "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
359
		{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
360
		{ "merge-tree", cmd_merge_tree, RUN_SETUP },
L
Linus Torvalds 已提交
361
		{ "mktag", cmd_mktag, RUN_SETUP },
J
Junio C Hamano 已提交
362
		{ "mktree", cmd_mktree, RUN_SETUP },
363
		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
364
		{ "name-rev", cmd_name_rev, RUN_SETUP },
J
Johan Herland 已提交
365
		{ "notes", cmd_notes, RUN_SETUP },
366
		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
367
		{ "pack-redundant", cmd_pack_redundant, RUN_SETUP },
L
Linus Torvalds 已提交
368
		{ "patch-id", cmd_patch_id },
369
		{ "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
370
		{ "pickaxe", cmd_blame, RUN_SETUP },
371 372
		{ "prune", cmd_prune, RUN_SETUP },
		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
373
		{ "push", cmd_push, RUN_SETUP },
374
		{ "read-tree", cmd_read_tree, RUN_SETUP },
J
Junio C Hamano 已提交
375
		{ "receive-pack", cmd_receive_pack },
J
Junio C Hamano 已提交
376
		{ "reflog", cmd_reflog, RUN_SETUP },
J
Johannes Schindelin 已提交
377
		{ "remote", cmd_remote, RUN_SETUP },
C
Christian Couder 已提交
378
		{ "replace", cmd_replace, RUN_SETUP },
379
		{ "repo-config", cmd_config, RUN_SETUP_GENTLY },
J
Johannes Schindelin 已提交
380
		{ "rerere", cmd_rerere, RUN_SETUP },
C
Carlos Rica 已提交
381
		{ "reset", cmd_reset, RUN_SETUP },
382
		{ "rev-list", cmd_rev_list, RUN_SETUP },
J
Junio C Hamano 已提交
383
		{ "rev-parse", cmd_rev_parse },
384
		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
385
		{ "rm", cmd_rm, RUN_SETUP },
386
		{ "send-pack", cmd_send_pack, RUN_SETUP },
387
		{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
388 389
		{ "show-branch", cmd_show_branch, RUN_SETUP },
		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
390
		{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
391
		{ "stripspace", cmd_stripspace },
392
		{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
C
Carlos Rica 已提交
393
		{ "tag", cmd_tag, RUN_SETUP },
R
Rene Scharfe 已提交
394
		{ "tar-tree", cmd_tar_tree },
395
		{ "unpack-file", cmd_unpack_file, RUN_SETUP },
396 397 398
		{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
		{ "update-index", cmd_update_index, RUN_SETUP },
		{ "update-ref", cmd_update_ref, RUN_SETUP },
399
		{ "update-server-info", cmd_update_server_info, RUN_SETUP },
F
Franck Bui-Huu 已提交
400
		{ "upload-archive", cmd_upload_archive },
L
Linus Torvalds 已提交
401
		{ "var", cmd_var },
C
Carlos Rica 已提交
402
		{ "verify-tag", cmd_verify_tag, RUN_SETUP },
J
Junio C Hamano 已提交
403
		{ "version", cmd_version },
404 405
		{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
		{ "write-tree", cmd_write_tree, RUN_SETUP },
R
Rene Scharfe 已提交
406
		{ "verify-pack", cmd_verify_pack },
407
		{ "show-ref", cmd_show_ref, RUN_SETUP },
408
		{ "pack-refs", cmd_pack_refs, RUN_SETUP },
409 410
	};
	int i;
411 412 413 414 415
	static const char ext[] = STRIP_EXTENSION;

	if (sizeof(ext) > 1) {
		i = strlen(argv[0]) - strlen(ext);
		if (i > 0 && !strcmp(argv[0] + i, ext)) {
416
			char *argv0 = xstrdup(argv[0]);
417 418 419 420
			argv[0] = cmd = argv0;
			argv0[i] = '\0';
		}
	}
421

L
Linus Torvalds 已提交
422 423 424 425 426 427
	/* Turn "git cmd --help" into "git help cmd" */
	if (argc > 1 && !strcmp(argv[1], "--help")) {
		argv[1] = argv[0];
		argv[0] = cmd = "help";
	}

428 429 430 431
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		struct cmd_struct *p = commands+i;
		if (strcmp(p->cmd, cmd))
			continue;
J
Jeff King 已提交
432
		exit(run_builtin(p, argc, argv));
433 434 435
	}
}

436 437
static void execv_dashed_external(const char **argv)
{
438
	struct strbuf cmd = STRBUF_INIT;
439
	const char *tmp;
440
	int status;
441

442 443
	commit_pager_choice();

444 445 446 447 448 449 450 451 452 453 454 455 456
	strbuf_addf(&cmd, "git-%s", argv[0]);

	/*
	 * argv[0] must be the git command, but the argv array
	 * belongs to the caller, and may be reused in
	 * subsequent loop iterations. Save argv[0] and
	 * restore it on error.
	 */
	tmp = argv[0];
	argv[0] = cmd.buf;

	trace_argv_printf(argv, "trace: exec:");

457 458 459 460
	/*
	 * if we fail because the command is not found, it is
	 * OK to return. Otherwise, we just pass along the status code.
	 */
461
	status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
462
	if (status >= 0 || errno != ENOENT)
463
		exit(status);
464 465 466 467 468 469

	argv[0] = tmp;

	strbuf_release(&cmd);
}

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
static int run_argv(int *argcp, const char ***argv)
{
	int done_alias = 0;

	while (1) {
		/* See if it's an internal command */
		handle_internal_command(*argcp, *argv);

		/* .. then try the external ones */
		execv_dashed_external(*argv);

		/* It could be an alias -- this works around the insanity
		 * of overriding "git log" with "git show" by having
		 * alias.log = show
		 */
		if (done_alias || !handle_alias(argcp, argv))
			break;
		done_alias = 1;
	}

	return done_alias;
}

493

494
int main(int argc, const char **argv)
495
{
496
	const char *cmd;
497

498 499
	startup_info = &git_startup_info;

500 501
	cmd = git_extract_argv0_path(argv[0]);
	if (!cmd)
502
		cmd = "git-help";
503

504 505 506 507 508 509 510 511 512 513
	/*
	 * "git-xxxx" is the same as "git xxxx", but we obviously:
	 *
	 *  - cannot take flags in between the "git" and the "xxxx".
	 *  - cannot execute it externally (since it would just do
	 *    the same thing over again)
	 *
	 * So we just directly call the internal command handler, and
	 * die if that one cannot handle it.
	 */
514
	if (!prefixcmp(cmd, "git-")) {
515 516
		cmd += 4;
		argv[0] = cmd;
517
		handle_internal_command(argc, argv);
518 519
		die("cannot handle %s internally", cmd);
	}
520

521
	/* Look for flags.. */
522 523
	argv++;
	argc--;
524
	handle_options(&argv, &argc, NULL);
525
	if (argc > 0) {
526
		if (!prefixcmp(argv[0], "--"))
527 528
			argv[0] += 2;
	} else {
529
		/* The user didn't specify a command; give them help */
530
		commit_pager_choice();
531 532
		printf("usage: %s\n\n", git_usage_string);
		list_common_cmds_help();
533
		printf("\n%s\n", git_more_info_string);
534
		exit(1);
535
	}
536
	cmd = argv[0];
537 538

	/*
539
	 * We use PATH to find git commands, but we prepend some higher
M
Mike Ralphson 已提交
540
	 * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
541 542
	 * environment, and the $(gitexecdir) from the Makefile at build
	 * time.
543
	 */
544
	setup_path();
545

J
Junio C Hamano 已提交
546
	while (1) {
547 548 549 550
		static int done_help = 0;
		static int was_alias = 0;
		was_alias = run_argv(&argc, &argv);
		if (errno != ENOENT)
J
Junio C Hamano 已提交
551
			break;
552
		if (was_alias) {
553
			fprintf(stderr, "Expansion of alias '%s' failed; "
554
				"'%s' is not a git command\n",
555 556 557
				cmd, argv[0]);
			exit(1);
		}
558 559 560 561 562
		if (!done_help) {
			cmd = argv[0] = help_unknown_cmd(cmd);
			done_help = 1;
		} else
			break;
563
	}
564 565

	fprintf(stderr, "Failed to run command '%s': %s\n",
566
		cmd, strerror(errno));
567 568 569

	return 1;
}