git.c 13.7 KB
Newer Older
1
#include "builtin.h"
2
#include "exec_cmd.h"
3
#include "cache.h"
4
#include "quote.h"
5

6
const char git_usage_string[] =
7
	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
8

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

J
Jeff King 已提交
12 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
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;
	}
}

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

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

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

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

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

140
	subdir = setup_git_directory_gently(&unused_nongit);
141 142

	alias_command = (*argv)[0];
143
	alias_string = alias_lookup(alias_command);
144
	if (alias_string) {
145
		if (alias_string[0] == '!') {
146
			if (*argcp > 1) {
147
				struct strbuf buf;
148

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

180
		trace_argv_printf(new_argv,
181 182
				  "trace: alias expansion: %s =>",
				  alias_command);
183

J
Jonas Fonseca 已提交
184 185
		new_argv = xrealloc(new_argv, sizeof(char*) *
				    (count + *argcp + 1));
186 187 188
		/* insert after command name */
		memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);
		new_argv[count+*argcp] = NULL;
189

190 191
		*argv = new_argv;
		*argcp += count - 1;
192

193
		ret = 1;
194 195 196 197 198
	}

	if (subdir)
		chdir(subdir);

199 200
	errno = saved_errno;

201 202 203
	return ret;
}

204
const char git_version_string[] = GIT_VERSION;
J
Junio C Hamano 已提交
205

206 207
#define RUN_SETUP	(1<<0)
#define USE_PAGER	(1<<1)
208 209 210 211
/*
 * require working tree to be present -- anything uses this needs
 * RUN_SETUP for reading from the configuration file.
 */
212
#define NEED_WORK_TREE	(1<<2)
213

214 215 216 217 218 219 220 221
struct cmd_struct {
	const char *cmd;
	int (*fn)(int, const char **, const char *);
	int option;
};

static int run_command(struct cmd_struct *p, int argc, const char **argv)
{
222 223
	int status;
	struct stat st;
224 225 226 227 228
	const char *prefix;

	prefix = NULL;
	if (p->option & RUN_SETUP)
		prefix = setup_git_directory();
J
Jeff King 已提交
229 230 231 232 233 234 235

	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 已提交
236 237 238
	if (p->option & NEED_WORK_TREE)
		setup_work_tree();

239
	trace_argv_printf(argv, "trace: built-in: git");
240

241 242
	status = p->fn(argc, argv, prefix);
	if (status)
243
		return status & 0xff;
244 245 246 247 248 249 250 251 252

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

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

	if (sizeof(ext) > 1) {
		i = strlen(argv[0]) - strlen(ext);
		if (i > 0 && !strcmp(argv[0] + i, ext)) {
			char *argv0 = strdup(argv[0]);
			argv[0] = cmd = argv0;
			argv0[i] = '\0';
		}
	}
372

L
Linus Torvalds 已提交
373 374 375 376 377 378
	/* Turn "git cmd --help" into "git help cmd" */
	if (argc > 1 && !strcmp(argv[1], "--help")) {
		argv[1] = argv[0];
		argv[0] = cmd = "help";
	}

379 380 381 382
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		struct cmd_struct *p = commands+i;
		if (strcmp(p->cmd, cmd))
			continue;
383
		exit(run_command(p, argc, argv));
384 385 386
	}
}

387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
static void execv_dashed_external(const char **argv)
{
	struct strbuf cmd;
	const char *tmp;

	strbuf_init(&cmd, 0);
	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:");

	/* execvp() can only ever return if it fails */
	execvp(cmd.buf, (char **)argv);

	trace_printf("trace: exec failed: %s\n", strerror(errno));

	argv[0] = tmp;

	strbuf_release(&cmd);
}


417
int main(int argc, const char **argv)
418
{
419 420
	const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
	char *slash = (char *)cmd + strlen(cmd);
J
Junio C Hamano 已提交
421
	int done_alias = 0;
422 423 424 425

	/*
	 * Take the basename of argv[0] as the command
	 * name, and the dirname as the default exec_path
426
	 * if we don't have anything better.
427
	 */
428 429 430 431
	do
		--slash;
	while (cmd <= slash && !is_dir_sep(*slash));
	if (cmd <= slash) {
432
		*slash++ = 0;
433
		git_set_argv0_path(cmd);
434 435
		cmd = slash;
	}
436

437 438 439 440 441 442 443 444 445 446
	/*
	 * "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.
	 */
447
	if (!prefixcmp(cmd, "git-")) {
448 449
		cmd += 4;
		argv[0] = cmd;
450
		handle_internal_command(argc, argv);
451 452
		die("cannot handle %s internally", cmd);
	}
453

454
	/* Look for flags.. */
455 456
	argv++;
	argc--;
457
	handle_options(&argv, &argc, NULL);
J
Jeff King 已提交
458
	commit_pager_choice();
459
	if (argc > 0) {
460
		if (!prefixcmp(argv[0], "--"))
461 462
			argv[0] += 2;
	} else {
463 464 465
		/* The user didn't specify a command; give them help */
		printf("usage: %s\n\n", git_usage_string);
		list_common_cmds_help();
466
		printf("\n%s\n", git_more_info_string);
467
		exit(1);
468
	}
469
	cmd = argv[0];
470 471

	/*
472 473 474 475
	 * We use PATH to find git commands, but we prepend some higher
	 * precidence paths: the "--exec-path" option, the GIT_EXEC_PATH
	 * environment, and the $(gitexecdir) from the Makefile at build
	 * time.
476
	 */
477
	setup_path();
478

J
Junio C Hamano 已提交
479 480
	while (1) {
		/* See if it's an internal command */
481
		handle_internal_command(argc, argv);
482

J
Junio C Hamano 已提交
483
		/* .. then try the external ones */
484
		execv_dashed_external(argv);
485

J
Junio C Hamano 已提交
486 487 488 489 490 491 492 493
		/* 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(&argc, &argv))
			break;
		done_alias = 1;
	}
494

495 496 497 498 499 500 501
	if (errno == ENOENT) {
		if (done_alias) {
			fprintf(stderr, "Expansion of alias '%s' failed; "
				"'%s' is not a git-command\n",
				cmd, argv[0]);
			exit(1);
		}
502 503 504
		argv[0] = help_unknown_cmd(cmd);
		handle_internal_command(argc, argv);
		execv_dashed_external(argv);
505
	}
506 507

	fprintf(stderr, "Failed to run command '%s': %s\n",
508
		cmd, strerror(errno));
509 510 511

	return 1;
}