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

	if (sizeof(ext) > 1) {
		i = strlen(argv[0]) - strlen(ext);
		if (i > 0 && !strcmp(argv[0] + i, ext)) {
385
			char *argv0 = xstrdup(argv[0]);
386 387 388 389
			argv[0] = cmd = argv0;
			argv0[i] = '\0';
		}
	}
390

L
Linus Torvalds 已提交
391 392 393 394 395 396
	/* Turn "git cmd --help" into "git help cmd" */
	if (argc > 1 && !strcmp(argv[1], "--help")) {
		argv[1] = argv[0];
		argv[0] = cmd = "help";
	}

397 398 399 400
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		struct cmd_struct *p = commands+i;
		if (strcmp(p->cmd, cmd))
			continue;
J
Jeff King 已提交
401
		exit(run_builtin(p, argc, argv));
402 403 404
	}
}

405 406
static void execv_dashed_external(const char **argv)
{
407
	struct strbuf cmd = STRBUF_INIT;
408
	const char *tmp;
409
	int status;
410 411 412 413 414 415 416 417 418 419 420 421 422 423

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

424 425 426 427
	/*
	 * if we fail because the command is not found, it is
	 * OK to return. Otherwise, we just pass along the status code.
	 */
428
	status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
429
	if (status >= 0 || errno != ENOENT)
430
		exit(status);
431 432 433 434 435 436

	argv[0] = tmp;

	strbuf_release(&cmd);
}

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
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;
}

460

461
int main(int argc, const char **argv)
462
{
463
	const char *cmd;
464

465 466
	cmd = git_extract_argv0_path(argv[0]);
	if (!cmd)
467
		cmd = "git-help";
468

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

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

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

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

	fprintf(stderr, "Failed to run command '%s': %s\n",
531
		cmd, strerror(errno));
532 533 534

	return 1;
}