vcs-svn: use error_errno()
[gitweb.git] / git.c
diff --git a/git.c b/git.c
index 77ef23ece0e9327dc6dc80659bd4165a9f2275d9..6cc0c077f9761f9b56e5fb7e666722b437c4fae4 100644 (file)
--- a/git.c
+++ b/git.c
@@ -25,14 +25,14 @@ static const char *env_names[] = {
        GIT_PREFIX_ENVIRONMENT
 };
 static char *orig_env[4];
-static int saved_env_before_alias;
+static int save_restore_env_balance;
 
 static void save_env_before_alias(void)
 {
        int i;
-       if (saved_env_before_alias)
-               return;
-       saved_env_before_alias = 1;
+
+       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]);
@@ -41,17 +41,25 @@ static void save_env_before_alias(void)
        }
 }
 
-static void restore_env(void)
+static void restore_env(int external_alias)
 {
        int i;
-       if (orig_cwd && chdir(orig_cwd))
+
+       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 (orig_env[i])
+               if (external_alias &&
+                   !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
+                       continue;
+               if (orig_env[i]) {
                        setenv(env_names[i], orig_env[i], 1);
-               else
+                       free(orig_env[i]);
+               } else {
                        unsetenv(env_names[i]);
+               }
        }
 }
 
@@ -239,19 +247,16 @@ static int handle_alias(int *argcp, const char ***argv)
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
                if (alias_string[0] == '!') {
-                       const char **alias_argv;
-                       int argc = *argcp, i;
+                       struct child_process child = CHILD_PROCESS_INIT;
 
                        commit_pager_choice();
+                       restore_env(1);
 
-                       /* build alias_argv */
-                       alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
-                       alias_argv[0] = alias_string + 1;
-                       for (i = 1; i < argc; ++i)
-                               alias_argv[i] = (*argv)[i];
-                       alias_argv[argc] = NULL;
+                       child.use_shell = 1;
+                       argv_array_push(&child.args, alias_string + 1);
+                       argv_array_pushv(&child.args, (*argv) + 1);
 
-                       ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
+                       ret = run_command(&child);
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
 
@@ -291,7 +296,7 @@ static int handle_alias(int *argcp, const char ***argv)
                ret = 1;
        }
 
-       restore_env();
+       restore_env(0);
 
        errno = saved_errno;
 
@@ -368,6 +373,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
 static struct cmd_struct commands[] = {
        { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+       { "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
        { "annotate", cmd_annotate, RUN_SETUP },
        { "apply", cmd_apply, RUN_SETUP_GENTLY },
        { "archive", cmd_archive },
@@ -414,7 +420,7 @@ static struct cmd_struct commands[] = {
        { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
        { "init", cmd_init_db },
        { "init-db", cmd_init_db },
-       { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP },
+       { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
        { "log", cmd_log, RUN_SETUP },
        { "ls-files", cmd_ls_files, RUN_SETUP },
        { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
@@ -443,6 +449,7 @@ static struct cmd_struct commands[] = {
        { "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 },
        { "receive-pack", cmd_receive_pack },
@@ -466,6 +473,7 @@ 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 },
        { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
        { "tag", cmd_tag, RUN_SETUP },
        { "unpack-file", cmd_unpack_file, RUN_SETUP },
@@ -501,21 +509,25 @@ int is_builtin(const char *s)
        return !!get_builtin(s);
 }
 
+#ifdef STRIP_EXTENSION
+static void strip_extension(const char **argv)
+{
+       size_t len;
+
+       if (strip_suffix(argv[0], STRIP_EXTENSION, &len))
+               argv[0] = xmemdupz(argv[0], len);
+}
+#else
+#define strip_extension(cmd)
+#endif
+
 static void handle_builtin(int argc, const char **argv)
 {
-       const char *cmd = argv[0];
-       int i;
-       static const char ext[] = STRIP_EXTENSION;
+       const char *cmd;
        struct cmd_struct *builtin;
 
-       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';
-               }
-       }
+       strip_extension(argv);
+       cmd = argv[0];
 
        /* Turn "git cmd --help" into "git help cmd" */
        if (argc > 1 && !strcmp(argv[1], "--help")) {
@@ -524,16 +536,8 @@ static void handle_builtin(int argc, const char **argv)
        }
 
        builtin = get_builtin(cmd);
-       if (builtin) {
-               /*
-                * XXX: if we can figure out cases where it is _safe_
-                * to do, we can avoid spawning a new process when
-                * saved_env_before_alias is true
-                * (i.e. setup_git_dir* has been run once)
-                */
-               if (!saved_env_before_alias)
-                       exit(run_builtin(builtin, argc, argv));
-       }
+       if (builtin)
+               exit(run_builtin(builtin, argc, argv));
 }
 
 static void execv_dashed_external(const char **argv)
@@ -577,8 +581,17 @@ static int run_argv(int *argcp, const char ***argv)
        int done_alias = 0;
 
        while (1) {
-               /* See if it's a builtin */
-               handle_builtin(*argcp, *argv);
+               /*
+                * If we tried alias and futzed with our environment,
+                * it no longer is safe to invoke builtins directly in
+                * general.  We have to spawn them as dashed externals.
+                *
+                * NEEDSWORK: if we can figure out cases
+                * where it is safe to do, we can avoid spawning a new
+                * process.
+                */
+               if (!done_alias)
+                       handle_builtin(*argcp, *argv);
 
                /* .. then try the external ones */
                execv_dashed_external(*argv);