commit: allow partial commits with relative paths
[gitweb.git] / git.c
diff --git a/git.c b/git.c
index 7acf290e2385ab35c10db4dd318af51106b12b7b..6b6d9f68e1ea010cb951501cab1fdfa82037b878 100644 (file)
--- a/git.c
+++ b/git.c
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "config.h"
 #include "exec_cmd.h"
 #include "help.h"
 #include "run-command.h"
@@ -16,50 +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]);
-               orig_env[i] = xstrdup_or_null(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) {
@@ -74,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;
@@ -163,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;
@@ -218,6 +201,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        }
                        (*argv)++;
                        (*argc)--;
+               } else if (!strcmp(cmd, "--list-builtins")) {
+                       list_builtins();
+                       exit(0);
                } else {
                        fprintf(stderr, "Unknown option: %s\n", cmd);
                        usage(git_usage_string);
@@ -236,19 +222,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);
@@ -294,8 +279,6 @@ static int handle_alias(int *argcp, const char ***argv)
                ret = 1;
        }
 
-       restore_env(0);
-
        errno = saved_errno;
 
        return ret;
@@ -309,6 +292,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;
@@ -332,7 +317,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;
@@ -343,6 +329,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();
 
@@ -374,7 +365,7 @@ 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 },
@@ -402,6 +393,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 },
@@ -412,7 +404,7 @@ static struct cmd_struct commands[] = {
        { "fsck-objects", cmd_fsck, RUN_SETUP },
        { "gc", cmd_gc, RUN_SETUP },
        { "get-tar-commit-id", cmd_get_tar_commit_id },
-       { "grep", cmd_grep, RUN_SETUP_GENTLY },
+       { "grep", cmd_grep, RUN_SETUP_GENTLY | SUPPORT_SUPER_PREFIX },
        { "hash-object", cmd_hash_object },
        { "help", cmd_help },
        { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
@@ -423,7 +415,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 },
@@ -443,13 +435,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 },
@@ -471,9 +464,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 },
@@ -507,6 +500,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)
 {
@@ -521,57 +521,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)