git.c 14.7 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[] =
M
Matthieu Moy 已提交
8
	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
9
	"           [-p|--paginate|--no-pager] [--no-replace-objects]\n"
M
Matthieu Moy 已提交
10 11
	"           [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
	"           [--help] COMMAND [ARGS]";
12

13 14 15
const char git_more_info_string[] =
	"See 'git help COMMAND' for more information on a specific command.";

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

53
static int handle_options(const char ***argv, int *argc, int *envchanged)
54 55 56 57 58 59 60 61
{
	int handled = 0;

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

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

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

142 143
static int handle_alias(int *argcp, const char ***argv)
{
144
	int envchanged = 0, ret = 0, saved_errno = errno;
145
	const char *subdir;
146
	int count, option_count;
147
	const char **new_argv;
148 149
	const char *alias_command;
	char *alias_string;
150
	int unused_nongit;
151

152
	subdir = setup_git_directory_gently(&unused_nongit);
153 154

	alias_command = (*argv)[0];
155
	alias_string = alias_lookup(alias_command);
156
	if (alias_string) {
157
		if (alias_string[0] == '!') {
158
			if (*argcp > 1) {
159
				struct strbuf buf;
160

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

194
		trace_argv_printf(new_argv,
195 196
				  "trace: alias expansion: %s =>",
				  alias_command);
197

198
		new_argv = xrealloc(new_argv, sizeof(char *) *
199
				    (count + *argcp));
200
		/* insert after command name */
201
		memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
202

203 204
		*argv = new_argv;
		*argcp += count - 1;
205

206
		ret = 1;
207 208
	}

A
Alex Riesen 已提交
209
	if (subdir && chdir(subdir))
210
		die_errno("Cannot change to '%s'", subdir);
211

212 213
	errno = saved_errno;

214 215 216
	return ret;
}

217
const char git_version_string[] = GIT_VERSION;
J
Junio C Hamano 已提交
218

219 220
#define RUN_SETUP	(1<<0)
#define USE_PAGER	(1<<1)
221 222 223 224
/*
 * require working tree to be present -- anything uses this needs
 * RUN_SETUP for reading from the configuration file.
 */
225
#define NEED_WORK_TREE	(1<<2)
226

227 228 229 230 231 232
struct cmd_struct {
	const char *cmd;
	int (*fn)(int, const char **, const char *);
	int option;
};

J
Jeff King 已提交
233
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
234
{
235
	int status, help;
236
	struct stat st;
237 238 239
	const char *prefix;

	prefix = NULL;
240 241 242 243 244 245 246 247 248 249
	help = argc == 2 && !strcmp(argv[1], "-h");
	if (!help) {
		if (p->option & RUN_SETUP)
			prefix = setup_git_directory();

		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;
	}
J
Jeff King 已提交
250 251
	commit_pager_choice();

252
	if (!help && p->option & NEED_WORK_TREE)
M
Mike Hommey 已提交
253 254
		setup_work_tree();

255
	trace_argv_printf(argv, "trace: built-in: git");
256

257 258
	status = p->fn(argc, argv, prefix);
	if (status)
259
		return status;
260 261 262 263 264 265 266 267 268

	/* 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.. */
269
	if (fflush(stdout))
270
		die_errno("write failure on standard output");
271 272 273
	if (ferror(stdout))
		die("unknown write failure on standard output");
	if (fclose(stdout))
274
		die_errno("close failed on standard output");
275
	return 0;
276 277 278
}

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

	if (sizeof(ext) > 1) {
		i = strlen(argv[0]) - strlen(ext);
		if (i > 0 && !strcmp(argv[0] + i, ext)) {
392
			char *argv0 = xstrdup(argv[0]);
393 394 395 396
			argv[0] = cmd = argv0;
			argv0[i] = '\0';
		}
	}
397

L
Linus Torvalds 已提交
398 399 400 401 402 403
	/* Turn "git cmd --help" into "git help cmd" */
	if (argc > 1 && !strcmp(argv[1], "--help")) {
		argv[1] = argv[0];
		argv[0] = cmd = "help";
	}

404 405 406 407
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		struct cmd_struct *p = commands+i;
		if (strcmp(p->cmd, cmd))
			continue;
J
Jeff King 已提交
408
		exit(run_builtin(p, argc, argv));
409 410 411
	}
}

412 413
static void execv_dashed_external(const char **argv)
{
414
	struct strbuf cmd = STRBUF_INIT;
415
	const char *tmp;
416
	int status;
417 418 419 420 421 422 423 424 425 426 427 428 429 430

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

431 432 433 434
	/*
	 * if we fail because the command is not found, it is
	 * OK to return. Otherwise, we just pass along the status code.
	 */
435
	status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
436
	if (status >= 0 || errno != ENOENT)
437
		exit(status);
438 439 440 441 442 443

	argv[0] = tmp;

	strbuf_release(&cmd);
}

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
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;
}

467

468
int main(int argc, const char **argv)
469
{
470
	const char *cmd;
471

472 473
	cmd = git_extract_argv0_path(argv[0]);
	if (!cmd)
474
		cmd = "git-help";
475

476 477 478 479 480 481 482 483 484 485
	/*
	 * "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.
	 */
486
	if (!prefixcmp(cmd, "git-")) {
487 488
		cmd += 4;
		argv[0] = cmd;
489
		handle_internal_command(argc, argv);
490 491
		die("cannot handle %s internally", cmd);
	}
492

493
	/* Look for flags.. */
494 495
	argv++;
	argc--;
496
	handle_options(&argv, &argc, NULL);
J
Jeff King 已提交
497
	commit_pager_choice();
498
	if (argc > 0) {
499
		if (!prefixcmp(argv[0], "--"))
500 501
			argv[0] += 2;
	} else {
502 503 504
		/* The user didn't specify a command; give them help */
		printf("usage: %s\n\n", git_usage_string);
		list_common_cmds_help();
505
		printf("\n%s\n", git_more_info_string);
506
		exit(1);
507
	}
508
	cmd = argv[0];
509 510

	/*
511
	 * We use PATH to find git commands, but we prepend some higher
M
Mike Ralphson 已提交
512
	 * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
513 514
	 * environment, and the $(gitexecdir) from the Makefile at build
	 * time.
515
	 */
516
	setup_path();
517

J
Junio C Hamano 已提交
518
	while (1) {
519 520 521 522
		static int done_help = 0;
		static int was_alias = 0;
		was_alias = run_argv(&argc, &argv);
		if (errno != ENOENT)
J
Junio C Hamano 已提交
523
			break;
524
		if (was_alias) {
525 526 527 528 529
			fprintf(stderr, "Expansion of alias '%s' failed; "
				"'%s' is not a git-command\n",
				cmd, argv[0]);
			exit(1);
		}
530 531 532 533 534
		if (!done_help) {
			cmd = argv[0] = help_unknown_cmd(cmd);
			done_help = 1;
		} else
			break;
535
	}
536 537

	fprintf(stderr, "Failed to run command '%s': %s\n",
538
		cmd, strerror(errno));
539 540 541

	return 1;
}