git.c 13.9 KB
Newer Older
1
#include "builtin.h"
2
#include "exec_cmd.h"
3
#include "cache.h"
4
#include "quote.h"
5

6
const char git_usage_string[] =
7
	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
8

9 10 11
const char git_more_info_string[] =
	"See 'git help COMMAND' for more information on a specific command.";

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

49
static int handle_options(const char*** argv, int* argc, int* envchanged)
50 51 52 53 54 55 56 57
{
	int handled = 0;

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

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

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

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

140
	subdir = setup_git_directory_gently(&unused_nongit);
141 142

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

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

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

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

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

195
		ret = 1;
196 197
	}

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

201 202
	errno = saved_errno;

203 204 205
	return ret;
}

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

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

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

static int run_command(struct cmd_struct *p, int argc, const char **argv)
{
224 225
	int status;
	struct stat st;
226 227 228 229 230
	const char *prefix;

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

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

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

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

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

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

	if (sizeof(ext) > 1) {
		i = strlen(argv[0]) - strlen(ext);
		if (i > 0 && !strcmp(argv[0] + i, ext)) {
371
			char *argv0 = xstrdup(argv[0]);
372 373 374 375
			argv[0] = cmd = argv0;
			argv0[i] = '\0';
		}
	}
376

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

383 384 385 386
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		struct cmd_struct *p = commands+i;
		if (strcmp(p->cmd, cmd))
			continue;
387
		exit(run_command(p, argc, argv));
388 389 390
	}
}

391 392
static void execv_dashed_external(const char **argv)
{
393
	struct strbuf cmd = STRBUF_INIT;
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
	const char *tmp;

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

	/* execvp() can only ever return if it fails */
	execvp(cmd.buf, (char **)argv);

	trace_printf("trace: exec failed: %s\n", strerror(errno));

	argv[0] = tmp;

	strbuf_release(&cmd);
}


420
int main(int argc, const char **argv)
421
{
422 423
	const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
	char *slash = (char *)cmd + strlen(cmd);
J
Junio C Hamano 已提交
424
	int done_alias = 0;
425 426 427 428

	/*
	 * Take the basename of argv[0] as the command
	 * name, and the dirname as the default exec_path
429
	 * if we don't have anything better.
430
	 */
431 432 433 434
	do
		--slash;
	while (cmd <= slash && !is_dir_sep(*slash));
	if (cmd <= slash) {
435
		*slash++ = 0;
436
		git_set_argv0_path(cmd);
437 438
		cmd = slash;
	}
439

440 441 442 443 444 445 446 447 448 449
	/*
	 * "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.
	 */
450
	if (!prefixcmp(cmd, "git-")) {
451 452
		cmd += 4;
		argv[0] = cmd;
453
		handle_internal_command(argc, argv);
454 455
		die("cannot handle %s internally", cmd);
	}
456

457
	/* Look for flags.. */
458 459
	argv++;
	argc--;
460
	handle_options(&argv, &argc, NULL);
J
Jeff King 已提交
461
	commit_pager_choice();
462
	if (argc > 0) {
463
		if (!prefixcmp(argv[0], "--"))
464 465
			argv[0] += 2;
	} else {
466 467 468
		/* The user didn't specify a command; give them help */
		printf("usage: %s\n\n", git_usage_string);
		list_common_cmds_help();
469
		printf("\n%s\n", git_more_info_string);
470
		exit(1);
471
	}
472
	cmd = argv[0];
473 474

	/*
475 476 477 478
	 * 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.
479
	 */
480
	setup_path();
481

J
Junio C Hamano 已提交
482 483
	while (1) {
		/* See if it's an internal command */
484
		handle_internal_command(argc, argv);
485

J
Junio C Hamano 已提交
486
		/* .. then try the external ones */
487
		execv_dashed_external(argv);
488

J
Junio C Hamano 已提交
489 490 491 492 493 494 495 496
		/* 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(&argc, &argv))
			break;
		done_alias = 1;
	}
497

498 499 500 501 502 503 504
	if (errno == ENOENT) {
		if (done_alias) {
			fprintf(stderr, "Expansion of alias '%s' failed; "
				"'%s' is not a git-command\n",
				cmd, argv[0]);
			exit(1);
		}
505 506 507
		argv[0] = help_unknown_cmd(cmd);
		handle_internal_command(argc, argv);
		execv_dashed_external(argv);
508
	}
509 510

	fprintf(stderr, "Failed to run command '%s': %s\n",
511
		cmd, strerror(errno));
512 513 514

	return 1;
}