git.c 14.2 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[] =
8
	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
9

10 11 12
const char git_more_info_string[] =
	"See 'git help COMMAND' for more information on a specific command.";

J
Jeff King 已提交
13 14 15 16 17 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
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;
	}
}

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

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

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

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

134 135
static int handle_alias(int *argcp, const char ***argv)
{
136
	int envchanged = 0, ret = 0, saved_errno = errno;
137
	const char *subdir;
138
	int count, option_count;
139
	const char **new_argv;
140 141
	const char *alias_command;
	char *alias_string;
142
	int unused_nongit;
143

144
	subdir = setup_git_directory_gently(&unused_nongit);
145 146

	alias_command = (*argv)[0];
147
	alias_string = alias_lookup(alias_command);
148
	if (alias_string) {
149
		if (alias_string[0] == '!') {
150
			if (*argcp > 1) {
151
				struct strbuf buf;
152

153 154
				strbuf_init(&buf, PATH_MAX);
				strbuf_addstr(&buf, alias_string);
155
				sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
156
				free(alias_string);
157
				alias_string = buf.buf;
158
			}
159 160 161 162 163 164
			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));
165
			die("Failed to run '%s' when expanding alias '%s'",
166 167
			    alias_string + 1, alias_command);
		}
168
		count = split_cmdline(alias_string, &new_argv);
169 170
		if (count < 0)
			die("Bad alias.%s string", alias_command);
171 172 173 174 175
		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);
176 177 178 179 180 181 182 183 184 185
		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);

186
		trace_argv_printf(new_argv,
187 188
				  "trace: alias expansion: %s =>",
				  alias_command);
189

190
		new_argv = xrealloc(new_argv, sizeof(char *) *
191
				    (count + *argcp));
192
		/* insert after command name */
193
		memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
194

195 196
		*argv = new_argv;
		*argcp += count - 1;
197

198
		ret = 1;
199 200
	}

A
Alex Riesen 已提交
201 202
	if (subdir && chdir(subdir))
		die("Cannot change to %s: %s", subdir, strerror(errno));
203

204 205
	errno = saved_errno;

206 207 208
	return ret;
}

209
const char git_version_string[] = GIT_VERSION;
J
Junio C Hamano 已提交
210

211 212
#define RUN_SETUP	(1<<0)
#define USE_PAGER	(1<<1)
213 214 215 216
/*
 * require working tree to be present -- anything uses this needs
 * RUN_SETUP for reading from the configuration file.
 */
217
#define NEED_WORK_TREE	(1<<2)
218

219 220 221 222 223 224
struct cmd_struct {
	const char *cmd;
	int (*fn)(int, const char **, const char *);
	int option;
};

J
Jeff King 已提交
225
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
226
{
227 228
	int status;
	struct stat st;
229 230 231 232 233
	const char *prefix;

	prefix = NULL;
	if (p->option & RUN_SETUP)
		prefix = setup_git_directory();
J
Jeff King 已提交
234 235 236 237 238 239 240

	if (use_pager == -1 && p->option & RUN_SETUP)
		use_pager = check_pager_config(p->cmd);
	if (use_pager == -1 && p->option & USE_PAGER)
		use_pager = 1;
	commit_pager_choice();

M
Mike Hommey 已提交
241 242 243
	if (p->option & NEED_WORK_TREE)
		setup_work_tree();

244
	trace_argv_printf(argv, "trace: built-in: git");
245

246 247
	status = p->fn(argc, argv, prefix);
	if (status)
248
		return status;
249 250 251 252 253 254 255 256 257

	/* 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.. */
258
	if (fflush(stdout))
259
		die("write failure on standard output: %s", strerror(errno));
260 261 262 263
	if (ferror(stdout))
		die("unknown write failure on standard output");
	if (fclose(stdout))
		die("close failed on standard output: %s", strerror(errno));
264
	return 0;
265 266 267
}

static void handle_internal_command(int argc, const char **argv)
268 269
{
	const char *cmd = argv[0];
270
	static struct cmd_struct commands[] = {
271
		{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
272
		{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
273
		{ "annotate", cmd_annotate, RUN_SETUP },
J
Junio C Hamano 已提交
274
		{ "apply", cmd_apply },
275
		{ "archive", cmd_archive },
276
		{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
277
		{ "blame", cmd_blame, RUN_SETUP },
J
Junio C Hamano 已提交
278
		{ "branch", cmd_branch, RUN_SETUP },
279
		{ "bundle", cmd_bundle },
280
		{ "cat-file", cmd_cat_file, RUN_SETUP },
D
Daniel Barkalow 已提交
281
		{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
282 283
		{ "checkout-index", cmd_checkout_index,
			RUN_SETUP | NEED_WORK_TREE},
P
Peter Eriksen 已提交
284
		{ "check-ref-format", cmd_check_ref_format },
285
		{ "check-attr", cmd_check_attr, RUN_SETUP },
R
Rene Scharfe 已提交
286
		{ "cherry", cmd_cherry, RUN_SETUP },
287
		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
D
Daniel Barkalow 已提交
288
		{ "clone", cmd_clone },
S
Shawn Bohrer 已提交
289
		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
K
Kristian Høgsberg 已提交
290
		{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
291
		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
292
		{ "config", cmd_config },
293
		{ "count-objects", cmd_count_objects, RUN_SETUP },
S
Shawn O. Pearce 已提交
294
		{ "describe", cmd_describe, RUN_SETUP },
295
		{ "diff", cmd_diff },
296
		{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
297 298
		{ "diff-index", cmd_diff_index, RUN_SETUP },
		{ "diff-tree", cmd_diff_tree, RUN_SETUP },
299
		{ "fast-export", cmd_fast_export, RUN_SETUP },
D
Daniel Barkalow 已提交
300
		{ "fetch", cmd_fetch, RUN_SETUP },
301
		{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
302
		{ "fetch--tool", cmd_fetch__tool, RUN_SETUP },
303
		{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
304
		{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
305
		{ "format-patch", cmd_format_patch, RUN_SETUP },
306 307
		{ "fsck", cmd_fsck, RUN_SETUP },
		{ "fsck-objects", cmd_fsck, RUN_SETUP },
J
James Bowes 已提交
308
		{ "gc", cmd_gc, RUN_SETUP },
J
Junio C Hamano 已提交
309
		{ "get-tar-commit-id", cmd_get_tar_commit_id },
J
Johannes Schindelin 已提交
310
		{ "grep", cmd_grep, RUN_SETUP | USE_PAGER },
J
Junio C Hamano 已提交
311
		{ "help", cmd_help },
D
Daniel Barkalow 已提交
312 313 314
#ifndef NO_CURL
		{ "http-fetch", cmd_http_fetch, RUN_SETUP },
#endif
315
		{ "init", cmd_init_db },
J
Junio C Hamano 已提交
316
		{ "init-db", cmd_init_db },
317 318 319
		{ "log", cmd_log, RUN_SETUP | USE_PAGER },
		{ "ls-files", cmd_ls_files, RUN_SETUP },
		{ "ls-tree", cmd_ls_tree, RUN_SETUP },
D
Daniel Barkalow 已提交
320
		{ "ls-remote", cmd_ls_remote },
J
Junio C Hamano 已提交
321 322
		{ "mailinfo", cmd_mailinfo },
		{ "mailsplit", cmd_mailsplit },
M
Miklos Vajna 已提交
323
		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
324
		{ "merge-base", cmd_merge_base, RUN_SETUP },
325
		{ "merge-file", cmd_merge_file },
326
		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
D
Daniel Barkalow 已提交
327
		{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
328
		{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
329
		{ "mktree", cmd_mktree, RUN_SETUP },
330
		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
331 332
		{ "name-rev", cmd_name_rev, RUN_SETUP },
		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
D
Daniel Barkalow 已提交
333
		{ "peek-remote", cmd_ls_remote },
334
		{ "pickaxe", cmd_blame, RUN_SETUP },
335 336
		{ "prune", cmd_prune, RUN_SETUP },
		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
337
		{ "push", cmd_push, RUN_SETUP },
338
		{ "read-tree", cmd_read_tree, RUN_SETUP },
J
Junio C Hamano 已提交
339
		{ "receive-pack", cmd_receive_pack },
J
Junio C Hamano 已提交
340
		{ "reflog", cmd_reflog, RUN_SETUP },
J
Johannes Schindelin 已提交
341
		{ "remote", cmd_remote, RUN_SETUP },
342
		{ "repo-config", cmd_config },
J
Johannes Schindelin 已提交
343
		{ "rerere", cmd_rerere, RUN_SETUP },
C
Carlos Rica 已提交
344
		{ "reset", cmd_reset, RUN_SETUP },
345
		{ "rev-list", cmd_rev_list, RUN_SETUP },
J
Junio C Hamano 已提交
346
		{ "rev-parse", cmd_rev_parse },
347
		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
348
		{ "rm", cmd_rm, RUN_SETUP },
349
		{ "send-pack", cmd_send_pack, RUN_SETUP },
350
		{ "shortlog", cmd_shortlog, USE_PAGER },
351 352
		{ "show-branch", cmd_show_branch, RUN_SETUP },
		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
353
		{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
354
		{ "stripspace", cmd_stripspace },
355
		{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
C
Carlos Rica 已提交
356
		{ "tag", cmd_tag, RUN_SETUP },
R
Rene Scharfe 已提交
357
		{ "tar-tree", cmd_tar_tree },
358 359 360
		{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
		{ "update-index", cmd_update_index, RUN_SETUP },
		{ "update-ref", cmd_update_ref, RUN_SETUP },
F
Franck Bui-Huu 已提交
361
		{ "upload-archive", cmd_upload_archive },
C
Carlos Rica 已提交
362
		{ "verify-tag", cmd_verify_tag, RUN_SETUP },
J
Junio C Hamano 已提交
363
		{ "version", cmd_version },
364 365
		{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
		{ "write-tree", cmd_write_tree, RUN_SETUP },
R
Rene Scharfe 已提交
366
		{ "verify-pack", cmd_verify_pack },
367
		{ "show-ref", cmd_show_ref, RUN_SETUP },
368
		{ "pack-refs", cmd_pack_refs, RUN_SETUP },
369 370
	};
	int i;
371 372 373 374 375
	static const char ext[] = STRIP_EXTENSION;

	if (sizeof(ext) > 1) {
		i = strlen(argv[0]) - strlen(ext);
		if (i > 0 && !strcmp(argv[0] + i, ext)) {
376
			char *argv0 = xstrdup(argv[0]);
377 378 379 380
			argv[0] = cmd = argv0;
			argv0[i] = '\0';
		}
	}
381

L
Linus Torvalds 已提交
382 383 384 385 386 387
	/* Turn "git cmd --help" into "git help cmd" */
	if (argc > 1 && !strcmp(argv[1], "--help")) {
		argv[1] = argv[0];
		argv[0] = cmd = "help";
	}

388 389 390 391
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		struct cmd_struct *p = commands+i;
		if (strcmp(p->cmd, cmd))
			continue;
J
Jeff King 已提交
392
		exit(run_builtin(p, argc, argv));
393 394 395
	}
}

396 397
static void execv_dashed_external(const char **argv)
{
398
	struct strbuf cmd = STRBUF_INIT;
399
	const char *tmp;
400
	int status;
401 402 403 404 405 406 407 408 409 410 411 412 413 414

	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:");

415 416 417 418 419 420 421 422 423 424 425
	/*
	 * if we fail because the command is not found, it is
	 * OK to return. Otherwise, we just pass along the status code.
	 */
	status = run_command_v_opt(argv, 0);
	if (status != -ERR_RUN_COMMAND_EXEC) {
		if (IS_RUN_COMMAND_ERR(status))
			die("unable to run '%s'", argv[0]);
		exit(-status);
	}
	errno = ENOENT; /* as if we called execvp */
426 427 428 429 430 431

	argv[0] = tmp;

	strbuf_release(&cmd);
}

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
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;
}

455

456
int main(int argc, const char **argv)
457
{
458
	const char *cmd;
459

460 461
	cmd = git_extract_argv0_path(argv[0]);
	if (!cmd)
462
		cmd = "git-help";
463

464 465 466 467 468 469 470 471 472 473
	/*
	 * "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.
	 */
474
	if (!prefixcmp(cmd, "git-")) {
475 476
		cmd += 4;
		argv[0] = cmd;
477
		handle_internal_command(argc, argv);
478 479
		die("cannot handle %s internally", cmd);
	}
480

481
	/* Look for flags.. */
482 483
	argv++;
	argc--;
484
	handle_options(&argv, &argc, NULL);
J
Jeff King 已提交
485
	commit_pager_choice();
486
	if (argc > 0) {
487
		if (!prefixcmp(argv[0], "--"))
488 489
			argv[0] += 2;
	} else {
490 491 492
		/* The user didn't specify a command; give them help */
		printf("usage: %s\n\n", git_usage_string);
		list_common_cmds_help();
493
		printf("\n%s\n", git_more_info_string);
494
		exit(1);
495
	}
496
	cmd = argv[0];
497 498

	/*
499
	 * We use PATH to find git commands, but we prepend some higher
M
Mike Ralphson 已提交
500
	 * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
501 502
	 * environment, and the $(gitexecdir) from the Makefile at build
	 * time.
503
	 */
504
	setup_path();
505

J
Junio C Hamano 已提交
506
	while (1) {
507 508 509 510
		static int done_help = 0;
		static int was_alias = 0;
		was_alias = run_argv(&argc, &argv);
		if (errno != ENOENT)
J
Junio C Hamano 已提交
511
			break;
512
		if (was_alias) {
513 514 515 516 517
			fprintf(stderr, "Expansion of alias '%s' failed; "
				"'%s' is not a git-command\n",
				cmd, argv[0]);
			exit(1);
		}
518 519 520 521 522
		if (!done_help) {
			cmd = argv[0] = help_unknown_cmd(cmd);
			done_help = 1;
		} else
			break;
523
	}
524 525

	fprintf(stderr, "Failed to run command '%s': %s\n",
526
		cmd, strerror(errno));
527 528 529

	return 1;
}