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

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

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

141
	subdir = setup_git_directory_gently(&unused_nongit);
142 143

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

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

183
		trace_argv_printf(new_argv,
184 185
				  "trace: alias expansion: %s =>",
				  alias_command);
186

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

193 194
		*argv = new_argv;
		*argcp += count - 1;
195

196
		ret = 1;
197 198
	}

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

202 203
	errno = saved_errno;

204 205 206
	return ret;
}

207
const char git_version_string[] = GIT_VERSION;
J
Junio C Hamano 已提交
208

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

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

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

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

	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 已提交
239 240 241
	if (p->option & NEED_WORK_TREE)
		setup_work_tree();

242
	trace_argv_printf(argv, "trace: built-in: git");
243

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

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

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

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

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

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

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

411 412 413 414 415 416 417 418 419 420 421
	/*
	 * 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 */
422 423 424 425 426 427

	argv[0] = tmp;

	strbuf_release(&cmd);
}

428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
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;
}

451

452
int main(int argc, const char **argv)
453
{
454
	const char *cmd;
455

456 457
	cmd = git_extract_argv0_path(argv[0]);
	if (!cmd)
458
		cmd = "git-help";
459

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

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

	/*
495 496 497 498
	 * 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.
499
	 */
500
	setup_path();
501

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

	fprintf(stderr, "Failed to run command '%s': %s\n",
522
		cmd, strerror(errno));
523 524 525

	return 1;
}