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

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

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

18
static struct startup_info git_startup_info;
J
Jeff King 已提交
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 55
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;
	}
}

56
static int handle_options(const char ***argv, int *argc, int *envchanged)
57 58 59 60 61 62 63 64
{
	int handled = 0;

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

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

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

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

163
	subdir = setup_git_directory_gently(&unused_nongit);
164 165

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

173 174
				strbuf_init(&buf, PATH_MAX);
				strbuf_addstr(&buf, alias_string);
175
				sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
176
				free(alias_string);
177
				alias_string = buf.buf;
178
			}
179 180 181 182 183 184
			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));
185
			die("Failed to run '%s' when expanding alias '%s'",
186 187
			    alias_string + 1, alias_command);
		}
188
		count = split_cmdline(alias_string, &new_argv);
189
		if (count < 0)
190 191
			die("Bad alias.%s string: %s", alias_command,
			    split_cmdline_strerror(count));
192 193 194 195 196
		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);
197 198 199 200 201 202 203 204 205 206
		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);

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

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

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

219
		ret = 1;
220 221
	}

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

225 226
	errno = saved_errno;

227 228 229
	return ret;
}

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

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

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

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

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

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

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

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

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

	/* 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.. */
287
	if (fflush(stdout))
288
		die_errno("write failure on standard output");
289 290 291
	if (ferror(stdout))
		die("unknown write failure on standard output");
	if (fclose(stdout))
292
		die_errno("close failed on standard output");
293
	return 0;
294 295 296
}

static void handle_internal_command(int argc, const char **argv)
297 298
{
	const char *cmd = argv[0];
299
	static struct cmd_struct commands[] = {
300
		{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
301
		{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
302
		{ "annotate", cmd_annotate, RUN_SETUP },
303
		{ "apply", cmd_apply, RUN_SETUP_GENTLY },
304
		{ "archive", cmd_archive },
305
		{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
306
		{ "blame", cmd_blame, RUN_SETUP },
J
Junio C Hamano 已提交
307
		{ "branch", cmd_branch, RUN_SETUP },
308
		{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
309
		{ "cat-file", cmd_cat_file, RUN_SETUP },
D
Daniel Barkalow 已提交
310
		{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
311 312
		{ "checkout-index", cmd_checkout_index,
			RUN_SETUP | NEED_WORK_TREE},
P
Peter Eriksen 已提交
313
		{ "check-ref-format", cmd_check_ref_format },
314
		{ "check-attr", cmd_check_attr, RUN_SETUP },
R
Rene Scharfe 已提交
315
		{ "cherry", cmd_cherry, RUN_SETUP },
316
		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
D
Daniel Barkalow 已提交
317
		{ "clone", cmd_clone },
S
Shawn Bohrer 已提交
318
		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
K
Kristian Høgsberg 已提交
319
		{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
320
		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
321
		{ "config", cmd_config, RUN_SETUP_GENTLY },
322
		{ "count-objects", cmd_count_objects, RUN_SETUP },
S
Shawn O. Pearce 已提交
323
		{ "describe", cmd_describe, RUN_SETUP },
324
		{ "diff", cmd_diff },
325
		{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
326 327
		{ "diff-index", cmd_diff_index, RUN_SETUP },
		{ "diff-tree", cmd_diff_tree, RUN_SETUP },
328
		{ "fast-export", cmd_fast_export, RUN_SETUP },
D
Daniel Barkalow 已提交
329
		{ "fetch", cmd_fetch, RUN_SETUP },
330
		{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
331
		{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
332
		{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
333
		{ "format-patch", cmd_format_patch, RUN_SETUP },
334 335
		{ "fsck", cmd_fsck, RUN_SETUP },
		{ "fsck-objects", cmd_fsck, RUN_SETUP },
J
James Bowes 已提交
336
		{ "gc", cmd_gc, RUN_SETUP },
J
Junio C Hamano 已提交
337
		{ "get-tar-commit-id", cmd_get_tar_commit_id },
338
		{ "grep", cmd_grep, RUN_SETUP_GENTLY },
339
		{ "hash-object", cmd_hash_object },
J
Junio C Hamano 已提交
340
		{ "help", cmd_help },
341
		{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
342
		{ "init", cmd_init_db },
J
Junio C Hamano 已提交
343
		{ "init-db", cmd_init_db },
344
		{ "log", cmd_log, RUN_SETUP },
345 346
		{ "ls-files", cmd_ls_files, RUN_SETUP },
		{ "ls-tree", cmd_ls_tree, RUN_SETUP },
347
		{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
J
Junio C Hamano 已提交
348 349
		{ "mailinfo", cmd_mailinfo },
		{ "mailsplit", cmd_mailsplit },
M
Miklos Vajna 已提交
350
		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
351
		{ "merge-base", cmd_merge_base, RUN_SETUP },
352
		{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
L
Linus Torvalds 已提交
353
		{ "merge-index", cmd_merge_index, RUN_SETUP },
354
		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
D
Daniel Barkalow 已提交
355
		{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
A
Avery Pennarun 已提交
356 357
		{ "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 已提交
358
		{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
359
		{ "merge-tree", cmd_merge_tree, RUN_SETUP },
L
Linus Torvalds 已提交
360
		{ "mktag", cmd_mktag, RUN_SETUP },
J
Junio C Hamano 已提交
361
		{ "mktree", cmd_mktree, RUN_SETUP },
362
		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
363
		{ "name-rev", cmd_name_rev, RUN_SETUP },
J
Johan Herland 已提交
364
		{ "notes", cmd_notes, RUN_SETUP },
365
		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
366
		{ "pack-redundant", cmd_pack_redundant, RUN_SETUP },
L
Linus Torvalds 已提交
367
		{ "patch-id", cmd_patch_id },
368
		{ "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
369
		{ "pickaxe", cmd_blame, RUN_SETUP },
370 371
		{ "prune", cmd_prune, RUN_SETUP },
		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
372
		{ "push", cmd_push, RUN_SETUP },
373
		{ "read-tree", cmd_read_tree, RUN_SETUP },
J
Junio C Hamano 已提交
374
		{ "receive-pack", cmd_receive_pack },
J
Junio C Hamano 已提交
375
		{ "reflog", cmd_reflog, RUN_SETUP },
J
Johannes Schindelin 已提交
376
		{ "remote", cmd_remote, RUN_SETUP },
I
Ilari Liusvaara 已提交
377
		{ "remote-fd", cmd_remote_fd },
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
		{ "show-branch", cmd_show_branch, RUN_SETUP },
389
		{ "show", cmd_show, RUN_SETUP },
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 },
401
		{ "var", cmd_var, RUN_SETUP_GENTLY },
C
Carlos Rica 已提交
402
		{ "verify-tag", cmd_verify_tag, RUN_SETUP },
J
Junio C Hamano 已提交
403
		{ "version", cmd_version },
404
		{ "whatchanged", cmd_whatchanged, RUN_SETUP },
405
		{ "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;
}