Merge branch 'nd/clear-gitenv-upon-use-of-alias'
authorJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2016 19:43:26 +0000 (11:43 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2016 19:43:26 +0000 (11:43 -0800)
d95138e6 (setup: set env $GIT_WORK_TREE when work tree is set, like
$GIT_DIR, 2015-06-26) attempted to work around a glitch in alias
handling by overwriting GIT_WORK_TREE environment variable to
affect subprocesses when set_git_work_tree() gets called, which
resulted in a rather unpleasant regression to "clone" and "init".
Try to address the same issue by always restoring the environment
and respawning the real underlying command when handling alias.

* nd/clear-gitenv-upon-use-of-alias:
run-command: don't warn on SIGPIPE deaths
git.c: make sure we do not leak GIT_* to alias scripts
setup.c: re-fix d95138e (setup: set env $GIT_WORK_TREE when ..
git.c: make it clear save_env() is for alias handling only

1  2 
git.c
run-command.c
t/t0001-init.sh
t/t0002-gitfile.sh
t/t5601-clone.sh
diff --combined git.c
index 6ed824cacfccddcb01104835b45ab934ff3f443e,98d441220a29f2b660a0bb37412ff1ec98453412..da278c3d41ed308581ffec46e3487e7670898d41
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -25,14 -25,14 +25,14 @@@ static const char *env_names[] = 
        GIT_PREFIX_ENVIRONMENT
  };
  static char *orig_env[4];
- static int saved_environment;
+ static int saved_env_before_alias;
  
- static void save_env(void)
+ static void save_env_before_alias(void)
  {
        int i;
-       if (saved_environment)
+       if (saved_env_before_alias)
                return;
-       saved_environment = 1;
+       saved_env_before_alias = 1;
        orig_cwd = xgetcwd();
        for (i = 0; i < ARRAY_SIZE(env_names); i++) {
                orig_env[i] = getenv(env_names[i]);
        }
  }
  
- static void restore_env(void)
+ static void restore_env(int external_alias)
  {
        int i;
-       if (orig_cwd && chdir(orig_cwd))
+       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);
                else
@@@ -226,14 -229,14 +229,14 @@@ static int handle_options(const char **
  static int handle_alias(int *argcp, const char ***argv)
  {
        int envchanged = 0, ret = 0, saved_errno = errno;
-       const char *subdir;
        int count, option_count;
        const char **new_argv;
        const char *alias_command;
        char *alias_string;
        int unused_nongit;
  
-       subdir = setup_git_directory_gently(&unused_nongit);
+       save_env_before_alias();
+       setup_git_directory_gently(&unused_nongit);
  
        alias_command = (*argv)[0];
        alias_string = alias_lookup(alias_command);
                        int argc = *argcp, i;
  
                        commit_pager_choice();
+                       restore_env(1);
  
                        /* build alias_argv */
                        alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
                ret = 1;
        }
  
-       if (subdir && chdir(subdir))
-               die_errno("Cannot change to '%s'", subdir);
+       restore_env(0);
  
        errno = saved_errno;
  
   * RUN_SETUP for reading from the configuration file.
   */
  #define NEED_WORK_TREE                (1<<3)
- #define NO_SETUP              (1<<4)
  
  struct cmd_struct {
        const char *cmd;
@@@ -370,7 -372,6 +372,7 @@@ static int run_builtin(struct cmd_struc
  
  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 },
        { "cherry", cmd_cherry, RUN_SETUP },
        { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
        { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
-       { "clone", cmd_clone, NO_SETUP },
+       { "clone", cmd_clone },
        { "column", cmd_column, RUN_SETUP_GENTLY },
        { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
        { "commit-tree", cmd_commit_tree, RUN_SETUP },
        { "hash-object", cmd_hash_object },
        { "help", cmd_help },
        { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
-       { "init", cmd_init_db, NO_SETUP },
-       { "init-db", cmd_init_db, NO_SETUP },
+       { "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 },
        { "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 },
        { "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 },
@@@ -530,9 -529,13 +532,13 @@@ static void handle_builtin(int argc, co
  
        builtin = get_builtin(cmd);
        if (builtin) {
-               if (saved_environment && (builtin->option & NO_SETUP))
-                       restore_env();
-               else
+               /*
+                * 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));
        }
  }
@@@ -590,7 -593,6 +596,6 @@@ static int run_argv(int *argcp, const c
                 */
                if (done_alias)
                        break;
-               save_env();
                if (!handle_alias(argcp, argv))
                        break;
                done_alias = 1;
diff --combined run-command.c
index 51fd72c4273c3ee59246285e40c9a5f9bd0e2f98,a573ab88d0dc731a89a84a1708d0638bdfa00549..cdf01845790849f863aef4195a68e12fa19ca543
@@@ -3,8 -3,6 +3,8 @@@
  #include "exec_cmd.h"
  #include "sigchain.h"
  #include "argv-array.h"
 +#include "thread-utils.h"
 +#include "strbuf.h"
  
  void child_process_init(struct child_process *child)
  {
        argv_array_init(&child->env_array);
  }
  
 +void child_process_clear(struct child_process *child)
 +{
 +      argv_array_clear(&child->args);
 +      argv_array_clear(&child->env_array);
 +}
 +
  struct child_to_clean {
        pid_t pid;
        struct child_to_clean *next;
  static struct child_to_clean *children_to_clean;
  static int installed_child_cleanup_handler;
  
 -static void cleanup_children(int sig)
 +static void cleanup_children(int sig, int in_signal)
  {
        while (children_to_clean) {
                struct child_to_clean *p = children_to_clean;
                children_to_clean = p->next;
                kill(p->pid, sig);
 -              free(p);
 +              if (!in_signal)
 +                      free(p);
        }
  }
  
  static void cleanup_children_on_signal(int sig)
  {
 -      cleanup_children(sig);
 +      cleanup_children(sig, 1);
        sigchain_pop(sig);
        raise(sig);
  }
  
  static void cleanup_children_on_exit(void)
  {
 -      cleanup_children(SIGTERM);
 +      cleanup_children(SIGTERM, 0);
  }
  
  static void mark_child_for_cleanup(pid_t pid)
@@@ -229,7 -220,7 +229,7 @@@ static inline void set_cloexec(int fd
                fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
  }
  
 -static int wait_or_whine(pid_t pid, const char *argv0)
 +static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
  {
        int status, code = -1;
        pid_t waiting;
  
        while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
                ;       /* nothing */
 +      if (in_signal)
 +              return 0;
  
        if (waiting < 0) {
                failed_errno = errno;
                error("waitpid is confused (%s)", argv0);
        } else if (WIFSIGNALED(status)) {
                code = WTERMSIG(status);
-               if (code != SIGINT && code != SIGQUIT)
+               if (code != SIGINT && code != SIGQUIT && code != SIGPIPE)
                        error("%s died of signal %d", argv0, code);
                /*
                 * This return value is chosen so that code & 0xff
@@@ -335,7 -324,8 +335,7 @@@ int start_command(struct child_process 
  fail_pipe:
                        error("cannot create %s pipe for %s: %s",
                                str, cmd->argv[0], strerror(failed_errno));
 -                      argv_array_clear(&cmd->args);
 -                      argv_array_clear(&cmd->env_array);
 +                      child_process_clear(cmd);
                        errno = failed_errno;
                        return -1;
                }
                 * At this point we know that fork() succeeded, but execvp()
                 * failed. Errors have been reported to our stderr.
                 */
 -              wait_or_whine(cmd->pid, cmd->argv[0]);
 +              wait_or_whine(cmd->pid, cmd->argv[0], 0);
                failed_errno = errno;
                cmd->pid = -1;
        }
                        close_pair(fderr);
                else if (cmd->err)
                        close(cmd->err);
 -              argv_array_clear(&cmd->args);
 -              argv_array_clear(&cmd->env_array);
 +              child_process_clear(cmd);
                errno = failed_errno;
                return -1;
        }
  
  int finish_command(struct child_process *cmd)
  {
 -      int ret = wait_or_whine(cmd->pid, cmd->argv[0]);
 -      argv_array_clear(&cmd->args);
 -      argv_array_clear(&cmd->env_array);
 +      int ret = wait_or_whine(cmd->pid, cmd->argv[0], 0);
 +      child_process_clear(cmd);
        return ret;
  }
  
 +int finish_command_in_signal(struct child_process *cmd)
 +{
 +      return wait_or_whine(cmd->pid, cmd->argv[0], 1);
 +}
 +
 +
  int run_command(struct child_process *cmd)
  {
        int code;
@@@ -609,7 -595,7 +609,7 @@@ static NORETURN void die_async(const ch
  {
        vreportf("fatal: ", err, params);
  
 -      if (!pthread_equal(main_thread, pthread_self())) {
 +      if (in_async()) {
                struct async *async = pthread_getspecific(async_key);
                if (async->proc_in >= 0)
                        close(async->proc_in);
@@@ -628,13 -614,6 +628,13 @@@ static int async_die_is_recursing(void
        return ret != NULL;
  }
  
 +int in_async(void)
 +{
 +      if (!main_thread_set)
 +              return 0; /* no asyncs started yet */
 +      return !pthread_equal(main_thread, pthread_self());
 +}
 +
  #else
  
  static struct {
@@@ -674,12 -653,6 +674,12 @@@ int git_atexit(void (*handler)(void)
  }
  #define atexit git_atexit
  
 +static int process_is_async;
 +int in_async(void)
 +{
 +      return process_is_async;
 +}
 +
  #endif
  
  int start_async(struct async *async)
                if (need_out)
                        close(fdout[0]);
                git_atexit_clear();
 +              process_is_async = 1;
                exit(!!async->proc(proc_in, proc_out, async->data));
        }
  
@@@ -800,7 -772,7 +800,7 @@@ error
  int finish_async(struct async *async)
  {
  #ifdef NO_PTHREADS
 -      return wait_or_whine(async->pid, "child process");
 +      return wait_or_whine(async->pid, "child process", 0);
  #else
        void *ret = (void *)(intptr_t)(-1);
  
  
  const char *find_hook(const char *name)
  {
 -      const char *path = git_path("hooks/%s", name);
 -      if (access(path, X_OK) < 0)
 -              path = NULL;
 +      static struct strbuf path = STRBUF_INIT;
  
 -      return path;
 +      strbuf_reset(&path);
 +      strbuf_git_path(&path, "hooks/%s", name);
 +      if (access(path.buf, X_OK) < 0)
 +              return NULL;
 +      return path.buf;
  }
  
  int run_hook_ve(const char *const *env, const char *name, va_list args)
@@@ -867,336 -837,3 +867,336 @@@ int capture_command(struct child_proces
        close(cmd->out);
        return finish_command(cmd);
  }
 +
 +enum child_state {
 +      GIT_CP_FREE,
 +      GIT_CP_WORKING,
 +      GIT_CP_WAIT_CLEANUP,
 +};
 +
 +struct parallel_processes {
 +      void *data;
 +
 +      int max_processes;
 +      int nr_processes;
 +
 +      get_next_task_fn get_next_task;
 +      start_failure_fn start_failure;
 +      task_finished_fn task_finished;
 +
 +      struct {
 +              enum child_state state;
 +              struct child_process process;
 +              struct strbuf err;
 +              void *data;
 +      } *children;
 +      /*
 +       * The struct pollfd is logically part of *children,
 +       * but the system call expects it as its own array.
 +       */
 +      struct pollfd *pfd;
 +
 +      unsigned shutdown : 1;
 +
 +      int output_owner;
 +      struct strbuf buffered_output; /* of finished children */
 +};
 +
 +static int default_start_failure(struct child_process *cp,
 +                               struct strbuf *err,
 +                               void *pp_cb,
 +                               void *pp_task_cb)
 +{
 +      int i;
 +
 +      strbuf_addstr(err, "Starting a child failed:");
 +      for (i = 0; cp->argv[i]; i++)
 +              strbuf_addf(err, " %s", cp->argv[i]);
 +
 +      return 0;
 +}
 +
 +static int default_task_finished(int result,
 +                               struct child_process *cp,
 +                               struct strbuf *err,
 +                               void *pp_cb,
 +                               void *pp_task_cb)
 +{
 +      int i;
 +
 +      if (!result)
 +              return 0;
 +
 +      strbuf_addf(err, "A child failed with return code %d:", result);
 +      for (i = 0; cp->argv[i]; i++)
 +              strbuf_addf(err, " %s", cp->argv[i]);
 +
 +      return 0;
 +}
 +
 +static void kill_children(struct parallel_processes *pp, int signo)
 +{
 +      int i, n = pp->max_processes;
 +
 +      for (i = 0; i < n; i++)
 +              if (pp->children[i].state == GIT_CP_WORKING)
 +                      kill(pp->children[i].process.pid, signo);
 +}
 +
 +static struct parallel_processes *pp_for_signal;
 +
 +static void handle_children_on_signal(int signo)
 +{
 +      kill_children(pp_for_signal, signo);
 +      sigchain_pop(signo);
 +      raise(signo);
 +}
 +
 +static void pp_init(struct parallel_processes *pp,
 +                  int n,
 +                  get_next_task_fn get_next_task,
 +                  start_failure_fn start_failure,
 +                  task_finished_fn task_finished,
 +                  void *data)
 +{
 +      int i;
 +
 +      if (n < 1)
 +              n = online_cpus();
 +
 +      pp->max_processes = n;
 +
 +      trace_printf("run_processes_parallel: preparing to run up to %d tasks", n);
 +
 +      pp->data = data;
 +      if (!get_next_task)
 +              die("BUG: you need to specify a get_next_task function");
 +      pp->get_next_task = get_next_task;
 +
 +      pp->start_failure = start_failure ? start_failure : default_start_failure;
 +      pp->task_finished = task_finished ? task_finished : default_task_finished;
 +
 +      pp->nr_processes = 0;
 +      pp->output_owner = 0;
 +      pp->shutdown = 0;
 +      pp->children = xcalloc(n, sizeof(*pp->children));
 +      pp->pfd = xcalloc(n, sizeof(*pp->pfd));
 +      strbuf_init(&pp->buffered_output, 0);
 +
 +      for (i = 0; i < n; i++) {
 +              strbuf_init(&pp->children[i].err, 0);
 +              child_process_init(&pp->children[i].process);
 +              pp->pfd[i].events = POLLIN | POLLHUP;
 +              pp->pfd[i].fd = -1;
 +      }
 +
 +      pp_for_signal = pp;
 +      sigchain_push_common(handle_children_on_signal);
 +}
 +
 +static void pp_cleanup(struct parallel_processes *pp)
 +{
 +      int i;
 +
 +      trace_printf("run_processes_parallel: done");
 +      for (i = 0; i < pp->max_processes; i++) {
 +              strbuf_release(&pp->children[i].err);
 +              child_process_clear(&pp->children[i].process);
 +      }
 +
 +      free(pp->children);
 +      free(pp->pfd);
 +
 +      /*
 +       * When get_next_task added messages to the buffer in its last
 +       * iteration, the buffered output is non empty.
 +       */
 +      fputs(pp->buffered_output.buf, stderr);
 +      strbuf_release(&pp->buffered_output);
 +
 +      sigchain_pop_common();
 +}
 +
 +/* returns
 + *  0 if a new task was started.
 + *  1 if no new jobs was started (get_next_task ran out of work, non critical
 + *    problem with starting a new command)
 + * <0 no new job was started, user wishes to shutdown early. Use negative code
 + *    to signal the children.
 + */
 +static int pp_start_one(struct parallel_processes *pp)
 +{
 +      int i, code;
 +
 +      for (i = 0; i < pp->max_processes; i++)
 +              if (pp->children[i].state == GIT_CP_FREE)
 +                      break;
 +      if (i == pp->max_processes)
 +              die("BUG: bookkeeping is hard");
 +
 +      code = pp->get_next_task(&pp->children[i].process,
 +                               &pp->children[i].err,
 +                               pp->data,
 +                               &pp->children[i].data);
 +      if (!code) {
 +              strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
 +              strbuf_reset(&pp->children[i].err);
 +              return 1;
 +      }
 +      pp->children[i].process.err = -1;
 +      pp->children[i].process.stdout_to_stderr = 1;
 +      pp->children[i].process.no_stdin = 1;
 +
 +      if (start_command(&pp->children[i].process)) {
 +              code = pp->start_failure(&pp->children[i].process,
 +                                       &pp->children[i].err,
 +                                       pp->data,
 +                                       &pp->children[i].data);
 +              strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
 +              strbuf_reset(&pp->children[i].err);
 +              if (code)
 +                      pp->shutdown = 1;
 +              return code;
 +      }
 +
 +      pp->nr_processes++;
 +      pp->children[i].state = GIT_CP_WORKING;
 +      pp->pfd[i].fd = pp->children[i].process.err;
 +      return 0;
 +}
 +
 +static void pp_buffer_stderr(struct parallel_processes *pp, int output_timeout)
 +{
 +      int i;
 +
 +      while ((i = poll(pp->pfd, pp->max_processes, output_timeout)) < 0) {
 +              if (errno == EINTR)
 +                      continue;
 +              pp_cleanup(pp);
 +              die_errno("poll");
 +      }
 +
 +      /* Buffer output from all pipes. */
 +      for (i = 0; i < pp->max_processes; i++) {
 +              if (pp->children[i].state == GIT_CP_WORKING &&
 +                  pp->pfd[i].revents & (POLLIN | POLLHUP)) {
 +                      int n = strbuf_read_once(&pp->children[i].err,
 +                                               pp->children[i].process.err, 0);
 +                      if (n == 0) {
 +                              close(pp->children[i].process.err);
 +                              pp->children[i].state = GIT_CP_WAIT_CLEANUP;
 +                      } else if (n < 0)
 +                              if (errno != EAGAIN)
 +                                      die_errno("read");
 +              }
 +      }
 +}
 +
 +static void pp_output(struct parallel_processes *pp)
 +{
 +      int i = pp->output_owner;
 +      if (pp->children[i].state == GIT_CP_WORKING &&
 +          pp->children[i].err.len) {
 +              fputs(pp->children[i].err.buf, stderr);
 +              strbuf_reset(&pp->children[i].err);
 +      }
 +}
 +
 +static int pp_collect_finished(struct parallel_processes *pp)
 +{
 +      int i, code;
 +      int n = pp->max_processes;
 +      int result = 0;
 +
 +      while (pp->nr_processes > 0) {
 +              for (i = 0; i < pp->max_processes; i++)
 +                      if (pp->children[i].state == GIT_CP_WAIT_CLEANUP)
 +                              break;
 +              if (i == pp->max_processes)
 +                      break;
 +
 +              code = finish_command(&pp->children[i].process);
 +
 +              code = pp->task_finished(code, &pp->children[i].process,
 +                                       &pp->children[i].err, pp->data,
 +                                       &pp->children[i].data);
 +
 +              if (code)
 +                      result = code;
 +              if (code < 0)
 +                      break;
 +
 +              pp->nr_processes--;
 +              pp->children[i].state = GIT_CP_FREE;
 +              pp->pfd[i].fd = -1;
 +              child_process_init(&pp->children[i].process);
 +
 +              if (i != pp->output_owner) {
 +                      strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
 +                      strbuf_reset(&pp->children[i].err);
 +              } else {
 +                      fputs(pp->children[i].err.buf, stderr);
 +                      strbuf_reset(&pp->children[i].err);
 +
 +                      /* Output all other finished child processes */
 +                      fputs(pp->buffered_output.buf, stderr);
 +                      strbuf_reset(&pp->buffered_output);
 +
 +                      /*
 +                       * Pick next process to output live.
 +                       * NEEDSWORK:
 +                       * For now we pick it randomly by doing a round
 +                       * robin. Later we may want to pick the one with
 +                       * the most output or the longest or shortest
 +                       * running process time.
 +                       */
 +                      for (i = 0; i < n; i++)
 +                              if (pp->children[(pp->output_owner + i) % n].state == GIT_CP_WORKING)
 +                                      break;
 +                      pp->output_owner = (pp->output_owner + i) % n;
 +              }
 +      }
 +      return result;
 +}
 +
 +int run_processes_parallel(int n,
 +                         get_next_task_fn get_next_task,
 +                         start_failure_fn start_failure,
 +                         task_finished_fn task_finished,
 +                         void *pp_cb)
 +{
 +      int i, code;
 +      int output_timeout = 100;
 +      int spawn_cap = 4;
 +      struct parallel_processes pp;
 +
 +      pp_init(&pp, n, get_next_task, start_failure, task_finished, pp_cb);
 +      while (1) {
 +              for (i = 0;
 +                  i < spawn_cap && !pp.shutdown &&
 +                  pp.nr_processes < pp.max_processes;
 +                  i++) {
 +                      code = pp_start_one(&pp);
 +                      if (!code)
 +                              continue;
 +                      if (code < 0) {
 +                              pp.shutdown = 1;
 +                              kill_children(&pp, -code);
 +                      }
 +                      break;
 +              }
 +              if (!pp.nr_processes)
 +                      break;
 +              pp_buffer_stderr(&pp, output_timeout);
 +              pp_output(&pp);
 +              code = pp_collect_finished(&pp);
 +              if (code) {
 +                      pp.shutdown = 1;
 +                      if (code < 0)
 +                              kill_children(&pp, -code);
 +              }
 +      }
 +
 +      pp_cleanup(&pp);
 +      return 0;
 +}
diff --combined t/t0001-init.sh
index f91bbcfc853a47e0a314160b79fc4816178f1723,f7c00f6b12c166ac563775af75005a91c49b2f02..295aa5949a13baeb8cafff0a6782f6e49e68a075
@@@ -87,6 -87,23 +87,23 @@@ test_expect_success 'plain nested in ba
        check_config bare-ancestor-aliased.git/plain-nested/.git false unset
  '
  
+ test_expect_success 'No extra GIT_* on alias scripts' '
+       (
+               env | sed -ne "/^GIT_/s/=.*//p" &&
+               echo GIT_PREFIX &&        # setup.c
+               echo GIT_TEXTDOMAINDIR    # wrapper-for-bin.sh
+       ) | sort | uniq >expected &&
+       cat <<-\EOF >script &&
+       #!/bin/sh
+       env | sed -ne "/^GIT_/s/=.*//p" | sort >actual
+       exit 0
+       EOF
+       chmod 755 script &&
+       git config alias.script \!./script &&
+       ( mkdir sub && cd sub && git script ) &&
+       test_cmp expected actual
+ '
  test_expect_success 'plain with GIT_WORK_TREE' '
        mkdir plain-wt &&
        test_must_fail env GIT_WORK_TREE="$(pwd)/plain-wt" git init plain-wt
@@@ -202,8 -219,8 +219,8 @@@ test_expect_success 'init honors globa
        x$(git config -f shared-honor-global/.git/config core.sharedRepository)
  '
  
 -test_expect_success 'init rejects insanely long --template' '
 -      test_must_fail git init --template=$(printf "x%09999dx" 1) test
 +test_expect_success 'init allows insanely long --template' '
 +      git init --template=$(printf "x%09999dx" 1) test
  '
  
  test_expect_success 'init creates a new directory' '
diff --combined t/t0002-gitfile.sh
index 3afe0125c99f037b9f144806c00435dcba3f93ea,9393322c3e7028ea7074d2b1ec50fbb293c6728f..9670e8cbe6cb9a9faa3519b0f11dc16713496188
@@@ -99,7 -99,7 +99,7 @@@ test_expect_success 'check rev-list' 
        test "$SHA" = "$(git rev-list HEAD)"
  '
  
- test_expect_failure 'setup_git_dir twice in subdir' '
+ test_expect_success 'setup_git_dir twice in subdir' '
        git init sgd &&
        (
                cd sgd &&
        )
  '
  
 +test_expect_success 'enter_repo non-strict mode' '
 +      test_create_repo enter_repo &&
 +      (
 +              cd enter_repo &&
 +              test_tick &&
 +              test_commit foo &&
 +              mv .git .realgit &&
 +              echo "gitdir: .realgit" >.git
 +      ) &&
 +      git ls-remote enter_repo >actual &&
 +      cat >expected <<-\EOF &&
 +      946e985ab20de757ca5b872b16d64e92ff3803a9        HEAD
 +      946e985ab20de757ca5b872b16d64e92ff3803a9        refs/heads/master
 +      946e985ab20de757ca5b872b16d64e92ff3803a9        refs/tags/foo
 +      EOF
 +      test_cmp expected actual
 +'
 +
 +test_expect_success 'enter_repo linked checkout' '
 +      (
 +              cd enter_repo &&
 +              git worktree add  ../foo refs/tags/foo
 +      ) &&
 +      git ls-remote foo >actual &&
 +      cat >expected <<-\EOF &&
 +      946e985ab20de757ca5b872b16d64e92ff3803a9        HEAD
 +      946e985ab20de757ca5b872b16d64e92ff3803a9        refs/heads/master
 +      946e985ab20de757ca5b872b16d64e92ff3803a9        refs/tags/foo
 +      EOF
 +      test_cmp expected actual
 +'
 +
 +test_expect_success 'enter_repo strict mode' '
 +      git ls-remote --upload-pack="git upload-pack --strict" foo/.git >actual &&
 +      cat >expected <<-\EOF &&
 +      946e985ab20de757ca5b872b16d64e92ff3803a9        HEAD
 +      946e985ab20de757ca5b872b16d64e92ff3803a9        refs/heads/master
 +      946e985ab20de757ca5b872b16d64e92ff3803a9        refs/tags/foo
 +      EOF
 +      test_cmp expected actual
 +'
 +
  test_done
diff --combined t/t5601-clone.sh
index 9b34f3c615df5080085b5f8cf956d2bac099d9d4,fce3471d1e4e93d61377bab076e30376864df4ca..31b46582d79889c4d85879e7dedfc2c618b274d1
@@@ -65,6 -65,29 +65,29 @@@ test_expect_success 'clone respects GIT
  
  '
  
+ test_expect_success 'clone from hooks' '
+       test_create_repo r0 &&
+       cd r0 &&
+       test_commit initial &&
+       cd .. &&
+       git init r1 &&
+       cd r1 &&
+       cat >.git/hooks/pre-commit <<-\EOF &&
+       #!/bin/sh
+       git clone ../r0 ../r2
+       exit 1
+       EOF
+       chmod u+x .git/hooks/pre-commit &&
+       : >file &&
+       git add file &&
+       test_must_fail git commit -m invoke-hook &&
+       cd .. &&
+       test_cmp r0/.git/HEAD r2/.git/HEAD &&
+       test_cmp r0/initial.t r2/initial.t
+ '
  test_expect_success 'clone creates intermediate directories' '
  
        git clone src long/path/to/dst &&
@@@ -496,11 -519,4 +519,11 @@@ test_expect_success 'shallow clone loca
        ( cd ddsstt && git fsck )
  '
  
 +test_expect_success 'GIT_TRACE_PACKFILE produces a usable pack' '
 +      rm -rf dst.git &&
 +      GIT_TRACE_PACKFILE=$PWD/tmp.pack git clone --no-local --bare src dst.git &&
 +      git init --bare replay.git &&
 +      git -C replay.git index-pack -v --stdin <tmp.pack
 +'
 +
  test_done