git.c 12.7 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
static int handle_options(const char*** argv, int* argc, int* envchanged)
10 11 12 13 14 15 16 17
{
	int handled = 0;

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

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

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

90 91 92 93 94
static int split_cmdline(char *cmdline, const char ***argv)
{
	int src, dst, count = 0, size = 16;
	char quoted = 0;

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

	/* 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 已提交
108
				*argv = xrealloc(*argv, sizeof(char*) * size);
109 110 111 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
			}
			(*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)
{
145
	int nongit = 0, envchanged = 0, ret = 0, saved_errno = errno;
146
	const char *subdir;
147 148
	int count, option_count;
	const char** new_argv;
149 150
	const char *alias_command;
	char *alias_string;
151 152

	subdir = setup_git_directory_gently(&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 173 174 175
			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);
		}
176
		count = split_cmdline(alias_string, &new_argv);
177 178 179 180 181
		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);
182 183 184 185 186 187 188 189 190 191
		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);

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

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

202 203
		*argv = new_argv;
		*argcp += count - 1;
204

205
		ret = 1;
206 207 208 209 210
	}

	if (subdir)
		chdir(subdir);

211 212
	errno = saved_errno;

213 214 215
	return ret;
}

216
const char git_version_string[] = GIT_VERSION;
J
Junio C Hamano 已提交
217

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

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

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

246
	trace_argv_printf(argv, "trace: built-in: git");
247

248 249
	status = p->fn(argc, argv, prefix);
	if (status)
250
		return status & 0xff;
251 252 253 254 255 256 257 258 259

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

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

L
Linus Torvalds 已提交
368 369 370 371 372 373
	/* Turn "git cmd --help" into "git help cmd" */
	if (argc > 1 && !strcmp(argv[1], "--help")) {
		argv[1] = argv[0];
		argv[0] = cmd = "help";
	}

374 375 376 377
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		struct cmd_struct *p = commands+i;
		if (strcmp(p->cmd, cmd))
			continue;
378
		exit(run_command(p, argc, argv));
379 380 381
	}
}

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

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

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

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

	/*
433 434 435 436
	 * 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.
437
	 */
438
	setup_path(cmd_path);
439

J
Junio C Hamano 已提交
440 441
	while (1) {
		/* See if it's an internal command */
442
		handle_internal_command(argc, argv);
443

J
Junio C Hamano 已提交
444 445
		/* .. then try the external ones */
		execv_git_cmd(argv);
446

J
Junio C Hamano 已提交
447 448 449 450 451 452 453 454
		/* 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;
	}
455

456 457 458 459 460 461 462
	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);
		}
463
		help_unknown_cmd(cmd);
464
	}
465 466

	fprintf(stderr, "Failed to run command '%s': %s\n",
467
		cmd, strerror(errno));
468 469 470

	return 1;
}