diff options
Diffstat (limited to 'git.c')
| -rw-r--r-- | git.c | 247 | 
1 files changed, 162 insertions, 85 deletions
@@ -1,41 +1,25 @@  #include "builtin.h" -#include "exec_cmd.h"  #include "cache.h" +#include "exec_cmd.h" +#include "help.h"  #include "quote.h"  #include "run-command.h" +#include "commit.h"  const char git_usage_string[] = -	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n" -	"           [-p|--paginate|--no-pager] [--no-replace-objects]\n" -	"           [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n" -	"           [--help] COMMAND [ARGS]"; +	"git [--version] [--help] [-C <path>] [-c name=value]\n" +	"           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n" +	"           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n" +	"           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n" +	"           <command> [<args>]";  const char git_more_info_string[] = -	"See 'git help COMMAND' for more information on a specific command."; +	N_("'git help -a' and 'git help -g' lists available subcommands and some\n" +	   "concept guides. See 'git help <command>' or 'git help <concept>'\n" +	   "to read about a specific subcommand or concept."); +static struct startup_info git_startup_info;  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) { @@ -52,10 +36,7 @@ static void commit_pager_choice(void) {  static int handle_options(const char ***argv, int *argc, int *envchanged)  { -	int handled = 0; - -	if (!getenv("GIT_ASKPASS") && getenv("SSH_ASKPASS")) -		setenv("GIT_ASKPASS", getenv("SSH_ASKPASS"), 1); +	const char **orig_argv = *argv;  	while (*argc > 0) {  		const char *cmd = (*argv)[0]; @@ -84,6 +65,12 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)  		} else if (!strcmp(cmd, "--html-path")) {  			puts(system_path(GIT_HTML_PATH));  			exit(0); +		} else if (!strcmp(cmd, "--man-path")) { +			puts(system_path(GIT_MAN_PATH)); +			exit(0); +		} else if (!strcmp(cmd, "--info-path")) { +			puts(system_path(GIT_INFO_PATH)); +			exit(0);  		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {  			use_pager = 1;  		} else if (!strcmp(cmd, "--no-pager")) { @@ -105,11 +92,24 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)  				*envchanged = 1;  			(*argv)++;  			(*argc)--; -			handled++;  		} else if (!prefixcmp(cmd, "--git-dir=")) {  			setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);  			if (envchanged)  				*envchanged = 1; +		} else if (!strcmp(cmd, "--namespace")) { +			if (*argc < 2) { +				fprintf(stderr, "No namespace given for --namespace.\n" ); +				usage(git_usage_string); +			} +			setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1); +			if (envchanged) +				*envchanged = 1; +			(*argv)++; +			(*argc)--; +		} else if (!prefixcmp(cmd, "--namespace=")) { +			setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1); +			if (envchanged) +				*envchanged = 1;  		} else if (!strcmp(cmd, "--work-tree")) {  			if (*argc < 2) {  				fprintf(stderr, "No directory given for --work-tree.\n" ); @@ -128,8 +128,54 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)  			static char git_dir[PATH_MAX+1];  			is_bare_repository_cfg = 1;  			setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0); +			setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1); +			if (envchanged) +				*envchanged = 1; +		} else if (!strcmp(cmd, "-c")) { +			if (*argc < 2) { +				fprintf(stderr, "-c expects a configuration string\n" ); +				usage(git_usage_string); +			} +			git_config_push_parameter((*argv)[1]); +			(*argv)++; +			(*argc)--; +		} else if (!strcmp(cmd, "--literal-pathspecs")) { +			setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1);  			if (envchanged)  				*envchanged = 1; +		} else if (!strcmp(cmd, "--no-literal-pathspecs")) { +			setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1); +			if (envchanged) +				*envchanged = 1; +		} else if (!strcmp(cmd, "--glob-pathspecs")) { +			setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, "1", 1); +			if (envchanged) +				*envchanged = 1; +		} else if (!strcmp(cmd, "--noglob-pathspecs")) { +			setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1); +			if (envchanged) +				*envchanged = 1; +		} else if (!strcmp(cmd, "--icase-pathspecs")) { +			setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1); +			if (envchanged) +				*envchanged = 1; +		} else if (!strcmp(cmd, "--shallow-file")) { +			(*argv)++; +			(*argc)--; +			set_alternate_shallow_file((*argv)[0]); +			if (envchanged) +				*envchanged = 1; +		} else if (!strcmp(cmd, "-C")) { +			if (*argc < 2) { +				fprintf(stderr, "No directory given for -C.\n" ); +				usage(git_usage_string); +			} +			if (chdir((*argv)[1])) +				die_errno("Cannot change to '%s'", (*argv)[1]); +			if (envchanged) +				*envchanged = 1; +			(*argv)++; +			(*argc)--;  		} else {  			fprintf(stderr, "Unknown option: %s\n", cmd);  			usage(git_usage_string); @@ -137,9 +183,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)  		(*argv)++;  		(*argc)--; -		handled++;  	} -	return handled; +	return (*argv) - orig_argv;  }  static int handle_alias(int *argcp, const char ***argv) @@ -158,27 +203,29 @@ static int handle_alias(int *argcp, const char ***argv)  	alias_string = alias_lookup(alias_command);  	if (alias_string) {  		if (alias_string[0] == '!') { -			if (*argcp > 1) { -				struct strbuf buf; - -				strbuf_init(&buf, PATH_MAX); -				strbuf_addstr(&buf, alias_string); -				sq_quote_argv(&buf, (*argv) + 1, PATH_MAX); -				free(alias_string); -				alias_string = buf.buf; -			} -			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'", -			    alias_string + 1, alias_command); +			const char **alias_argv; +			int argc = *argcp, i; + +			commit_pager_choice(); + +			/* build alias_argv */ +			alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1)); +			alias_argv[0] = alias_string + 1; +			for (i = 1; i < argc; ++i) +				alias_argv[i] = (*argv)[i]; +			alias_argv[argc] = NULL; + +			ret = run_command_v_opt(alias_argv, RUN_USING_SHELL); +			if (ret >= 0)   /* normal exit */ +				exit(ret); + +			die_errno("While expanding alias '%s': '%s'", +			    alias_command, alias_string + 1);  		}  		count = split_cmdline(alias_string, &new_argv);  		if (count < 0) -			die("Bad alias.%s string", alias_command); +			die("Bad alias.%s string: %s", alias_command, +			    split_cmdline_strerror(count));  		option_count = handle_options(&new_argv, &count, &envchanged);  		if (envchanged)  			die("alias '%s' changes environment variables\n" @@ -217,15 +264,14 @@ static int handle_alias(int *argcp, const char ***argv)  	return ret;  } -const char git_version_string[] = GIT_VERSION; - -#define RUN_SETUP	(1<<0) -#define USE_PAGER	(1<<1) +#define RUN_SETUP		(1<<0) +#define RUN_SETUP_GENTLY	(1<<1) +#define USE_PAGER		(1<<2)  /*   * require working tree to be present -- anything uses this needs   * RUN_SETUP for reading from the configuration file.   */ -#define NEED_WORK_TREE	(1<<2) +#define NEED_WORK_TREE		(1<<3)  struct cmd_struct {  	const char *cmd; @@ -244,11 +290,19 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)  	if (!help) {  		if (p->option & RUN_SETUP)  			prefix = setup_git_directory(); +		if (p->option & RUN_SETUP_GENTLY) { +			int nongit_ok; +			prefix = setup_git_directory_gently(&nongit_ok); +		} -		if (use_pager == -1 && p->option & RUN_SETUP) +		if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))  			use_pager = check_pager_config(p->cmd);  		if (use_pager == -1 && p->option & USE_PAGER)  			use_pager = 1; + +		if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) && +		    startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */ +			trace_repo_setup(prefix);  	}  	commit_pager_choice(); @@ -283,28 +337,31 @@ static void handle_internal_command(int argc, const char **argv)  	const char *cmd = argv[0];  	static struct cmd_struct commands[] = {  		{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE }, -		{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },  		{ "annotate", cmd_annotate, RUN_SETUP }, -		{ "apply", cmd_apply }, +		{ "apply", cmd_apply, RUN_SETUP_GENTLY },  		{ "archive", cmd_archive }, -		{ "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE }, +		{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },  		{ "blame", cmd_blame, RUN_SETUP },  		{ "branch", cmd_branch, RUN_SETUP }, -		{ "bundle", cmd_bundle }, +		{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },  		{ "cat-file", cmd_cat_file, RUN_SETUP }, +		{ "check-attr", cmd_check_attr, RUN_SETUP }, +		{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE }, +		{ "check-mailmap", cmd_check_mailmap, RUN_SETUP }, +		{ "check-ref-format", cmd_check_ref_format },  		{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },  		{ "checkout-index", cmd_checkout_index,  			RUN_SETUP | NEED_WORK_TREE}, -		{ "check-ref-format", cmd_check_ref_format }, -		{ "check-attr", cmd_check_attr, RUN_SETUP },  		{ "cherry", cmd_cherry, RUN_SETUP },  		{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, -		{ "clone", cmd_clone },  		{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, +		{ "clone", cmd_clone }, +		{ "column", cmd_column, RUN_SETUP_GENTLY },  		{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },  		{ "commit-tree", cmd_commit_tree, RUN_SETUP }, -		{ "config", cmd_config }, +		{ "config", cmd_config, RUN_SETUP_GENTLY },  		{ "count-objects", cmd_count_objects, RUN_SETUP }, +		{ "credential", cmd_credential, RUN_SETUP_GENTLY },  		{ "describe", cmd_describe, RUN_SETUP },  		{ "diff", cmd_diff },  		{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE }, @@ -320,21 +377,21 @@ static void handle_internal_command(int argc, const char **argv)  		{ "fsck-objects", cmd_fsck, RUN_SETUP },  		{ "gc", cmd_gc, RUN_SETUP },  		{ "get-tar-commit-id", cmd_get_tar_commit_id }, -		{ "grep", cmd_grep, USE_PAGER }, +		{ "grep", cmd_grep, RUN_SETUP_GENTLY },  		{ "hash-object", cmd_hash_object },  		{ "help", cmd_help }, -		{ "index-pack", cmd_index_pack }, +		{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },  		{ "init", cmd_init_db },  		{ "init-db", cmd_init_db }, -		{ "log", cmd_log, RUN_SETUP | USE_PAGER }, +		{ "log", cmd_log, RUN_SETUP },  		{ "ls-files", cmd_ls_files, RUN_SETUP }, +		{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },  		{ "ls-tree", cmd_ls_tree, RUN_SETUP }, -		{ "ls-remote", cmd_ls_remote },  		{ "mailinfo", cmd_mailinfo },  		{ "mailsplit", cmd_mailsplit },  		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },  		{ "merge-base", cmd_merge_base, RUN_SETUP }, -		{ "merge-file", cmd_merge_file }, +		{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },  		{ "merge-index", cmd_merge_index, RUN_SETUP },  		{ "merge-ours", cmd_merge_ours, RUN_SETUP },  		{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE }, @@ -349,8 +406,9 @@ static void handle_internal_command(int argc, const char **argv)  		{ "notes", cmd_notes, RUN_SETUP },  		{ "pack-objects", cmd_pack_objects, RUN_SETUP },  		{ "pack-redundant", cmd_pack_redundant, RUN_SETUP }, +		{ "pack-refs", cmd_pack_refs, RUN_SETUP },  		{ "patch-id", cmd_patch_id }, -		{ "peek-remote", cmd_ls_remote }, +		{ "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },  		{ "pickaxe", cmd_blame, RUN_SETUP },  		{ "prune", cmd_prune, RUN_SETUP },  		{ "prune-packed", cmd_prune_packed, RUN_SETUP }, @@ -359,8 +417,10 @@ static void handle_internal_command(int argc, const char **argv)  		{ "receive-pack", cmd_receive_pack },  		{ "reflog", cmd_reflog, RUN_SETUP },  		{ "remote", cmd_remote, RUN_SETUP }, +		{ "remote-ext", cmd_remote_ext }, +		{ "remote-fd", cmd_remote_fd },  		{ "replace", cmd_replace, RUN_SETUP }, -		{ "repo-config", cmd_config }, +		{ "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },  		{ "rerere", cmd_rerere, RUN_SETUP },  		{ "reset", cmd_reset, RUN_SETUP },  		{ "rev-list", cmd_rev_list, RUN_SETUP }, @@ -368,9 +428,11 @@ static void handle_internal_command(int argc, const char **argv)  		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },  		{ "rm", cmd_rm, RUN_SETUP },  		{ "send-pack", cmd_send_pack, RUN_SETUP }, -		{ "shortlog", cmd_shortlog, USE_PAGER }, +		{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER }, +		{ "show", cmd_show, RUN_SETUP },  		{ "show-branch", cmd_show_branch, RUN_SETUP }, -		{ "show", cmd_show, RUN_SETUP | USE_PAGER }, +		{ "show-ref", cmd_show_ref, RUN_SETUP }, +		{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },  		{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },  		{ "stripspace", cmd_stripspace },  		{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, @@ -382,14 +444,13 @@ static void handle_internal_command(int argc, const char **argv)  		{ "update-ref", cmd_update_ref, RUN_SETUP },  		{ "update-server-info", cmd_update_server_info, RUN_SETUP },  		{ "upload-archive", cmd_upload_archive }, -		{ "var", cmd_var }, +		{ "upload-archive--writer", cmd_upload_archive_writer }, +		{ "var", cmd_var, RUN_SETUP_GENTLY }, +		{ "verify-pack", cmd_verify_pack },  		{ "verify-tag", cmd_verify_tag, RUN_SETUP },  		{ "version", cmd_version }, -		{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER }, +		{ "whatchanged", cmd_whatchanged, RUN_SETUP },  		{ "write-tree", cmd_write_tree, RUN_SETUP }, -		{ "verify-pack", cmd_verify_pack }, -		{ "show-ref", cmd_show_ref, RUN_SETUP }, -		{ "pack-refs", cmd_pack_refs, RUN_SETUP },  	};  	int i;  	static const char ext[] = STRIP_EXTENSION; @@ -423,6 +484,10 @@ static void execv_dashed_external(const char **argv)  	const char *tmp;  	int status; +	if (use_pager == -1) +		use_pager = check_pager_config(argv[0]); +	commit_pager_choice(); +  	strbuf_addf(&cmd, "git-%s", argv[0]);  	/* @@ -440,7 +505,7 @@ static void execv_dashed_external(const char **argv)  	 * if we fail because the command is not found, it is  	 * OK to return. Otherwise, we just pass along the status code.  	 */ -	status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE); +	status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT);  	if (status >= 0 || errno != ENOENT)  		exit(status); @@ -473,15 +538,27 @@ static int run_argv(int *argcp, const char ***argv)  } -int main(int argc, const char **argv) +int main(int argc, char **av)  { +	const char **argv = (const char **) av;  	const char *cmd; +	startup_info = &git_startup_info; +  	cmd = git_extract_argv0_path(argv[0]);  	if (!cmd)  		cmd = "git-help";  	/* +	 * Always open file descriptors 0/1/2 to avoid clobbering files +	 * in die().  It also avoids messing up when the pipes are dup'ed +	 * onto stdin/stdout/stderr in the child processes we spawn. +	 */ +	sanitize_stdfds(); + +	git_setup_gettext(); + +	/*  	 * "git-xxxx" is the same as "git xxxx", but we obviously:  	 *  	 *  - cannot take flags in between the "git" and the "xxxx". @@ -502,15 +579,15 @@ int main(int argc, const char **argv)  	argv++;  	argc--;  	handle_options(&argv, &argc, NULL); -	commit_pager_choice();  	if (argc > 0) {  		if (!prefixcmp(argv[0], "--"))  			argv[0] += 2;  	} else {  		/* The user didn't specify a command; give them help */ +		commit_pager_choice();  		printf("usage: %s\n\n", git_usage_string);  		list_common_cmds_help(); -		printf("\n%s\n", git_more_info_string); +		printf("\n%s\n", _(git_more_info_string));  		exit(1);  	}  	cmd = argv[0];  | 
