Documentation: clarify which parameters are optional to git-cherry
[gitweb.git] / git.c
diff --git a/git.c b/git.c
index 59f0fcc1f2278d3234a7e4a306db56c7cfcde9a2..940a498962ceb06a1dba85547d61ad62bb81a499 100644 (file)
--- a/git.c
+++ b/git.c
@@ -9,6 +9,43 @@ const char git_usage_string[] =
 const char git_more_info_string[] =
        "See 'git help COMMAND' for more information on a specific command.";
 
+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;
+       }
+}
+
 static int handle_options(const char*** argv, int* argc, int* envchanged)
 {
        int handled = 0;
@@ -38,9 +75,9 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
                                exit(0);
                        }
                } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
-                       setup_pager();
+                       use_pager = 1;
                } else if (!strcmp(cmd, "--no-pager")) {
-                       setenv("GIT_PAGER", "cat", 1);
+                       use_pager = 0;
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--git-dir")) {
@@ -90,59 +127,6 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
        return handled;
 }
 
-static int split_cmdline(char *cmdline, const char ***argv)
-{
-       int src, dst, count = 0, size = 16;
-       char quoted = 0;
-
-       *argv = xmalloc(sizeof(char*) * size);
-
-       /* 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;
-                               *argv = xrealloc(*argv, sizeof(char*) * size);
-                       }
-                       (*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)
 {
        int envchanged = 0, ret = 0, saved_errno = errno;
@@ -178,6 +162,8 @@ static int handle_alias(int *argcp, const char ***argv)
                            alias_string + 1, alias_command);
                }
                count = split_cmdline(alias_string, &new_argv);
+               if (count < 0)
+                       die("Bad alias.%s string", alias_command);
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
                        die("alias '%s' changes environment variables\n"
@@ -209,8 +195,8 @@ static int handle_alias(int *argcp, const char ***argv)
                ret = 1;
        }
 
-       if (subdir)
-               chdir(subdir);
+       if (subdir && chdir(subdir))
+               die("Cannot change to %s: %s", subdir, strerror(errno));
 
        errno = saved_errno;
 
@@ -242,8 +228,13 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
        prefix = NULL;
        if (p->option & RUN_SETUP)
                prefix = setup_git_directory();
-       if (p->option & USE_PAGER)
-               setup_pager();
+
+       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;
+       commit_pager_choice();
+
        if (p->option & NEED_WORK_TREE)
                setup_work_tree();
 
@@ -275,6 +266,7 @@ 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 },
                { "archive", cmd_archive },
@@ -297,7 +289,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "count-objects", cmd_count_objects, RUN_SETUP },
                { "describe", cmd_describe, RUN_SETUP },
                { "diff", cmd_diff },
-               { "diff-files", cmd_diff_files, RUN_SETUP },
+               { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
                { "diff-index", cmd_diff_index, RUN_SETUP },
                { "diff-tree", cmd_diff_tree, RUN_SETUP },
                { "fast-export", cmd_fast_export, RUN_SETUP },
@@ -324,6 +316,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "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-ours", cmd_merge_ours, RUN_SETUP },
@@ -338,6 +331,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "prune-packed", cmd_prune_packed, RUN_SETUP },
                { "push", cmd_push, RUN_SETUP },
                { "read-tree", cmd_read_tree, RUN_SETUP },
+               { "receive-pack", cmd_receive_pack },
                { "reflog", cmd_reflog, RUN_SETUP },
                { "remote", cmd_remote, RUN_SETUP },
                { "repo-config", cmd_config },
@@ -351,7 +345,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "shortlog", cmd_shortlog, USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
-               { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE | USE_PAGER },
+               { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
                { "tag", cmd_tag, RUN_SETUP },
@@ -369,6 +363,16 @@ static void handle_internal_command(int argc, const char **argv)
                { "pack-refs", cmd_pack_refs, RUN_SETUP },
        };
        int i;
+       static const char ext[] = STRIP_EXTENSION;
+
+       if (sizeof(ext) > 1) {
+               i = strlen(argv[0]) - strlen(ext);
+               if (i > 0 && !strcmp(argv[0] + i, ext)) {
+                       char *argv0 = xstrdup(argv[0]);
+                       argv[0] = cmd = argv0;
+                       argv0[i] = '\0';
+               }
+       }
 
        /* Turn "git cmd --help" into "git help cmd" */
        if (argc > 1 && !strcmp(argv[1], "--help")) {
@@ -384,11 +388,39 @@ static void handle_internal_command(int argc, const char **argv)
        }
 }
 
+static void execv_dashed_external(const char **argv)
+{
+       struct strbuf cmd = STRBUF_INIT;
+       const char *tmp;
+
+       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:");
+
+       /* execvp() can only ever return if it fails */
+       execvp(cmd.buf, (char **)argv);
+
+       trace_printf("trace: exec failed: %s\n", strerror(errno));
+
+       argv[0] = tmp;
+
+       strbuf_release(&cmd);
+}
+
+
 int main(int argc, const char **argv)
 {
-       const char *cmd = argv[0] ? argv[0] : "git-help";
-       char *slash = strrchr(cmd, '/');
-       const char *cmd_path = NULL;
+       const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
+       char *slash = (char *)cmd + strlen(cmd);
        int done_alias = 0;
 
        /*
@@ -396,9 +428,12 @@ int main(int argc, const char **argv)
         * name, and the dirname as the default exec_path
         * if we don't have anything better.
         */
-       if (slash) {
+       do
+               --slash;
+       while (cmd <= slash && !is_dir_sep(*slash));
+       if (cmd <= slash) {
                *slash++ = 0;
-               cmd_path = cmd;
+               git_set_argv0_path(cmd);
                cmd = slash;
        }
 
@@ -423,6 +458,7 @@ 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;
@@ -441,14 +477,14 @@ int main(int argc, const char **argv)
         * environment, and the $(gitexecdir) from the Makefile at build
         * time.
         */
-       setup_path(cmd_path);
+       setup_path();
 
        while (1) {
                /* See if it's an internal command */
                handle_internal_command(argc, argv);
 
                /* .. then try the external ones */
-               execv_git_cmd(argv);
+               execv_dashed_external(argv);
 
                /* It could be an alias -- this works around the insanity
                 * of overriding "git log" with "git show" by having
@@ -466,7 +502,9 @@ int main(int argc, const char **argv)
                                cmd, argv[0]);
                        exit(1);
                }
-               help_unknown_cmd(cmd);
+               argv[0] = help_unknown_cmd(cmd);
+               handle_internal_command(argc, argv);
+               execv_dashed_external(argv);
        }
 
        fprintf(stderr, "Failed to run command '%s': %s\n",