git.c 14.3 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 *) *
J
Jonas Fonseca 已提交
191
				    (count + *argcp + 1));
192
		/* insert after command name */
193
		memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
194
		new_argv[count+*argcp] = NULL;
195

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

199
		ret = 1;
200 201
	}

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

205 206
	errno = saved_errno;

207 208 209
	return ret;
}

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

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

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

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

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

	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 已提交
242 243 244
	if (p->option & NEED_WORK_TREE)
		setup_work_tree();

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

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

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

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

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

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

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

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

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

416 417 418 419 420 421 422 423 424 425 426
	/*
	 * 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 */
427 428 429 430 431 432

	argv[0] = tmp;

	strbuf_release(&cmd);
}

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

456

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

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

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

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

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

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

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

	return 1;
}