Code

Fix deletion of last character in levenshtein distance
[git.git] / git.c
diff --git a/git.c b/git.c
index 770aadd0a4ad358ec3dc290eed417df1698fd18e..89feb0b6dc9c34902aa7c3c4ac526c646c0e0d84 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")) {
@@ -125,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"
@@ -189,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();
 
@@ -244,7 +288,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 },
@@ -286,6 +330,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 },
@@ -299,7 +344,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 },
@@ -317,6 +362,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")) {
@@ -332,11 +387,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;
 
        /*
@@ -344,9 +427,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;
        }
 
@@ -371,6 +457,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;
@@ -389,14 +476,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
@@ -414,7 +501,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",