Imported Upstream version 2.17.1
[platform/upstream/git.git] / git.c
diff --git a/git.c b/git.c
index 968a8a4..ceaa58e 100644 (file)
--- a/git.c
+++ b/git.c
@@ -1,14 +1,15 @@
 #include "builtin.h"
+#include "config.h"
 #include "exec_cmd.h"
 #include "help.h"
 #include "run-command.h"
 
 const char git_usage_string[] =
-       "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>]";
+       N_("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[] =
        N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -16,51 +17,8 @@ const char git_more_info_string[] =
           "to read about a specific subcommand or concept.");
 
 static int use_pager = -1;
-static char *orig_cwd;
-static const char *env_names[] = {
-       GIT_DIR_ENVIRONMENT,
-       GIT_WORK_TREE_ENVIRONMENT,
-       GIT_IMPLICIT_WORK_TREE_ENVIRONMENT,
-       GIT_PREFIX_ENVIRONMENT
-};
-static char *orig_env[4];
-static int save_restore_env_balance;
 
-static void save_env_before_alias(void)
-{
-       int i;
-
-       assert(save_restore_env_balance == 0);
-       save_restore_env_balance = 1;
-       orig_cwd = xgetcwd();
-       for (i = 0; i < ARRAY_SIZE(env_names); i++) {
-               orig_env[i] = getenv(env_names[i]);
-               if (orig_env[i])
-                       orig_env[i] = xstrdup(orig_env[i]);
-       }
-}
-
-static void restore_env(int external_alias)
-{
-       int i;
-
-       assert(save_restore_env_balance == 1);
-       save_restore_env_balance = 0;
-       if (!external_alias && orig_cwd && chdir(orig_cwd))
-               die_errno("could not move to %s", orig_cwd);
-       free(orig_cwd);
-       for (i = 0; i < ARRAY_SIZE(env_names); i++) {
-               if (external_alias &&
-                   !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
-                       continue;
-               if (orig_env[i]) {
-                       setenv(env_names[i], orig_env[i], 1);
-                       free(orig_env[i]);
-               } else {
-                       unsetenv(env_names[i]);
-               }
-       }
-}
+static void list_builtins(void);
 
 static void commit_pager_choice(void) {
        switch (use_pager) {
@@ -75,6 +33,16 @@ static void commit_pager_choice(void) {
        }
 }
 
+void setup_auto_pager(const char *cmd, int def)
+{
+       if (use_pager != -1 || pager_in_use())
+               return;
+       use_pager = check_pager_config(cmd);
+       if (use_pager == -1)
+               use_pager = def;
+       commit_pager_choice();
+}
+
 static int handle_options(const char ***argv, int *argc, int *envchanged)
 {
        const char **orig_argv = *argv;
@@ -124,7 +92,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--git-dir")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No directory given for --git-dir.\n" );
+                               fprintf(stderr, _("no directory given for --git-dir\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
@@ -138,7 +106,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--namespace")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No namespace given for --namespace.\n" );
+                               fprintf(stderr, _("no namespace given for --namespace\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
@@ -152,7 +120,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No directory given for --work-tree.\n" );
+                               fprintf(stderr, _("no directory given for --work-tree\n" ));
                                usage(git_usage_string);
                        }
                        setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
@@ -164,6 +132,20 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
+               } else if (!strcmp(cmd, "--super-prefix")) {
+                       if (*argc < 2) {
+                               fprintf(stderr, _("no prefix given for --super-prefix\n" ));
+                               usage(git_usage_string);
+                       }
+                       setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
+                       if (envchanged)
+                               *envchanged = 1;
+                       (*argv)++;
+                       (*argc)--;
+               } else if (skip_prefix(cmd, "--super-prefix=", &cmd)) {
+                       setenv(GIT_SUPER_PREFIX_ENVIRONMENT, cmd, 1);
+                       if (envchanged)
+                               *envchanged = 1;
                } else if (!strcmp(cmd, "--bare")) {
                        char *cwd = xgetcwd();
                        is_bare_repository_cfg = 1;
@@ -174,7 +156,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-c")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "-c expects a configuration string\n" );
+                               fprintf(stderr, _("-c expects a configuration string\n" ));
                                usage(git_usage_string);
                        }
                        git_config_push_parameter((*argv)[1]);
@@ -200,6 +182,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1);
                        if (envchanged)
                                *envchanged = 1;
+               } else if (!strcmp(cmd, "--no-optional-locks")) {
+                       setenv(GIT_OPTIONAL_LOCKS_ENVIRONMENT, "0", 1);
+                       if (envchanged)
+                               *envchanged = 1;
                } else if (!strcmp(cmd, "--shallow-file")) {
                        (*argv)++;
                        (*argc)--;
@@ -208,19 +194,22 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-C")) {
                        if (*argc < 2) {
-                               fprintf(stderr, "No directory given for -C.\n" );
+                               fprintf(stderr, _("no directory given for -C\n" ));
                                usage(git_usage_string);
                        }
                        if ((*argv)[1][0]) {
                                if (chdir((*argv)[1]))
-                                       die_errno("Cannot change to '%s'", (*argv)[1]);
+                                       die_errno("cannot change to '%s'", (*argv)[1]);
                                if (envchanged)
                                        *envchanged = 1;
                        }
                        (*argv)++;
                        (*argc)--;
+               } else if (!strcmp(cmd, "--list-builtins")) {
+                       list_builtins();
+                       exit(0);
                } else {
-                       fprintf(stderr, "Unknown option: %s\n", cmd);
+                       fprintf(stderr, _("unknown option: %s\n"), cmd);
                        usage(git_usage_string);
                }
 
@@ -237,19 +226,18 @@ static int handle_alias(int *argcp, const char ***argv)
        const char **new_argv;
        const char *alias_command;
        char *alias_string;
-       int unused_nongit;
-
-       save_env_before_alias();
-       setup_git_directory_gently(&unused_nongit);
 
        alias_command = (*argv)[0];
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
                if (alias_string[0] == '!') {
                        struct child_process child = CHILD_PROCESS_INIT;
+                       int nongit_ok;
+
+                       /* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */
+                       setup_git_directory_gently(&nongit_ok);
 
                        commit_pager_choice();
-                       restore_env(1);
 
                        child.use_shell = 1;
                        argv_array_push(&child.args, alias_string + 1);
@@ -259,7 +247,7 @@ static int handle_alias(int *argcp, const char ***argv)
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
 
-                       die_errno("While expanding alias '%s': '%s'",
+                       die_errno("while expanding alias '%s': '%s'",
                            alias_command, alias_string + 1);
                }
                count = split_cmdline(alias_string, &new_argv);
@@ -268,8 +256,8 @@ static int handle_alias(int *argcp, const char ***argv)
                            split_cmdline_strerror(count));
                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.",
+                       die("alias '%s' changes environment variables.\n"
+                                "You can use '!git' in the alias to do this",
                                 alias_command);
                memmove(new_argv - option_count, new_argv,
                                count * sizeof(char *));
@@ -295,8 +283,6 @@ static int handle_alias(int *argcp, const char ***argv)
                ret = 1;
        }
 
-       restore_env(0);
-
        errno = saved_errno;
 
        return ret;
@@ -310,6 +296,8 @@ static int handle_alias(int *argcp, const char ***argv)
  * RUN_SETUP for reading from the configuration file.
  */
 #define NEED_WORK_TREE         (1<<3)
+#define SUPPORT_SUPER_PREFIX   (1<<4)
+#define DELAY_PAGER_CONFIG     (1<<5)
 
 struct cmd_struct {
        const char *cmd;
@@ -333,7 +321,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
                        prefix = setup_git_directory_gently(&nongit_ok);
                }
 
-               if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
+               if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) &&
+                   !(p->option & DELAY_PAGER_CONFIG))
                        use_pager = check_pager_config(p->cmd);
                if (use_pager == -1 && p->option & USE_PAGER)
                        use_pager = 1;
@@ -344,6 +333,11 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
        }
        commit_pager_choice();
 
+       if (!help && get_super_prefix()) {
+               if (!(p->option & SUPPORT_SUPER_PREFIX))
+                       die("%s doesn't support --super-prefix", p->cmd);
+       }
+
        if (!help && p->option & NEED_WORK_TREE)
                setup_work_tree();
 
@@ -375,10 +369,10 @@ static struct cmd_struct commands[] = {
        { "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
        { "annotate", cmd_annotate, RUN_SETUP },
        { "apply", cmd_apply, RUN_SETUP_GENTLY },
-       { "archive", cmd_archive },
+       { "archive", cmd_archive, RUN_SETUP_GENTLY },
        { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
        { "blame", cmd_blame, RUN_SETUP },
-       { "branch", cmd_branch, RUN_SETUP },
+       { "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
        { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
        { "cat-file", cmd_cat_file, RUN_SETUP },
        { "check-attr", cmd_check_attr, RUN_SETUP },
@@ -395,7 +389,7 @@ static struct cmd_struct commands[] = {
        { "column", cmd_column, RUN_SETUP_GENTLY },
        { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
        { "commit-tree", cmd_commit_tree, RUN_SETUP },
-       { "config", cmd_config, RUN_SETUP_GENTLY },
+       { "config", cmd_config, RUN_SETUP_GENTLY | DELAY_PAGER_CONFIG },
        { "count-objects", cmd_count_objects, RUN_SETUP },
        { "credential", cmd_credential, RUN_SETUP_GENTLY },
        { "describe", cmd_describe, RUN_SETUP },
@@ -403,6 +397,7 @@ static struct cmd_struct commands[] = {
        { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
        { "diff-index", cmd_diff_index, RUN_SETUP },
        { "diff-tree", cmd_diff_tree, RUN_SETUP },
+       { "difftool", cmd_difftool, RUN_SETUP | NEED_WORK_TREE },
        { "fast-export", cmd_fast_export, RUN_SETUP },
        { "fetch", cmd_fetch, RUN_SETUP },
        { "fetch-pack", cmd_fetch_pack, RUN_SETUP },
@@ -424,7 +419,7 @@ static struct cmd_struct commands[] = {
        { "ls-files", cmd_ls_files, RUN_SETUP },
        { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
        { "ls-tree", cmd_ls_tree, RUN_SETUP },
-       { "mailinfo", cmd_mailinfo },
+       { "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY },
        { "mailsplit", cmd_mailsplit },
        { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
        { "merge-base", cmd_merge_base, RUN_SETUP },
@@ -444,13 +439,14 @@ static struct cmd_struct commands[] = {
        { "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 },
+       { "patch-id", cmd_patch_id, RUN_SETUP_GENTLY },
        { "pickaxe", cmd_blame, RUN_SETUP },
        { "prune", cmd_prune, RUN_SETUP },
        { "prune-packed", cmd_prune_packed, RUN_SETUP },
        { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
        { "push", cmd_push, RUN_SETUP },
-       { "read-tree", cmd_read_tree, RUN_SETUP },
+       { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
+       { "rebase--helper", cmd_rebase__helper, RUN_SETUP | NEED_WORK_TREE },
        { "receive-pack", cmd_receive_pack },
        { "reflog", cmd_reflog, RUN_SETUP },
        { "remote", cmd_remote, RUN_SETUP },
@@ -472,9 +468,9 @@ static struct cmd_struct commands[] = {
        { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
        { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
        { "stripspace", cmd_stripspace },
-       { "submodule--helper", cmd_submodule__helper, RUN_SETUP },
+       { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX},
        { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
-       { "tag", cmd_tag, RUN_SETUP },
+       { "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
        { "unpack-file", cmd_unpack_file, RUN_SETUP },
        { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
        { "update-index", cmd_update_index, RUN_SETUP },
@@ -508,6 +504,13 @@ int is_builtin(const char *s)
        return !!get_builtin(s);
 }
 
+static void list_builtins(void)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(commands); i++)
+               printf("%s\n", commands[i].cmd);
+}
+
 #ifdef STRIP_EXTENSION
 static void strip_extension(const char **argv)
 {
@@ -522,57 +525,67 @@ static void strip_extension(const char **argv)
 
 static void handle_builtin(int argc, const char **argv)
 {
+       struct argv_array args = ARGV_ARRAY_INIT;
        const char *cmd;
        struct cmd_struct *builtin;
 
        strip_extension(argv);
        cmd = argv[0];
 
-       /* Turn "git cmd --help" into "git help cmd" */
+       /* Turn "git cmd --help" into "git help --exclude-guides cmd" */
        if (argc > 1 && !strcmp(argv[1], "--help")) {
+               int i;
+
                argv[1] = argv[0];
                argv[0] = cmd = "help";
+
+               for (i = 0; i < argc; i++) {
+                       argv_array_push(&args, argv[i]);
+                       if (!i)
+                               argv_array_push(&args, "--exclude-guides");
+               }
+
+               argc++;
+               argv = args.argv;
        }
 
        builtin = get_builtin(cmd);
        if (builtin)
                exit(run_builtin(builtin, argc, argv));
+       argv_array_clear(&args);
 }
 
 static void execv_dashed_external(const char **argv)
 {
-       struct strbuf cmd = STRBUF_INIT;
-       const char *tmp;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        int status;
 
-       if (use_pager == -1)
+       if (get_super_prefix())
+               die("%s doesn't support --super-prefix", argv[0]);
+
+       if (use_pager == -1 && !is_builtin(argv[0]))
                use_pager = check_pager_config(argv[0]);
        commit_pager_choice();
 
-       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;
+       argv_array_pushf(&cmd.args, "git-%s", argv[0]);
+       argv_array_pushv(&cmd.args, argv + 1);
+       cmd.clean_on_exit = 1;
+       cmd.wait_after_clean = 1;
+       cmd.silent_exec_failure = 1;
 
-       trace_argv_printf(argv, "trace: exec:");
+       trace_argv_printf(cmd.args.argv, "trace: exec:");
 
        /*
-        * if we fail because the command is not found, it is
-        * OK to return. Otherwise, we just pass along the status code.
+        * If we fail because the command is not found, it is
+        * OK to return. Otherwise, we just pass along the status code,
+        * or our usual generic code if we were not even able to exec
+        * the program.
         */
-       status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT);
-       if (status >= 0 || errno != ENOENT)
+       status = run_command(&cmd);
+       if (status >= 0)
                exit(status);
-
-       argv[0] = tmp;
-
-       strbuf_release(&cmd);
+       else if (errno != ENOENT)
+               exit(128);
 }
 
 static int run_argv(int *argcp, const char ***argv)
@@ -609,47 +622,19 @@ static int run_argv(int *argcp, const char ***argv)
        return done_alias;
 }
 
-/*
- * Many parts of Git have subprograms communicate via pipe, expect the
- * upstream of a pipe to die with SIGPIPE when the downstream of a
- * pipe does not need to read all that is written.  Some third-party
- * programs that ignore or block SIGPIPE for their own reason forget
- * to restore SIGPIPE handling to the default before spawning Git and
- * break this carefully orchestrated machinery.
- *
- * Restore the way SIGPIPE is handled to default, which is what we
- * expect.
- */
-static void restore_sigpipe_to_default(void)
-{
-       sigset_t unblock;
-
-       sigemptyset(&unblock);
-       sigaddset(&unblock, SIGPIPE);
-       sigprocmask(SIG_UNBLOCK, &unblock, NULL);
-       signal(SIGPIPE, SIG_DFL);
-}
-
-int main(int argc, char **av)
+int cmd_main(int argc, const char **argv)
 {
-       const char **argv = (const char **) av;
        const char *cmd;
        int done_help = 0;
 
-       cmd = git_extract_argv0_path(argv[0]);
+       cmd = 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();
-
-       restore_sigpipe_to_default();
-
-       git_setup_gettext();
+       else {
+               const char *slash = find_last_dir_sep(cmd);
+               if (slash)
+                       cmd = slash + 1;
+       }
 
        trace_command_performance(argv);
 
@@ -699,8 +684,8 @@ int main(int argc, char **av)
                if (errno != ENOENT)
                        break;
                if (was_alias) {
-                       fprintf(stderr, "Expansion of alias '%s' failed; "
-                               "'%s' is not a git command\n",
+                       fprintf(stderr, _("expansion of alias '%s' failed; "
+                                         "'%s' is not a git command\n"),
                                cmd, argv[0]);
                        exit(1);
                }
@@ -711,7 +696,7 @@ int main(int argc, char **av)
                        break;
        }
 
-       fprintf(stderr, "Failed to run command '%s': %s\n",
+       fprintf(stderr, _("failed to run command '%s': %s\n"),
                cmd, strerror(errno));
 
        return 1;