git.c 12.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.";

12
static int handle_options(const char*** argv, int* argc, int* envchanged)
13 14 15 16 17 18 19 20
{
	int handled = 0;

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

21 22 23 24 25 26 27 28 29 30 31
		/*
		 * 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.
		 */
32
		if (!prefixcmp(cmd, "--exec-path")) {
33 34
			cmd += 11;
			if (*cmd == '=')
35
				git_set_argv_exec_path(cmd + 1);
36 37 38 39 40
			else {
				puts(git_exec_path());
				exit(0);
			}
		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
41
			setup_pager();
42 43 44 45
		} else if (!strcmp(cmd, "--no-pager")) {
			setenv("GIT_PAGER", "cat", 1);
			if (envchanged)
				*envchanged = 1;
46
		} else if (!strcmp(cmd, "--git-dir")) {
47 48 49 50
			if (*argc < 2) {
				fprintf(stderr, "No directory given for --git-dir.\n" );
				usage(git_usage_string);
			}
51
			setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
52 53
			if (envchanged)
				*envchanged = 1;
54 55
			(*argv)++;
			(*argc)--;
56
			handled++;
57
		} else if (!prefixcmp(cmd, "--git-dir=")) {
58
			setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
59 60
			if (envchanged)
				*envchanged = 1;
61 62 63 64 65 66
		} 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);
67 68
			if (envchanged)
				*envchanged = 1;
69 70 71 72
			(*argv)++;
			(*argc)--;
		} else if (!prefixcmp(cmd, "--work-tree=")) {
			setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
73 74
			if (envchanged)
				*envchanged = 1;
75
		} else if (!strcmp(cmd, "--bare")) {
76
			static char git_dir[PATH_MAX+1];
77
			is_bare_repository_cfg = 1;
78
			setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
79 80
			if (envchanged)
				*envchanged = 1;
81 82
		} else {
			fprintf(stderr, "Unknown option: %s\n", cmd);
83
			usage(git_usage_string);
84
		}
85 86 87 88 89 90 91 92

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

93 94 95 96 97
static int split_cmdline(char *cmdline, const char ***argv)
{
	int src, dst, count = 0, size = 16;
	char quoted = 0;

J
James Bowes 已提交
98
	*argv = xmalloc(sizeof(char*) * size);
99 100 101 102 103 104 105 106 107 108 109 110

	/* split alias_string */
	(*argv)[count++] = cmdline;
	for (src = dst = 0; cmdline[src];) {
		char c = cmdline[src];
		if (!quoted && isspace(c)) {
			cmdline[dst++] = 0;
			while (cmdline[++src]
					&& isspace(cmdline[src]))
				; /* skip */
			if (count >= size) {
				size += 16;
J
Jonas Fonseca 已提交
111
				*argv = xrealloc(*argv, sizeof(char*) * size);
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
			}
			(*argv)[count++] = cmdline + dst;
		} else if(!quoted && (c == '\'' || c == '"')) {
			quoted = c;
			src++;
		} else if (c == quoted) {
			quoted = 0;
			src++;
		} else {
			if (c == '\\' && quoted != '\'') {
				src++;
				c = cmdline[src];
				if (!c) {
					free(*argv);
					*argv = NULL;
					return error("cmdline ends with \\");
				}
			}
			cmdline[dst++] = c;
			src++;
		}
	}

	cmdline[dst] = 0;

	if (quoted) {
		free(*argv);
		*argv = NULL;
		return error("unclosed quote");
	}

	return count;
}

static int handle_alias(int *argcp, const char ***argv)
{
148
	int envchanged = 0, ret = 0, saved_errno = errno;
149
	const char *subdir;
150 151
	int count, option_count;
	const char** new_argv;
152 153
	const char *alias_command;
	char *alias_string;
154
	int unused_nongit;
155

156
	subdir = setup_git_directory_gently(&unused_nongit);
157 158

	alias_command = (*argv)[0];
159
	alias_string = alias_lookup(alias_command);
160
	if (alias_string) {
161
		if (alias_string[0] == '!') {
162
			if (*argcp > 1) {
163
				struct strbuf buf;
164

165 166
				strbuf_init(&buf, PATH_MAX);
				strbuf_addstr(&buf, alias_string);
167
				sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
168
				free(alias_string);
169
				alias_string = buf.buf;
170
			}
171 172 173 174 175 176 177 178 179
			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);
		}
180
		count = split_cmdline(alias_string, &new_argv);
181 182 183 184 185
		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);
186 187 188 189 190 191 192 193 194 195
		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);

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

J
Jonas Fonseca 已提交
200 201
		new_argv = xrealloc(new_argv, sizeof(char*) *
				    (count + *argcp + 1));
202 203 204
		/* insert after command name */
		memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);
		new_argv[count+*argcp] = NULL;
205

206 207
		*argv = new_argv;
		*argcp += count - 1;
208

209
		ret = 1;
210 211 212 213 214
	}

	if (subdir)
		chdir(subdir);

215 216
	errno = saved_errno;

217 218 219
	return ret;
}

220
const char git_version_string[] = GIT_VERSION;
J
Junio C Hamano 已提交
221

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

230 231 232 233 234 235 236 237
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)
{
238 239
	int status;
	struct stat st;
240 241 242 243 244 245 246
	const char *prefix;

	prefix = NULL;
	if (p->option & RUN_SETUP)
		prefix = setup_git_directory();
	if (p->option & USE_PAGER)
		setup_pager();
M
Mike Hommey 已提交
247 248 249
	if (p->option & NEED_WORK_TREE)
		setup_work_tree();

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

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

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

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

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

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

387
int main(int argc, const char **argv)
388
{
D
Dmitry V. Levin 已提交
389
	const char *cmd = argv[0] ? argv[0] : "git-help";
390
	char *slash = strrchr(cmd, '/');
391
	const char *cmd_path = NULL;
J
Junio C Hamano 已提交
392
	int done_alias = 0;
393 394 395 396

	/*
	 * Take the basename of argv[0] as the command
	 * name, and the dirname as the default exec_path
397
	 * if we don't have anything better.
398 399 400
	 */
	if (slash) {
		*slash++ = 0;
401
		cmd_path = cmd;
402 403
		cmd = slash;
	}
404

405 406 407 408 409 410 411 412 413 414
	/*
	 * "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.
	 */
415
	if (!prefixcmp(cmd, "git-")) {
416 417
		cmd += 4;
		argv[0] = cmd;
418
		handle_internal_command(argc, argv);
419 420
		die("cannot handle %s internally", cmd);
	}
421

422
	/* Look for flags.. */
423 424
	argv++;
	argc--;
425
	handle_options(&argv, &argc, NULL);
426
	if (argc > 0) {
427
		if (!prefixcmp(argv[0], "--"))
428 429
			argv[0] += 2;
	} else {
430 431 432
		/* The user didn't specify a command; give them help */
		printf("usage: %s\n\n", git_usage_string);
		list_common_cmds_help();
433
		printf("\n%s\n", git_more_info_string);
434
		exit(1);
435
	}
436
	cmd = argv[0];
437 438

	/*
439 440 441 442
	 * 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.
443
	 */
444
	setup_path(cmd_path);
445

J
Junio C Hamano 已提交
446 447
	while (1) {
		/* See if it's an internal command */
448
		handle_internal_command(argc, argv);
449

J
Junio C Hamano 已提交
450 451
		/* .. then try the external ones */
		execv_git_cmd(argv);
452

J
Junio C Hamano 已提交
453 454 455 456 457 458 459 460
		/* 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;
	}
461

462 463 464 465 466 467 468
	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);
		}
469
		help_unknown_cmd(cmd);
470
	}
471 472

	fprintf(stderr, "Failed to run command '%s': %s\n",
473
		cmd, strerror(errno));
474 475 476

	return 1;
}