git.c 14.9 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 },
J
Junio C Hamano 已提交
320
		{ "grep", cmd_grep, USE_PAGER },
321
		{ "hash-object", cmd_hash_object },
J
Junio C Hamano 已提交
322
		{ "help", cmd_help },
L
Linus Torvalds 已提交
323
		{ "index-pack", cmd_index_pack },
324
		{ "init", cmd_init_db },
J
Junio C Hamano 已提交
325
		{ "init-db", cmd_init_db },
326 327 328
		{ "log", cmd_log, RUN_SETUP | USE_PAGER },
		{ "ls-files", cmd_ls_files, RUN_SETUP },
		{ "ls-tree", cmd_ls_tree, RUN_SETUP },
D
Daniel Barkalow 已提交
329
		{ "ls-remote", cmd_ls_remote },
J
Junio C Hamano 已提交
330 331
		{ "mailinfo", cmd_mailinfo },
		{ "mailsplit", cmd_mailsplit },
M
Miklos Vajna 已提交
332
		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
333
		{ "merge-base", cmd_merge_base, RUN_SETUP },
334
		{ "merge-file", cmd_merge_file },
L
Linus Torvalds 已提交
335
		{ "merge-index", cmd_merge_index, RUN_SETUP },
336
		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
D
Daniel Barkalow 已提交
337
		{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
A
Avery Pennarun 已提交
338 339
		{ "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 已提交
340
		{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
341
		{ "merge-tree", cmd_merge_tree, RUN_SETUP },
L
Linus Torvalds 已提交
342
		{ "mktag", cmd_mktag, RUN_SETUP },
J
Junio C Hamano 已提交
343
		{ "mktree", cmd_mktree, RUN_SETUP },
344
		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
345 346
		{ "name-rev", cmd_name_rev, RUN_SETUP },
		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
347
		{ "pack-redundant", cmd_pack_redundant, RUN_SETUP },
L
Linus Torvalds 已提交
348
		{ "patch-id", cmd_patch_id },
D
Daniel Barkalow 已提交
349
		{ "peek-remote", cmd_ls_remote },
350
		{ "pickaxe", cmd_blame, RUN_SETUP },
351 352
		{ "prune", cmd_prune, RUN_SETUP },
		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
353
		{ "push", cmd_push, RUN_SETUP },
354
		{ "read-tree", cmd_read_tree, RUN_SETUP },
J
Junio C Hamano 已提交
355
		{ "receive-pack", cmd_receive_pack },
J
Junio C Hamano 已提交
356
		{ "reflog", cmd_reflog, RUN_SETUP },
J
Johannes Schindelin 已提交
357
		{ "remote", cmd_remote, RUN_SETUP },
C
Christian Couder 已提交
358
		{ "replace", cmd_replace, RUN_SETUP },
359
		{ "repo-config", cmd_config },
J
Johannes Schindelin 已提交
360
		{ "rerere", cmd_rerere, RUN_SETUP },
C
Carlos Rica 已提交
361
		{ "reset", cmd_reset, RUN_SETUP },
362
		{ "rev-list", cmd_rev_list, RUN_SETUP },
J
Junio C Hamano 已提交
363
		{ "rev-parse", cmd_rev_parse },
364
		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
365
		{ "rm", cmd_rm, RUN_SETUP },
366
		{ "send-pack", cmd_send_pack, RUN_SETUP },
367
		{ "shortlog", cmd_shortlog, USE_PAGER },
368 369
		{ "show-branch", cmd_show_branch, RUN_SETUP },
		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
370
		{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
371
		{ "stripspace", cmd_stripspace },
372
		{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
C
Carlos Rica 已提交
373
		{ "tag", cmd_tag, RUN_SETUP },
R
Rene Scharfe 已提交
374
		{ "tar-tree", cmd_tar_tree },
375
		{ "unpack-file", cmd_unpack_file, RUN_SETUP },
376 377 378
		{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
		{ "update-index", cmd_update_index, RUN_SETUP },
		{ "update-ref", cmd_update_ref, RUN_SETUP },
379
		{ "update-server-info", cmd_update_server_info, RUN_SETUP },
F
Franck Bui-Huu 已提交
380
		{ "upload-archive", cmd_upload_archive },
L
Linus Torvalds 已提交
381
		{ "var", cmd_var },
C
Carlos Rica 已提交
382
		{ "verify-tag", cmd_verify_tag, RUN_SETUP },
J
Junio C Hamano 已提交
383
		{ "version", cmd_version },
384 385
		{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
		{ "write-tree", cmd_write_tree, RUN_SETUP },
R
Rene Scharfe 已提交
386
		{ "verify-pack", cmd_verify_pack },
387
		{ "show-ref", cmd_show_ref, RUN_SETUP },
388
		{ "pack-refs", cmd_pack_refs, RUN_SETUP },
389 390
	};
	int i;
391 392 393 394 395
	static const char ext[] = STRIP_EXTENSION;

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

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

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

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

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

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

	argv[0] = tmp;

	strbuf_release(&cmd);
}

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
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;
}

471

472
int main(int argc, const char **argv)
473
{
474
	const char *cmd;
475

476 477
	cmd = git_extract_argv0_path(argv[0]);
	if (!cmd)
478
		cmd = "git-help";
479

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

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

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

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

	fprintf(stderr, "Failed to run command '%s': %s\n",
542
		cmd, strerror(errno));
543 544 545

	return 1;
}