Merge branch 'sb/rerere-lib'
[gitweb.git] / git.c
diff --git a/git.c b/git.c
index 89b431fa28162adbd0f64d7a036a71b5bfce9eee..7075533aa7243abacf100cb8a8987284d5d144de 100644 (file)
--- a/git.c
+++ b/git.c
@@ -6,6 +6,46 @@
 const char git_usage_string[] =
        "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
 
+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;
@@ -35,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")) {
@@ -239,8 +279,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();
 
@@ -283,9 +328,10 @@ static void handle_internal_command(int argc, const char **argv)
                { "checkout-index", cmd_checkout_index,
                        RUN_SETUP | NEED_WORK_TREE},
                { "check-ref-format", cmd_check_ref_format },
-               { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
+               { "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 },
                { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
@@ -293,7 +339,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 },
+               { "diff-files", cmd_diff_files, RUN_SETUP },
                { "diff-index", cmd_diff_index, RUN_SETUP },
                { "diff-tree", cmd_diff_tree, RUN_SETUP },
                { "fast-export", cmd_fast_export, RUN_SETUP },
@@ -365,6 +411,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 = strdup(argv[0]);
+                       argv[0] = cmd = argv0;
+                       argv0[i] = '\0';
+               }
+       }
 
        /* Turn "git cmd --help" into "git help cmd" */
        if (argc > 1 && !strcmp(argv[1], "--help")) {
@@ -380,10 +436,40 @@ static void handle_internal_command(int argc, const char **argv)
        }
 }
 
+static void execv_dashed_external(const char **argv)
+{
+       struct strbuf cmd;
+       const char *tmp;
+
+       strbuf_init(&cmd, 0);
+       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 = argv[0] && *argv[0] ? argv[0] : "git-help";
+       char *slash = (char *)cmd + strlen(cmd);
        const char *cmd_path = NULL;
        int done_alias = 0;
 
@@ -392,7 +478,10 @@ 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;
                cmd = slash;
@@ -419,6 +508,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;
@@ -426,6 +516,7 @@ int main(int argc, const char **argv)
                /* The user didn't specify a command; give them help */
                printf("usage: %s\n\n", git_usage_string);
                list_common_cmds_help();
+               printf("\n%s\n", git_more_info_string);
                exit(1);
        }
        cmd = argv[0];
@@ -443,7 +534,7 @@ int main(int argc, const char **argv)
                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