git.c 26 KB
Newer Older
1
#include "builtin.h"
2
#include "config.h"
3
#include "exec-cmd.h"
4
#include "help.h"
5
#include "run-command.h"
6
#include "alias.h"
7

8 9 10 11 12 13 14 15 16 17
#define RUN_SETUP		(1<<0)
#define RUN_SETUP_GENTLY	(1<<1)
#define USE_PAGER		(1<<2)
/*
 * require working tree to be present -- anything uses this needs
 * RUN_SETUP for reading from the configuration file.
 */
#define NEED_WORK_TREE		(1<<3)
#define SUPPORT_SUPER_PREFIX	(1<<4)
#define DELAY_PAGER_CONFIG	(1<<5)
18
#define NO_PARSEOPT		(1<<6) /* parse-options is not used */
19 20 21 22

struct cmd_struct {
	const char *cmd;
	int (*fn)(int, const char **, const char *);
23
	unsigned int option;
24 25
};

26
const char git_usage_string[] =
27 28
	N_("git [--version] [--help] [-C <path>] [-c <name>=<value>]\n"
	   "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
29
	   "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
30 31
	   "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
	   "           <command> [<args>]");
32

33
const char git_more_info_string[] =
34
	N_("'git help -a' and 'git help -g' list available subcommands and some\n"
35 36
	   "concept guides. See 'git help <command>' or 'git help <concept>'\n"
	   "to read about a specific subcommand or concept.");
37

38
static int use_pager = -1;
39

40
static void list_builtins(struct string_list *list, unsigned int exclude_option);
41

42 43 44 45 46 47 48 49 50 51 52 53
static void exclude_helpers_from_list(struct string_list *list)
{
	int i = 0;

	while (i < list->nr) {
		if (strstr(list->items[i].string, "--"))
			unsorted_string_list_delete_item(list, i, 0);
		else
			i++;
	}
}

54 55 56 57 58 59 60 61 62
static int match_token(const char *spec, int len, const char *token)
{
	int token_len = strlen(token);

	return len == token_len && !strncmp(spec, token, token_len);
}

static int list_cmds(const char *spec)
{
63 64
	struct string_list list = STRING_LIST_INIT_DUP;
	int i;
65 66 67 68 69 70 71
	int nongit;

	/*
	* Set up the repository so we can pick up any repo-level config (like
	* completion.commands).
	*/
	setup_git_directory_gently(&nongit);
72

73 74 75 76 77
	while (*spec) {
		const char *sep = strchrnul(spec, ',');
		int len = sep - spec;

		if (match_token(spec, len, "builtins"))
78
			list_builtins(&list, 0);
79 80 81 82
		else if (match_token(spec, len, "main"))
			list_all_main_cmds(&list);
		else if (match_token(spec, len, "others"))
			list_all_other_cmds(&list);
83 84
		else if (match_token(spec, len, "nohelpers"))
			exclude_helpers_from_list(&list);
85 86
		else if (match_token(spec, len, "alias"))
			list_aliases(&list);
87 88
		else if (match_token(spec, len, "config"))
			list_cmds_by_config(&list);
89 90 91 92 93 94 95
		else if (len > 5 && !strncmp(spec, "list-", 5)) {
			struct strbuf sb = STRBUF_INIT;

			strbuf_add(&sb, spec + 5, len - 5);
			list_cmds_by_category(&list, sb.buf);
			strbuf_release(&sb);
		}
96 97 98 99 100 101
		else
			die(_("unsupported command listing type '%s'"), spec);
		spec += len;
		if (*spec == ',')
			spec++;
	}
102 103 104
	for (i = 0; i < list.nr; i++)
		puts(list.items[i].string);
	string_list_clear(&list, 0);
105 106
	return 0;
}
107

108 109
static void commit_pager_choice(void)
{
110 111 112 113 114 115 116 117 118 119 120 121
	switch (use_pager) {
	case 0:
		setenv("GIT_PAGER", "cat", 1);
		break;
	case 1:
		setup_pager();
		break;
	default:
		break;
	}
}

122 123 124 125 126 127 128 129 130 131
void setup_auto_pager(const char *cmd, int def)
{
	if (use_pager != -1 || pager_in_use())
		return;
	use_pager = check_pager_config(cmd);
	if (use_pager == -1)
		use_pager = def;
	commit_pager_choice();
}

132
static int handle_options(const char ***argv, int *argc, int *envchanged)
133
{
134
	const char **orig_argv = *argv;
135 136 137 138 139 140

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

141 142 143 144 145 146 147 148 149 150 151
		/*
		 * 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.
		 */
152
		if (skip_prefix(cmd, "--exec-path", &cmd)) {
153
			if (*cmd == '=')
154
				git_set_exec_path(cmd + 1);
155 156
			else {
				puts(git_exec_path());
157
				trace2_cmd_name("_query_");
158 159
				exit(0);
			}
160 161
		} else if (!strcmp(cmd, "--html-path")) {
			puts(system_path(GIT_HTML_PATH));
162
			trace2_cmd_name("_query_");
163
			exit(0);
164 165
		} else if (!strcmp(cmd, "--man-path")) {
			puts(system_path(GIT_MAN_PATH));
166
			trace2_cmd_name("_query_");
167 168 169
			exit(0);
		} else if (!strcmp(cmd, "--info-path")) {
			puts(system_path(GIT_INFO_PATH));
170
			trace2_cmd_name("_query_");
171
			exit(0);
172
		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
173
			use_pager = 1;
174
		} else if (!strcmp(cmd, "-P") || !strcmp(cmd, "--no-pager")) {
175
			use_pager = 0;
176 177
			if (envchanged)
				*envchanged = 1;
178
		} else if (!strcmp(cmd, "--no-replace-objects")) {
179
			read_replace_refs = 0;
180 181 182
			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
183
		} else if (!strcmp(cmd, "--git-dir")) {
184
			if (*argc < 2) {
185
				fprintf(stderr, _("no directory given for --git-dir\n" ));
186 187
				usage(git_usage_string);
			}
188
			setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
189 190
			if (envchanged)
				*envchanged = 1;
191 192
			(*argv)++;
			(*argc)--;
193 194
		} else if (skip_prefix(cmd, "--git-dir=", &cmd)) {
			setenv(GIT_DIR_ENVIRONMENT, cmd, 1);
195 196
			if (envchanged)
				*envchanged = 1;
197 198
		} else if (!strcmp(cmd, "--namespace")) {
			if (*argc < 2) {
199
				fprintf(stderr, _("no namespace given for --namespace\n" ));
200 201 202 203 204 205 206
				usage(git_usage_string);
			}
			setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
			if (envchanged)
				*envchanged = 1;
			(*argv)++;
			(*argc)--;
207 208
		} else if (skip_prefix(cmd, "--namespace=", &cmd)) {
			setenv(GIT_NAMESPACE_ENVIRONMENT, cmd, 1);
209 210
			if (envchanged)
				*envchanged = 1;
211 212
		} else if (!strcmp(cmd, "--work-tree")) {
			if (*argc < 2) {
213
				fprintf(stderr, _("no directory given for --work-tree\n" ));
214 215 216
				usage(git_usage_string);
			}
			setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
217 218
			if (envchanged)
				*envchanged = 1;
219 220
			(*argv)++;
			(*argc)--;
221 222
		} else if (skip_prefix(cmd, "--work-tree=", &cmd)) {
			setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
223 224
			if (envchanged)
				*envchanged = 1;
225 226
		} else if (!strcmp(cmd, "--super-prefix")) {
			if (*argc < 2) {
227
				fprintf(stderr, _("no prefix given for --super-prefix\n" ));
228 229 230 231 232 233 234 235 236 237 238
				usage(git_usage_string);
			}
			setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
			if (envchanged)
				*envchanged = 1;
			(*argv)++;
			(*argc)--;
		} else if (skip_prefix(cmd, "--super-prefix=", &cmd)) {
			setenv(GIT_SUPER_PREFIX_ENVIRONMENT, cmd, 1);
			if (envchanged)
				*envchanged = 1;
239
		} else if (!strcmp(cmd, "--bare")) {
240
			char *cwd = xgetcwd();
241
			is_bare_repository_cfg = 1;
242 243
			setenv(GIT_DIR_ENVIRONMENT, cwd, 0);
			free(cwd);
244
			setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
245 246
			if (envchanged)
				*envchanged = 1;
247 248
		} else if (!strcmp(cmd, "-c")) {
			if (*argc < 2) {
249
				fprintf(stderr, _("-c expects a configuration string\n" ));
250 251
				usage(git_usage_string);
			}
252
			git_config_push_parameter((*argv)[1]);
253 254
			(*argv)++;
			(*argc)--;
255 256 257 258 259 260 261 262
		} else if (!strcmp(cmd, "--literal-pathspecs")) {
			setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--no-literal-pathspecs")) {
			setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
			if (envchanged)
				*envchanged = 1;
263 264 265 266 267 268 269 270
		} else if (!strcmp(cmd, "--glob-pathspecs")) {
			setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--noglob-pathspecs")) {
			setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
271 272 273 274
		} else if (!strcmp(cmd, "--icase-pathspecs")) {
			setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
275 276 277 278
		} else if (!strcmp(cmd, "--no-optional-locks")) {
			setenv(GIT_OPTIONAL_LOCKS_ENVIRONMENT, "0", 1);
			if (envchanged)
				*envchanged = 1;
279 280 281
		} else if (!strcmp(cmd, "--shallow-file")) {
			(*argv)++;
			(*argc)--;
282
			set_alternate_shallow_file(the_repository, (*argv)[0], 1);
283 284
			if (envchanged)
				*envchanged = 1;
285 286
		} else if (!strcmp(cmd, "-C")) {
			if (*argc < 2) {
287
				fprintf(stderr, _("no directory given for -C\n" ));
288 289
				usage(git_usage_string);
			}
290 291
			if ((*argv)[1][0]) {
				if (chdir((*argv)[1]))
292
					die_errno("cannot change to '%s'", (*argv)[1]);
293 294 295
				if (envchanged)
					*envchanged = 1;
			}
296 297
			(*argv)++;
			(*argc)--;
298
		} else if (skip_prefix(cmd, "--list-cmds=", &cmd)) {
299
			trace2_cmd_name("_query_");
300
			if (!strcmp(cmd, "parseopt")) {
301 302 303 304 305 306 307
				struct string_list list = STRING_LIST_INIT_DUP;
				int i;

				list_builtins(&list, NO_PARSEOPT);
				for (i = 0; i < list.nr; i++)
					printf("%s ", list.items[i].string);
				string_list_clear(&list, 0);
308 309 310 311
				exit(0);
			} else {
				exit(list_cmds(cmd));
			}
312
		} else {
313
			fprintf(stderr, _("unknown option: %s\n"), cmd);
314
			usage(git_usage_string);
315
		}
316 317 318 319

		(*argv)++;
		(*argc)--;
	}
320
	return (*argv) - orig_argv;
321 322
}

323 324
static int handle_alias(int *argcp, const char ***argv)
{
325
	int envchanged = 0, ret = 0, saved_errno = errno;
326
	int count, option_count;
327
	const char **new_argv;
328 329
	const char *alias_command;
	char *alias_string;
330 331

	alias_command = (*argv)[0];
332
	alias_string = alias_lookup(alias_command);
333
	if (alias_string) {
334 335 336
		if (*argcp > 1 && !strcmp((*argv)[1], "-h"))
			fprintf_ln(stderr, _("'%s' is aliased to '%s'"),
				   alias_command, alias_string);
337
		if (alias_string[0] == '!') {
338
			struct child_process child = CHILD_PROCESS_INIT;
339 340 341 342
			int nongit_ok;

			/* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */
			setup_git_directory_gently(&nongit_ok);
343

344
			commit_pager_choice();
345

346
			child.use_shell = 1;
347
			child.trace2_child_class = "shell_alias";
348 349
			argv_array_push(&child.args, alias_string + 1);
			argv_array_pushv(&child.args, (*argv) + 1);
350

351 352 353 354
			trace2_cmd_alias(alias_command, child.args.argv);
			trace2_cmd_list_config();
			trace2_cmd_name("_run_shell_alias_");

355
			ret = run_command(&child);
356 357 358
			if (ret >= 0)   /* normal exit */
				exit(ret);

359 360
			die_errno(_("while expanding alias '%s': '%s'"),
				  alias_command, alias_string + 1);
361
		}
362
		count = split_cmdline(alias_string, &new_argv);
363
		if (count < 0)
364
			die(_("bad alias.%s string: %s"), alias_command,
365
			    _(split_cmdline_strerror(count)));
366 367
		option_count = handle_options(&new_argv, &count, &envchanged);
		if (envchanged)
368 369 370
			die(_("alias '%s' changes environment variables.\n"
			      "You can use '!git' in the alias to do this"),
			    alias_command);
371 372 373 374 375
		memmove(new_argv - option_count, new_argv,
				count * sizeof(char *));
		new_argv -= option_count;

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

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

381
		trace_argv_printf(new_argv,
382 383
				  "trace: alias expansion: %s =>",
				  alias_command);
384

385
		REALLOC_ARRAY(new_argv, count + *argcp);
386
		/* insert after command name */
387
		memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
388

389 390 391
		trace2_cmd_alias(alias_command, new_argv);
		trace2_cmd_list_config();

392 393
		*argv = new_argv;
		*argcp += count - 1;
394

395
		ret = 1;
396 397
	}

398 399
	errno = saved_errno;

400 401 402
	return ret;
}

403
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
404
{
405
	int status, help;
406
	struct stat st;
407 408 409
	const char *prefix;

	prefix = NULL;
410 411 412 413
	help = argc == 2 && !strcmp(argv[1], "-h");
	if (!help) {
		if (p->option & RUN_SETUP)
			prefix = setup_git_directory();
414
		else if (p->option & RUN_SETUP_GENTLY) {
415 416 417
			int nongit_ok;
			prefix = setup_git_directory_gently(&nongit_ok);
		}
418

419 420
		if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) &&
		    !(p->option & DELAY_PAGER_CONFIG))
421 422 423
			use_pager = check_pager_config(p->cmd);
		if (use_pager == -1 && p->option & USE_PAGER)
			use_pager = 1;
424 425 426 427

		if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) &&
		    startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */
			trace_repo_setup(prefix);
428
	}
429 430
	commit_pager_choice();

431 432
	if (!help && get_super_prefix()) {
		if (!(p->option & SUPPORT_SUPER_PREFIX))
433
			die(_("%s doesn't support --super-prefix"), p->cmd);
434 435
	}

436
	if (!help && p->option & NEED_WORK_TREE)
437 438
		setup_work_tree();

439
	trace_argv_printf(argv, "trace: built-in: git");
440 441
	trace2_cmd_name(p->cmd);
	trace2_cmd_list_config();
442

443
	validate_cache_entries(the_repository->index);
444
	status = p->fn(argc, argv, prefix);
445
	validate_cache_entries(the_repository->index);
446

447
	if (status)
448
		return status;
449 450 451 452 453 454 455 456 457

	/* 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.. */
458
	if (fflush(stdout))
459
		die_errno(_("write failure on standard output"));
460
	if (ferror(stdout))
461
		die(_("unknown write failure on standard output"));
462
	if (fclose(stdout))
463
		die_errno(_("close failed on standard output"));
464
	return 0;
465 466
}

467 468
static struct cmd_struct commands[] = {
	{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
469
	{ "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
470
	{ "annotate", cmd_annotate, RUN_SETUP | NO_PARSEOPT },
471
	{ "apply", cmd_apply, RUN_SETUP_GENTLY },
472
	{ "archive", cmd_archive, RUN_SETUP_GENTLY },
473 474
	{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },
	{ "blame", cmd_blame, RUN_SETUP },
475
	{ "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
476
	{ "bundle", cmd_bundle, RUN_SETUP_GENTLY | NO_PARSEOPT },
477 478 479 480
	{ "cat-file", cmd_cat_file, RUN_SETUP },
	{ "check-attr", cmd_check_attr, RUN_SETUP },
	{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
	{ "check-mailmap", cmd_check_mailmap, RUN_SETUP },
481
	{ "check-ref-format", cmd_check_ref_format, NO_PARSEOPT  },
482
	{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
483 484 485 486 487
	{ "checkout-index", cmd_checkout_index,
		RUN_SETUP | NEED_WORK_TREE},
	{ "cherry", cmd_cherry, RUN_SETUP },
	{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
	{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
488
	{ "clone", cmd_clone },
489 490
	{ "column", cmd_column, RUN_SETUP_GENTLY },
	{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
491
	{ "commit-graph", cmd_commit_graph, RUN_SETUP },
492
	{ "commit-tree", cmd_commit_tree, RUN_SETUP | NO_PARSEOPT },
493
	{ "config", cmd_config, RUN_SETUP_GENTLY | DELAY_PAGER_CONFIG },
494
	{ "count-objects", cmd_count_objects, RUN_SETUP },
495
	{ "credential", cmd_credential, RUN_SETUP_GENTLY | NO_PARSEOPT },
496
	{ "describe", cmd_describe, RUN_SETUP },
497 498 499 500
	{ "diff", cmd_diff, NO_PARSEOPT },
	{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
	{ "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
501
	{ "difftool", cmd_difftool, RUN_SETUP_GENTLY },
502 503
	{ "fast-export", cmd_fast_export, RUN_SETUP },
	{ "fetch", cmd_fetch, RUN_SETUP },
504
	{ "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT },
505 506 507 508 509 510
	{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
	{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
	{ "format-patch", cmd_format_patch, RUN_SETUP },
	{ "fsck", cmd_fsck, RUN_SETUP },
	{ "fsck-objects", cmd_fsck, RUN_SETUP },
	{ "gc", cmd_gc, RUN_SETUP },
511
	{ "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
512
	{ "grep", cmd_grep, RUN_SETUP_GENTLY },
513 514
	{ "hash-object", cmd_hash_object },
	{ "help", cmd_help },
515
	{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
516 517
	{ "init", cmd_init_db },
	{ "init-db", cmd_init_db },
518
	{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
519
	{ "log", cmd_log, RUN_SETUP },
520
	{ "ls-files", cmd_ls_files, RUN_SETUP },
521 522
	{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
	{ "ls-tree", cmd_ls_tree, RUN_SETUP },
523 524
	{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "mailsplit", cmd_mailsplit, NO_PARSEOPT },
525 526 527
	{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
	{ "merge-base", cmd_merge_base, RUN_SETUP },
	{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
528 529 530 531 532 533 534 535
	{ "merge-index", cmd_merge_index, RUN_SETUP | NO_PARSEOPT },
	{ "merge-ours", cmd_merge_ours, RUN_SETUP | NO_PARSEOPT },
	{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-tree", cmd_merge_tree, RUN_SETUP | NO_PARSEOPT },
	{ "mktag", cmd_mktag, RUN_SETUP | NO_PARSEOPT },
536
	{ "mktree", cmd_mktree, RUN_SETUP },
537
	{ "multi-pack-index", cmd_multi_pack_index, RUN_SETUP_GENTLY },
538 539 540 541
	{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
	{ "name-rev", cmd_name_rev, RUN_SETUP },
	{ "notes", cmd_notes, RUN_SETUP },
	{ "pack-objects", cmd_pack_objects, RUN_SETUP },
542
	{ "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },
543
	{ "pack-refs", cmd_pack_refs, RUN_SETUP },
544
	{ "patch-id", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },
545 546 547
	{ "pickaxe", cmd_blame, RUN_SETUP },
	{ "prune", cmd_prune, RUN_SETUP },
	{ "prune-packed", cmd_prune_packed, RUN_SETUP },
548
	{ "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
549
	{ "push", cmd_push, RUN_SETUP },
550
	{ "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
551
	{ "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
552 553 554 555 556 557
	/*
	 * NEEDSWORK: Until the rebase is independent and needs no redirection
	 * to rebase shell script this is kept as is, then should be changed to
	 * RUN_SETUP | NEED_WORK_TREE
	 */
	{ "rebase", cmd_rebase },
558
	{ "rebase--interactive", cmd_rebase__interactive, RUN_SETUP | NEED_WORK_TREE },
559 560 561
	{ "receive-pack", cmd_receive_pack },
	{ "reflog", cmd_reflog, RUN_SETUP },
	{ "remote", cmd_remote, RUN_SETUP },
562 563
	{ "remote-ext", cmd_remote_ext, NO_PARSEOPT },
	{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
564 565 566 567
	{ "repack", cmd_repack, RUN_SETUP },
	{ "replace", cmd_replace, RUN_SETUP },
	{ "rerere", cmd_rerere, RUN_SETUP },
	{ "reset", cmd_reset, RUN_SETUP },
568 569
	{ "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
	{ "rev-parse", cmd_rev_parse, NO_PARSEOPT },
570 571 572
	{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
	{ "rm", cmd_rm, RUN_SETUP },
	{ "send-pack", cmd_send_pack, RUN_SETUP },
573
	{ "serve", cmd_serve, RUN_SETUP },
574 575 576
	{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
	{ "show", cmd_show, RUN_SETUP },
	{ "show-branch", cmd_show_branch, RUN_SETUP },
Jeff King's avatar
Jeff King committed
577
	{ "show-index", cmd_show_index },
578 579
	{ "show-ref", cmd_show_ref, RUN_SETUP },
	{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
580 581 582 583 584 585
	/*
	 * NEEDSWORK: Until the builtin stash is thoroughly robust and no
	 * longer needs redirection to the stash shell script this is kept as
	 * is, then should be changed to RUN_SETUP | NEED_WORK_TREE
	 */
	{ "stash", cmd_stash },
586 587
	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
	{ "stripspace", cmd_stripspace },
588
	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
589
	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
590
	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
591 592
	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
	{ "unpack-objects", cmd_unpack_objects, RUN_SETUP | NO_PARSEOPT },
593 594 595
	{ "update-index", cmd_update_index, RUN_SETUP },
	{ "update-ref", cmd_update_ref, RUN_SETUP },
	{ "update-server-info", cmd_update_server_info, RUN_SETUP },
596 597
	{ "upload-archive", cmd_upload_archive, NO_PARSEOPT },
	{ "upload-archive--writer", cmd_upload_archive_writer, NO_PARSEOPT },
598
	{ "upload-pack", cmd_upload_pack },
599
	{ "var", cmd_var, RUN_SETUP_GENTLY | NO_PARSEOPT },
600
	{ "verify-commit", cmd_verify_commit, RUN_SETUP },
601 602 603 604
	{ "verify-pack", cmd_verify_pack },
	{ "verify-tag", cmd_verify_tag, RUN_SETUP },
	{ "version", cmd_version },
	{ "whatchanged", cmd_whatchanged, RUN_SETUP },
605
	{ "worktree", cmd_worktree, RUN_SETUP | NO_PARSEOPT },
606 607 608
	{ "write-tree", cmd_write_tree, RUN_SETUP },
};

609
static struct cmd_struct *get_builtin(const char *s)
610 611 612
{
	int i;
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
613
		struct cmd_struct *p = commands + i;
614
		if (!strcmp(s, p->cmd))
615
			return p;
616
	}
617 618 619 620 621 622
	return NULL;
}

int is_builtin(const char *s)
{
	return !!get_builtin(s);
623 624
}

625
static void list_builtins(struct string_list *out, unsigned int exclude_option)
626 627
{
	int i;
628 629 630 631
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		if (exclude_option &&
		    (commands[i].option & exclude_option))
			continue;
632
		string_list_append(out, commands[i].cmd);
633
	}
634 635
}

636 637 638 639 640 641 642 643 644 645 646 647
#ifdef STRIP_EXTENSION
static void strip_extension(const char **argv)
{
	size_t len;

	if (strip_suffix(argv[0], STRIP_EXTENSION, &len))
		argv[0] = xmemdupz(argv[0], len);
}
#else
#define strip_extension(cmd)
#endif

648
static void handle_builtin(int argc, const char **argv)
649
{
650
	struct argv_array args = ARGV_ARRAY_INIT;
651
	const char *cmd;
652
	struct cmd_struct *builtin;
653

654 655
	strip_extension(argv);
	cmd = argv[0];
656

657
	/* Turn "git cmd --help" into "git help --exclude-guides cmd" */
658
	if (argc > 1 && !strcmp(argv[1], "--help")) {
659 660
		int i;

661 662
		argv[1] = argv[0];
		argv[0] = cmd = "help";
663 664 665 666 667 668 669 670 671

		for (i = 0; i < argc; i++) {
			argv_array_push(&args, argv[i]);
			if (!i)
				argv_array_push(&args, "--exclude-guides");
		}

		argc++;
		argv = args.argv;
672 673
	}

674
	builtin = get_builtin(cmd);
675 676
	if (builtin)
		exit(run_builtin(builtin, argc, argv));
677
	argv_array_clear(&args);
678 679
}

680 681
static void execv_dashed_external(const char **argv)
{
682
	struct child_process cmd = CHILD_PROCESS_INIT;
683
	int status;
684

685
	if (get_super_prefix())
686
		die(_("%s doesn't support --super-prefix"), argv[0]);
687

688
	if (use_pager == -1 && !is_builtin(argv[0]))
689
		use_pager = check_pager_config(argv[0]);
690 691
	commit_pager_choice();

692 693 694
	argv_array_pushf(&cmd.args, "git-%s", argv[0]);
	argv_array_pushv(&cmd.args, argv + 1);
	cmd.clean_on_exit = 1;
695
	cmd.wait_after_clean = 1;
696
	cmd.silent_exec_failure = 1;
697
	cmd.trace2_child_class = "dashed";
698

699 700 701 702 703 704
	trace2_cmd_name("_run_dashed_");

	/*
	 * The code in run_command() logs trace2 child_start/child_exit
	 * events, so we do not need to report exec/exec_result events here.
	 */
705
	trace_argv_printf(cmd.args.argv, "trace: exec:");
706

707
	/*
708 709 710 711
	 * If we fail because the command is not found, it is
	 * OK to return. Otherwise, we just pass along the status code,
	 * or our usual generic code if we were not even able to exec
	 * the program.
712
	 */
713
	status = run_command(&cmd);
714 715 716 717 718 719

	/*
	 * If the child process ran and we are now going to exit, emit a
	 * generic string as our trace2 command verb to indicate that we
	 * launched a dashed command.
	 */
720
	if (status >= 0)
721
		exit(status);
722 723
	else if (errno != ENOENT)
		exit(128);
724 725
}

726 727 728
static int run_argv(int *argcp, const char ***argv)
{
	int done_alias = 0;
729
	struct string_list cmd_list = STRING_LIST_INIT_NODUP;
730
	struct string_list_item *seen;
731 732

	while (1) {
733 734 735 736 737 738 739 740 741 742 743
		/*
		 * If we tried alias and futzed with our environment,
		 * it no longer is safe to invoke builtins directly in
		 * general.  We have to spawn them as dashed externals.
		 *
		 * NEEDSWORK: if we can figure out cases
		 * where it is safe to do, we can avoid spawning a new
		 * process.
		 */
		if (!done_alias)
			handle_builtin(*argcp, *argv);
744

745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
#if 0 // TODO In GFW, need to amend a7924b655e940b06cb547c235d6bed9767929673 to include trace2_ and _tr2 lines.
		else if (get_builtin(**argv)) {
			struct argv_array args = ARGV_ARRAY_INIT;
			int i;

			/*
			 * The current process is committed to launching a
			 * child process to run the command named in (**argv)
			 * and exiting.  Log a generic string as the trace2
			 * command verb to indicate this.  Note that the child
			 * process will log the actual verb when it runs.
			 */
			trace2_cmd_name("_run_git_alias_");

			if (get_super_prefix())
				die("%s doesn't support --super-prefix", **argv);

			commit_pager_choice();

			argv_array_push(&args, "git");
			for (i = 0; i < *argcp; i++)
				argv_array_push(&args, (*argv)[i]);

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

			/*
			 * if we fail because the command is not found, it is
			 * OK to return. Otherwise, we just pass along the status code.
			 */
			i = run_command_v_opt_tr2(args.argv, RUN_SILENT_EXEC_FAILURE |
						  RUN_CLEAN_ON_EXIT, "git_alias");
			if (i >= 0 || errno != ENOENT)
				exit(i);
			die("could not execute builtin %s", **argv);
		}
#endif // a7924b655e940b06cb547c235d6bed9767929673

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

785 786 787 788 789 790 791 792 793 794 795 796 797
		seen = unsorted_string_list_lookup(&cmd_list, *argv[0]);
		if (seen) {
			int i;
			struct strbuf sb = STRBUF_INIT;
			for (i = 0; i < cmd_list.nr; i++) {
				struct string_list_item *item = &cmd_list.items[i];

				strbuf_addf(&sb, "\n  %s", item->string);
				if (item == seen)
					strbuf_addstr(&sb, " <==");
				else if (i == cmd_list.nr - 1)
					strbuf_addstr(&sb, " ==>");
			}
798
			die(_("alias loop detected: expansion of '%s' does"
799
			      " not terminate:%s"), cmd_list.items[0].string, sb.buf);
800 801 802 803 804 805
		}

		string_list_append(&cmd_list, *argv[0]);

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

814 815
	string_list_clear(&cmd_list, 0);

816 817 818
	return done_alias;
}

819
int cmd_main(int argc, const char **argv)
820
{
821
	const char *cmd;
822
	int done_help = 0;
823

824
	cmd = argv[0];
825
	if (!cmd)
826
		cmd = "git-help";
827 828 829 830 831
	else {
		const char *slash = find_last_dir_sep(cmd);
		if (slash)
			cmd = slash + 1;
	}
832

833 834
	trace_command_performance(argv);

835 836 837 838 839 840 841
	/*
	 * "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)
	 *
842 843
	 * So we just directly call the builtin handler, and die if
	 * that one cannot handle it.
844
	 */
845
	if (skip_prefix(cmd, "git-", &cmd)) {
846
		argv[0] = cmd;
847
		handle_builtin(argc, argv);
848
		die(_("cannot handle %s as a builtin"), cmd);
849
	}
850

851
	/* Look for flags.. */
852 853
	argv++;
	argc--;
854
	handle_options(&argv, &argc, NULL);
855
	if (argc > 0) {
856 857
		/* translate --help and --version into commands */
		skip_prefix(argv[0], "--", &argv[0]);
858
	} else {
859
		/* The user didn't specify a command; give them help */
860
		commit_pager_choice();
861
		printf(_("usage: %s\n\n"), git_usage_string);
862
		list_common_cmds_help();
863
		printf("\n%s\n", _(git_more_info_string));
864
		exit(1);
865
	}
866
	cmd = argv[0];
867 868

	/*
869
	 * We use PATH to find git commands, but we prepend some higher
870
	 * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
871 872
	 * environment, and the $(gitexecdir) from the Makefile at build
	 * time.
873
	 */
874
	setup_path();
875

876
	while (1) {
877
		int was_alias = run_argv(&argc, &argv);
878
		if (errno != ENOENT)
879
			break;
880
		if (was_alias) {
881 882
			fprintf(stderr, _("expansion of alias '%s' failed; "
					  "'%s' is not a git command\n"),
883 884 885
				cmd, argv[0]);
			exit(1);
		}
886 887 888 889 890
		if (!done_help) {
			cmd = argv[0] = help_unknown_cmd(cmd);
			done_help = 1;
		} else
			break;
891
	}
892

893
	fprintf(stderr, _("failed to run command '%s': %s\n"),
894
		cmd, strerror(errno));
895 896 897

	return 1;
}