git.c 14.2 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
		} else if (!strcmp(cmd, "--git-dir")) {
93 94 95 96
			if (*argc < 2) {
				fprintf(stderr, "No directory given for --git-dir.\n" );
				usage(git_usage_string);
			}
97
			setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
98 99
			if (envchanged)
				*envchanged = 1;
100 101
			(*argv)++;
			(*argc)--;
102
			handled++;
103
		} else if (!prefixcmp(cmd, "--git-dir=")) {
104
			setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
105 106
			if (envchanged)
				*envchanged = 1;
107 108 109 110 111 112
		} 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);
113 114
			if (envchanged)
				*envchanged = 1;
115 116 117 118
			(*argv)++;
			(*argc)--;
		} else if (!prefixcmp(cmd, "--work-tree=")) {
			setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
119 120
			if (envchanged)
				*envchanged = 1;
121
		} else if (!strcmp(cmd, "--bare")) {
122
			static char git_dir[PATH_MAX+1];
123
			is_bare_repository_cfg = 1;
124
			setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
125 126
			if (envchanged)
				*envchanged = 1;
127 128
		} else {
			fprintf(stderr, "Unknown option: %s\n", cmd);
129
			usage(git_usage_string);
130
		}
131 132 133 134 135 136 137 138

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

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

149
	subdir = setup_git_directory_gently(&unused_nongit);
150 151

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

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

191
		trace_argv_printf(new_argv,
192 193
				  "trace: alias expansion: %s =>",
				  alias_command);
194

195
		new_argv = xrealloc(new_argv, sizeof(char *) *
196
				    (count + *argcp));
197
		/* insert after command name */
198
		memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
199

200 201
		*argv = new_argv;
		*argcp += count - 1;
202

203
		ret = 1;
204 205
	}

A
Alex Riesen 已提交
206
	if (subdir && chdir(subdir))
207
		die_errno("Cannot change to '%s'", subdir);
208

209 210
	errno = saved_errno;

211 212 213
	return ret;
}

214
const char git_version_string[] = GIT_VERSION;
J
Junio C Hamano 已提交
215

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

224 225 226 227 228 229
struct cmd_struct {
	const char *cmd;
	int (*fn)(int, const char **, const char *);
	int option;
};

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

	prefix = NULL;
	if (p->option & RUN_SETUP)
		prefix = setup_git_directory();
J
Jeff King 已提交
239 240 241 242 243 244 245

	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 已提交
246 247 248
	if (p->option & NEED_WORK_TREE)
		setup_work_tree();

249
	trace_argv_printf(argv, "trace: built-in: git");
250

251 252
	status = p->fn(argc, argv, prefix);
	if (status)
253
		return status;
254 255 256 257 258 259 260 261 262

	/* 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.. */
263
	if (fflush(stdout))
264
		die_errno("write failure on standard output");
265 266 267
	if (ferror(stdout))
		die("unknown write failure on standard output");
	if (fclose(stdout))
268
		die_errno("close failed on standard output");
269
	return 0;
270 271 272
}

static void handle_internal_command(int argc, const char **argv)
273 274
{
	const char *cmd = argv[0];
275
	static struct cmd_struct commands[] = {
276
		{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
277
		{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
278
		{ "annotate", cmd_annotate, RUN_SETUP },
J
Junio C Hamano 已提交
279
		{ "apply", cmd_apply },
280
		{ "archive", cmd_archive },
281
		{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
282
		{ "blame", cmd_blame, RUN_SETUP },
J
Junio C Hamano 已提交
283
		{ "branch", cmd_branch, RUN_SETUP },
284
		{ "bundle", cmd_bundle },
285
		{ "cat-file", cmd_cat_file, RUN_SETUP },
D
Daniel Barkalow 已提交
286
		{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
287 288
		{ "checkout-index", cmd_checkout_index,
			RUN_SETUP | NEED_WORK_TREE},
P
Peter Eriksen 已提交
289
		{ "check-ref-format", cmd_check_ref_format },
290
		{ "check-attr", cmd_check_attr, RUN_SETUP },
R
Rene Scharfe 已提交
291
		{ "cherry", cmd_cherry, RUN_SETUP },
292
		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
D
Daniel Barkalow 已提交
293
		{ "clone", cmd_clone },
S
Shawn Bohrer 已提交
294
		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
K
Kristian Høgsberg 已提交
295
		{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
296
		{ "commit-tree", cmd_commit_tree, RUN_SETUP },
297
		{ "config", cmd_config },
298
		{ "count-objects", cmd_count_objects, RUN_SETUP },
S
Shawn O. Pearce 已提交
299
		{ "describe", cmd_describe, RUN_SETUP },
300
		{ "diff", cmd_diff },
301
		{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
302 303
		{ "diff-index", cmd_diff_index, RUN_SETUP },
		{ "diff-tree", cmd_diff_tree, RUN_SETUP },
304
		{ "fast-export", cmd_fast_export, RUN_SETUP },
D
Daniel Barkalow 已提交
305
		{ "fetch", cmd_fetch, RUN_SETUP },
306
		{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
307
		{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
308
		{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
309
		{ "format-patch", cmd_format_patch, RUN_SETUP },
310 311
		{ "fsck", cmd_fsck, RUN_SETUP },
		{ "fsck-objects", cmd_fsck, RUN_SETUP },
J
James Bowes 已提交
312
		{ "gc", cmd_gc, RUN_SETUP },
J
Junio C Hamano 已提交
313
		{ "get-tar-commit-id", cmd_get_tar_commit_id },
J
Johannes Schindelin 已提交
314
		{ "grep", cmd_grep, RUN_SETUP | USE_PAGER },
J
Junio C Hamano 已提交
315
		{ "help", cmd_help },
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 },
C
Christian Couder 已提交
343
		{ "replace", cmd_replace, RUN_SETUP },
344
		{ "repo-config", cmd_config },
J
Johannes Schindelin 已提交
345
		{ "rerere", cmd_rerere, RUN_SETUP },
C
Carlos Rica 已提交
346
		{ "reset", cmd_reset, RUN_SETUP },
347
		{ "rev-list", cmd_rev_list, RUN_SETUP },
J
Junio C Hamano 已提交
348
		{ "rev-parse", cmd_rev_parse },
349
		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
350
		{ "rm", cmd_rm, RUN_SETUP },
351
		{ "send-pack", cmd_send_pack, RUN_SETUP },
352
		{ "shortlog", cmd_shortlog, USE_PAGER },
353 354
		{ "show-branch", cmd_show_branch, RUN_SETUP },
		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
355
		{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
J
Junio C Hamano 已提交
356
		{ "stripspace", cmd_stripspace },
357
		{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
C
Carlos Rica 已提交
358
		{ "tag", cmd_tag, RUN_SETUP },
R
Rene Scharfe 已提交
359
		{ "tar-tree", cmd_tar_tree },
360 361 362
		{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
		{ "update-index", cmd_update_index, RUN_SETUP },
		{ "update-ref", cmd_update_ref, RUN_SETUP },
363
		{ "update-server-info", cmd_update_server_info, RUN_SETUP },
F
Franck Bui-Huu 已提交
364
		{ "upload-archive", cmd_upload_archive },
C
Carlos Rica 已提交
365
		{ "verify-tag", cmd_verify_tag, RUN_SETUP },
J
Junio C Hamano 已提交
366
		{ "version", cmd_version },
367 368
		{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
		{ "write-tree", cmd_write_tree, RUN_SETUP },
R
Rene Scharfe 已提交
369
		{ "verify-pack", cmd_verify_pack },
370
		{ "show-ref", cmd_show_ref, RUN_SETUP },
371
		{ "pack-refs", cmd_pack_refs, RUN_SETUP },
372 373
	};
	int i;
374 375 376 377 378
	static const char ext[] = STRIP_EXTENSION;

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

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

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

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

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

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.
	 */
422
	status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
423
	if (status >= 0 || errno != ENOENT)
424
		exit(status);
425 426 427 428 429 430

	argv[0] = tmp;

	strbuf_release(&cmd);
}

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

454

455
int main(int argc, const char **argv)
456
{
457
	const char *cmd;
458

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

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

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

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

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

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

	return 1;
}