Code

Merge branch 'js/exec-error-report'
authorJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2010 22:44:12 +0000 (14:44 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2010 22:44:12 +0000 (14:44 -0800)
* js/exec-error-report:
  Improve error message when a transport helper was not found
  start_command: detect execvp failures early
  run-command: move wait_or_whine earlier
  start_command: report child process setup errors to the parent's stderr

Conflicts:
Makefile

1  2 
Makefile
run-command.c
transport-helper.c

diff --combined Makefile
index d74c1962340597f2f4afc760f09c3b1bd764b4f0,22c1546e2f150d15a659ce4d9bab0c4fafb2f7ba..cf0723ea122b09c66a6da367a653381966729847
+++ b/Makefile
@@@ -187,6 -187,10 +187,6 @@@ all:
  # is a simplified version of the merge sort used in glibc. This is
  # recommended if Git triggers O(n^2) behavior in your platform's qsort().
  #
 -# Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call
 -# your external grep (e.g., if your system lacks grep, if its grep is
 -# broken, or spawning external process is slower than built-in grep git has).
 -#
  # Define UNRELIABLE_FSTAT if your system's fstat does not return the same
  # information on a not yet closed file that lstat would return for the same
  # file after it was closed.
  #   DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
  #   DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
  
 -GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
 +GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
  -include GIT-VERSION-FILE
  
@@@ -420,6 -424,16 +420,6 @@@ BUILT_INS += git-stage$
  BUILT_INS += git-status$X
  BUILT_INS += git-whatchanged$X
  
 -ifdef NO_CURL
 -REMOTE_CURL_PRIMARY =
 -REMOTE_CURL_ALIASES =
 -REMOTE_CURL_NAMES =
 -else
 -REMOTE_CURL_PRIMARY = git-remote-http$X
 -REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
 -REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
 -endif
 -
  # what 'all' will build and 'install' will install in gitexecdir,
  # excluding programs for built-in commands
  ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
  # what 'all' will build but not install in gitexecdir
  OTHER_PROGRAMS = git$X
  
 +# what test wrappers are needed and 'install' will install, in bindir
 +BINDIR_PROGRAMS_NEED_X += git
 +BINDIR_PROGRAMS_NEED_X += git-upload-pack
 +BINDIR_PROGRAMS_NEED_X += git-receive-pack
 +BINDIR_PROGRAMS_NEED_X += git-upload-archive
 +BINDIR_PROGRAMS_NEED_X += git-shell
 +
 +BINDIR_PROGRAMS_NO_X += git-cvsserver
 +
  # Set paths to tools early so that they can be used for version tests.
  ifndef SHELL_PATH
        SHELL_PATH = /bin/sh
@@@ -464,7 -469,6 +464,7 @@@ LIB_H += commit.
  LIB_H += compat/bswap.h
  LIB_H += compat/cygwin.h
  LIB_H += compat/mingw.h
 +LIB_H += compat/win32/pthread.h
  LIB_H += csum-file.h
  LIB_H += decorate.h
  LIB_H += delta.h
@@@ -791,6 -795,7 +791,6 @@@ ifeq ($(uname_S),SunOS
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
        NO_REGEX = YesPlease
 -      NO_EXTERNAL_GREP = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
        ifeq ($(uname_R),5.7)
                NEEDS_RESOLV = YesPlease
@@@ -841,7 -846,6 +841,7 @@@ ifeq ($(uname_O),Cygwin
  endif
  ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
 +      OLD_ICONV = YesPlease
        NO_MEMMEM = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
@@@ -909,6 -913,7 +909,6 @@@ ifeq ($(uname_S),IRIX
        # NO_MMAP.  If you suspect that your compiler is not affected by this
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
 -      NO_EXTERNAL_GREP = UnfortunatelyYes
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH = /usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@@ -928,6 -933,7 +928,6 @@@ ifeq ($(uname_S),IRIX64
        # NO_MMAP.  If you suspect that your compiler is not affected by this
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
 -      NO_EXTERNAL_GREP = UnfortunatelyYes
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@@ -979,16 -985,15 +979,16 @@@ ifeq ($(uname_S),Windows
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
        NO_CURL = YesPlease
 -      NO_PTHREADS = YesPlease
 +      NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
 +      THREADED_DELTA_SEARCH = YesPlease
  
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
        CFLAGS =
        BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
 -      COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o
 -      COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -DSTRIP_EXTENSION=\".exe\"
 +      COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o compat/win32/pthread.o
 +      COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
        BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
        EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
        lib =
@@@ -1030,13 -1035,10 +1030,13 @@@ ifneq (,$(findstring MINGW,$(uname_S))
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
 +      NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
 +      THREADED_DELTA_SEARCH = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
 -      COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
 +      COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
 +              compat/win32/pthread.o
        EXTLIBS += -lws2_32
        X = .exe
  ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
        EXTLIBS += /mingw/lib/libz.a
        NO_R_TO_GCC_LINKER = YesPlease
        INTERNAL_QSORT = YesPlease
 -      THREADED_DELTA_SEARCH = YesPlease
  else
        NO_CURL = YesPlease
 -      NO_PTHREADS = YesPlease
  endif
  endif
  
@@@ -1095,9 -1099,6 +1095,9 @@@ endi
  
  ifdef NO_CURL
        BASIC_CFLAGS += -DNO_CURL
 +      REMOTE_CURL_PRIMARY =
 +      REMOTE_CURL_ALIASES =
 +      REMOTE_CURL_NAMES =
  else
        ifdef CURLDIR
                # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
        else
                CURL_LIBCURL = -lcurl
        endif
 +      REMOTE_CURL_PRIMARY = git-remote-http$X
 +      REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
 +      REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
        PROGRAMS += $(REMOTE_CURL_NAMES) git-http-fetch$X
        curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "070908"
@@@ -1342,6 -1340,9 +1342,6 @@@ endi
  ifdef DIR_HAS_BSD_GROUP_SEMANTICS
        COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
  endif
 -ifdef NO_EXTERNAL_GREP
 -      BASIC_CFLAGS += -DNO_EXTERNAL_GREP
 -endif
  ifdef UNRELIABLE_FSTAT
        BASIC_CFLAGS += -DUNRELIABLE_FSTAT
  endif
@@@ -1475,19 -1476,20 +1475,19 @@@ shell_compatibility_test: please_set_SH
  strip: $(PROGRAMS) git$X
        $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
  
 -git.o: git.c common-cmds.h GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
 -              '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 -              $(ALL_CFLAGS) -o $@ -c $(filter %.c,$^)
 +git.o: common-cmds.h
 +git.s git.o: ALL_CFLAGS += -DGIT_VERSION='"$(GIT_VERSION)"' \
 +      '-DGIT_HTML_PATH="$(htmldir_SQ)"'
  
  git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
  
 -builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
 -              '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 -              '-DGIT_MAN_PATH="$(mandir_SQ)"' \
 -              '-DGIT_INFO_PATH="$(infodir_SQ)"' $<
 +builtin-help.o: common-cmds.h
 +builtin-help.s builtin-help.o: ALL_CFLAGS += \
 +      '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 +      '-DGIT_MAN_PATH="$(mandir_SQ)"' \
 +      '-DGIT_INFO_PATH="$(infodir_SQ)"'
  
  $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
@@@ -1640,26 -1642,30 +1640,26 @@@ git.o git.spec 
  
  %.o: %.c GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
 -%.s: %.c GIT-CFLAGS
 +%.s: %.c GIT-CFLAGS FORCE
        $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
 -%.o: %.S
 +%.o: %.S GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
  
 -exec_cmd.o: exec_cmd.c GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
 -              '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
 -              '-DBINDIR="$(bindir_relative_SQ)"' \
 -              '-DPREFIX="$(prefix_SQ)"' \
 -              $<
 +exec_cmd.s exec_cmd.o: ALL_CFLAGS += \
 +      '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
 +      '-DBINDIR="$(bindir_relative_SQ)"' \
 +      '-DPREFIX="$(prefix_SQ)"'
  
 -builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
 +builtin-init-db.s builtin-init-db.o: ALL_CFLAGS += \
 +      -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
  
 -config.o: config.c GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $<
 +config.s config.o: ALL_CFLAGS += -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
  
 -http.o: http.c GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
 +http.s http.o: ALL_CFLAGS += -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
  
  ifdef NO_EXPAT
 -http-walker.o: http-walker.c http.h GIT-CFLAGS
 -      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
 +http-walker.o: http.h
 +http-walker.s http-walker.o: ALL_CFLAGS += -DNO_EXPAT
  endif
  
  git-%$X: %.o $(GITLIBS)
@@@ -1737,7 -1743,7 +1737,7 @@@ cscope
  TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
               $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
  
 -GIT-CFLAGS: .FORCE-GIT-CFLAGS
 +GIT-CFLAGS: FORCE
        @FLAGS='$(TRACK_CFLAGS)'; \
            if test x"$$FLAGS" != x"`cat GIT-CFLAGS 2>/dev/null`" ; then \
                echo 1>&2 "    * new build flags or prefix"; \
  # We need to apply sq twice, once to protect from the shell
  # that runs GIT-BUILD-OPTIONS, and then again to protect it
  # and the first level quoting from the shell that runs "echo".
 -GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
 +GIT-BUILD-OPTIONS: FORCE
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
        @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
 +      @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
  
  ### Detect Tck/Tk interpreter path changes
  ifndef NO_TCLTK
  TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
  
 -GIT-GUI-VARS: .FORCE-GIT-GUI-VARS
 +GIT-GUI-VARS: FORCE
        @VARS='$(TRACK_VARS)'; \
            if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
                echo 1>&2 "    * new Tcl/Tk interpreter location"; \
                echo "$$VARS" >$@; \
              fi
 -
 -.PHONY: .FORCE-GIT-GUI-VARS
  endif
  
  ### Testing rules
  
 -TEST_PROGRAMS += test-chmtime$X
 -TEST_PROGRAMS += test-ctype$X
 -TEST_PROGRAMS += test-date$X
 -TEST_PROGRAMS += test-delta$X
 -TEST_PROGRAMS += test-dump-cache-tree$X
 -TEST_PROGRAMS += test-genrandom$X
 -TEST_PROGRAMS += test-match-trees$X
 -TEST_PROGRAMS += test-parse-options$X
 -TEST_PROGRAMS += test-path-utils$X
 -TEST_PROGRAMS += test-run-command$X
 -TEST_PROGRAMS += test-sha1$X
 -TEST_PROGRAMS += test-sigchain$X
 -
 -all:: $(TEST_PROGRAMS)
 +TEST_PROGRAMS_NEED_X += test-chmtime
 +TEST_PROGRAMS_NEED_X += test-ctype
 +TEST_PROGRAMS_NEED_X += test-date
 +TEST_PROGRAMS_NEED_X += test-delta
 +TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 +TEST_PROGRAMS_NEED_X += test-genrandom
 +TEST_PROGRAMS_NEED_X += test-match-trees
 +TEST_PROGRAMS_NEED_X += test-parse-options
 +TEST_PROGRAMS_NEED_X += test-path-utils
++TEST_PROGRAMS_NEED_X += test-run-command
 +TEST_PROGRAMS_NEED_X += test-sha1
 +TEST_PROGRAMS_NEED_X += test-sigchain
 +TEST_PROGRAMS_NEED_X += test-index-version
 +
 +TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
 +
 +test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 +
 +all:: $(TEST_PROGRAMS) $(test_bindir_programs)
 +
 +bin-wrappers/%: wrap-for-bin.sh
 +      @mkdir -p bin-wrappers
 +      $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 +           -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
 +           -e 's|@@PROG@@|$(@F)|' < $< > $@ && \
 +      chmod +x $@
  
  # GNU make supports exporting all variables by "export" without parameters.
  # However, the environment gets quite big, and some programs have problems
@@@ -1854,13 -1850,11 +1855,13 @@@ endi
  gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
  export gitexec_instdir
  
 +install_bindir_programs := $(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X)) $(BINDIR_PROGRAMS_NO_X)
 +
  install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 -      $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
  ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
@@@ -1981,7 -1975,6 +1982,7 @@@ clean
                $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
 +      $(RM) -r bin-wrappers
        $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
        $(RM) -r autom4te.cache
        $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
@@@ -2006,7 -1999,8 +2007,7 @@@ endi
  
  .PHONY: all install clean strip
  .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
 -.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
 -.PHONY: .FORCE-GIT-BUILD-OPTIONS
 +.PHONY: FORCE TAGS tags cscope
  
  ### Check documentation
  #
diff --combined run-command.c
index a90984576411d237c1b83b66427e99d9e37ea7c8,efe9fe413887966ced2bb3cd976789ec8789ec4d..2feb493951322617692085998ac8507cdba9dd30
@@@ -8,59 -8,85 +8,131 @@@ static inline void close_pair(int fd[2]
        close(fd[1]);
  }
  
 +#ifndef WIN32
  static inline void dup_devnull(int to)
  {
        int fd = open("/dev/null", O_RDWR);
        dup2(fd, to);
        close(fd);
  }
 +#endif
 +
 +static const char **prepare_shell_cmd(const char **argv)
 +{
 +      int argc, nargc = 0;
 +      const char **nargv;
 +
 +      for (argc = 0; argv[argc]; argc++)
 +              ; /* just counting */
 +      /* +1 for NULL, +3 for "sh -c" plus extra $0 */
 +      nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));
 +
 +      if (argc < 1)
 +              die("BUG: shell command is empty");
 +
 +      if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
 +              nargv[nargc++] = "sh";
 +              nargv[nargc++] = "-c";
 +
 +              if (argc < 2)
 +                      nargv[nargc++] = argv[0];
 +              else {
 +                      struct strbuf arg0 = STRBUF_INIT;
 +                      strbuf_addf(&arg0, "%s \"$@\"", argv[0]);
 +                      nargv[nargc++] = strbuf_detach(&arg0, NULL);
 +              }
 +      }
 +
 +      for (argc = 0; argv[argc]; argc++)
 +              nargv[nargc++] = argv[argc];
 +      nargv[nargc] = NULL;
 +
 +      return nargv;
 +}
 +
 +#ifndef WIN32
 +static int execv_shell_cmd(const char **argv)
 +{
 +      const char **nargv = prepare_shell_cmd(argv);
 +      trace_argv_printf(nargv, "trace: exec:");
 +      execvp(nargv[0], (char **)nargv);
 +      free(nargv);
 +      return -1;
 +}
 +#endif
  
+ #ifndef WIN32
+ static int child_err = 2;
+ static int child_notifier = -1;
+ static void notify_parent(void)
+ {
+       write(child_notifier, "", 1);
+ }
+ static NORETURN void die_child(const char *err, va_list params)
+ {
+       char msg[4096];
+       int len = vsnprintf(msg, sizeof(msg), err, params);
+       if (len > sizeof(msg))
+               len = sizeof(msg);
+       write(child_err, "fatal: ", 7);
+       write(child_err, msg, len);
+       write(child_err, "\n", 1);
+       exit(128);
+ }
+ static inline void set_cloexec(int fd)
+ {
+       int flags = fcntl(fd, F_GETFD);
+       if (flags >= 0)
+               fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ }
+ #endif
+ static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
+ {
+       int status, code = -1;
+       pid_t waiting;
+       int failed_errno = 0;
+       while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
+               ;       /* nothing */
+       if (waiting < 0) {
+               failed_errno = errno;
+               error("waitpid for %s failed: %s", argv0, strerror(errno));
+       } else if (waiting != pid) {
+               error("waitpid is confused (%s)", argv0);
+       } else if (WIFSIGNALED(status)) {
+               code = WTERMSIG(status);
+               error("%s died of signal %d", argv0, code);
+               /*
+                * This return value is chosen so that code & 0xff
+                * mimics the exit code that a POSIX shell would report for
+                * a program that died from this signal.
+                */
+               code -= 128;
+       } else if (WIFEXITED(status)) {
+               code = WEXITSTATUS(status);
+               /*
+                * Convert special exit code when execvp failed.
+                */
+               if (code == 127) {
+                       code = -1;
+                       failed_errno = ENOENT;
+                       if (!silent_exec_failure)
+                               error("cannot run %s: %s", argv0,
+                                       strerror(ENOENT));
+               }
+       } else {
+               error("waitpid is confused (%s)", argv0);
+       }
+       errno = failed_errno;
+       return code;
+ }
  int start_command(struct child_process *cmd)
  {
        int need_in, need_out, need_err;
@@@ -122,9 -148,30 +194,30 @@@ fail_pipe
        trace_argv_printf(cmd->argv, "trace: run_command:");
  
  #ifndef WIN32
+ {
+       int notify_pipe[2];
+       if (pipe(notify_pipe))
+               notify_pipe[0] = notify_pipe[1] = -1;
        fflush(NULL);
        cmd->pid = fork();
        if (!cmd->pid) {
+               /*
+                * Redirect the channel to write syscall error messages to
+                * before redirecting the process's stderr so that all die()
+                * in subsequent call paths use the parent's stderr.
+                */
+               if (cmd->no_stderr || need_err) {
+                       child_err = dup(2);
+                       set_cloexec(child_err);
+               }
+               set_die_routine(die_child);
+               close(notify_pipe[0]);
+               set_cloexec(notify_pipe[1]);
+               child_notifier = notify_pipe[1];
+               atexit(notify_parent);
                if (cmd->no_stdin)
                        dup_devnull(0);
                else if (need_in) {
                                        unsetenv(*cmd->env);
                        }
                }
-               if (cmd->preexec_cb)
+               if (cmd->preexec_cb) {
+                       /*
+                        * We cannot predict what the pre-exec callback does.
+                        * Forgo parent notification.
+                        */
+                       close(child_notifier);
+                       child_notifier = -1;
                        cmd->preexec_cb();
+               }
                if (cmd->git_cmd) {
                        execv_git_cmd(cmd->argv);
 +              } else if (cmd->use_shell) {
 +                      execv_shell_cmd(cmd->argv);
                } else {
                        execvp(cmd->argv[0], (char *const*) cmd->argv);
                }
-               trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0],
-                               strerror(errno));
-               exit(127);
+               /*
+                * Do not check for cmd->silent_exec_failure; the parent
+                * process will check it when it sees this exit code.
+                */
+               if (errno == ENOENT)
+                       exit(127);
+               else
+                       die_errno("cannot exec '%s'", cmd->argv[0]);
        }
        if (cmd->pid < 0)
                error("cannot fork() for %s: %s", cmd->argv[0],
                        strerror(failed_errno = errno));
+       /*
+        * Wait for child's execvp. If the execvp succeeds (or if fork()
+        * failed), EOF is seen immediately by the parent. Otherwise, the
+        * child process sends a single byte.
+        * Note that use of this infrastructure is completely advisory,
+        * therefore, we keep error checks minimal.
+        */
+       close(notify_pipe[1]);
+       if (read(notify_pipe[0], &notify_pipe[1], 1) == 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],
+                             cmd->silent_exec_failure);
+               failed_errno = errno;
+               cmd->pid = -1;
+       }
+       close(notify_pipe[0]);
+ }
  #else
  {
 -      int s0 = -1, s1 = -1, s2 = -1;  /* backups of stdin, stdout, stderr */
 +      int fhin = 0, fhout = 1, fherr = 2;
        const char **sargv = cmd->argv;
        char **env = environ;
  
 -      if (cmd->no_stdin) {
 -              s0 = dup(0);
 -              dup_devnull(0);
 -      } else if (need_in) {
 -              s0 = dup(0);
 -              dup2(fdin[0], 0);
 -      } else if (cmd->in) {
 -              s0 = dup(0);
 -              dup2(cmd->in, 0);
 -      }
 -
 -      if (cmd->no_stderr) {
 -              s2 = dup(2);
 -              dup_devnull(2);
 -      } else if (need_err) {
 -              s2 = dup(2);
 -              dup2(fderr[1], 2);
 -      }
 -
 -      if (cmd->no_stdout) {
 -              s1 = dup(1);
 -              dup_devnull(1);
 -      } else if (cmd->stdout_to_stderr) {
 -              s1 = dup(1);
 -              dup2(2, 1);
 -      } else if (need_out) {
 -              s1 = dup(1);
 -              dup2(fdout[1], 1);
 -      } else if (cmd->out > 1) {
 -              s1 = dup(1);
 -              dup2(cmd->out, 1);
 -      }
 +      if (cmd->no_stdin)
 +              fhin = open("/dev/null", O_RDWR);
 +      else if (need_in)
 +              fhin = dup(fdin[0]);
 +      else if (cmd->in)
 +              fhin = dup(cmd->in);
 +
 +      if (cmd->no_stderr)
 +              fherr = open("/dev/null", O_RDWR);
 +      else if (need_err)
 +              fherr = dup(fderr[1]);
 +
 +      if (cmd->no_stdout)
 +              fhout = open("/dev/null", O_RDWR);
 +      else if (cmd->stdout_to_stderr)
 +              fhout = dup(fherr);
 +      else if (need_out)
 +              fhout = dup(fdout[1]);
 +      else if (cmd->out > 1)
 +              fhout = dup(cmd->out);
  
        if (cmd->dir)
                die("chdir in start_command() not implemented");
  
        if (cmd->git_cmd) {
                cmd->argv = prepare_git_cmd(cmd->argv);
 +      } else if (cmd->use_shell) {
 +              cmd->argv = prepare_shell_cmd(cmd->argv);
        }
  
 -      cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
 +      cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env,
 +                                fhin, fhout, fherr);
        failed_errno = errno;
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
                error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
                free(cmd->argv);
  
        cmd->argv = sargv;
 -      if (s0 >= 0)
 -              dup2(s0, 0), close(s0);
 -      if (s1 >= 0)
 -              dup2(s1, 1), close(s1);
 -      if (s2 >= 0)
 -              dup2(s2, 2), close(s2);
 +      if (fhin != 0)
 +              close(fhin);
 +      if (fhout != 1)
 +              close(fhout);
 +      if (fherr != 2)
 +              close(fherr);
  }
  #endif
  
        return 0;
  }
  
- static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
- {
-       int status, code = -1;
-       pid_t waiting;
-       int failed_errno = 0;
-       while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
-               ;       /* nothing */
-       if (waiting < 0) {
-               failed_errno = errno;
-               error("waitpid for %s failed: %s", argv0, strerror(errno));
-       } else if (waiting != pid) {
-               error("waitpid is confused (%s)", argv0);
-       } else if (WIFSIGNALED(status)) {
-               code = WTERMSIG(status);
-               error("%s died of signal %d", argv0, code);
-               /*
-                * This return value is chosen so that code & 0xff
-                * mimics the exit code that a POSIX shell would report for
-                * a program that died from this signal.
-                */
-               code -= 128;
-       } else if (WIFEXITED(status)) {
-               code = WEXITSTATUS(status);
-               /*
-                * Convert special exit code when execvp failed.
-                */
-               if (code == 127) {
-                       code = -1;
-                       failed_errno = ENOENT;
-                       if (!silent_exec_failure)
-                               error("cannot run %s: %s", argv0,
-                                       strerror(ENOENT));
-               }
-       } else {
-               error("waitpid is confused (%s)", argv0);
-       }
-       errno = failed_errno;
-       return code;
- }
  int finish_command(struct child_process *cmd)
  {
        return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure);
@@@ -336,7 -382,6 +421,7 @@@ static void prepare_run_command_v_opt(s
        cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
        cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
        cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
 +      cmd->use_shell = opt & RUN_USING_SHELL ? 1 : 0;
  }
  
  int run_command_v_opt(const char **argv, int opt)
diff --combined transport-helper.c
index fdf22562201d12d1321baae9157a0ca70053a7cb,7dce4a4cac50a2612ddbe22c137a7260534a4696..107742891f6e68d4b103f61a425a585770b0d155
@@@ -102,6 -102,7 +102,7 @@@ static struct child_process *get_helper
        int refspec_nr = 0;
        int refspec_alloc = 0;
        int duped;
+       int code;
  
        if (data->helper)
                return data->helper;
        helper->out = -1;
        helper->err = 0;
        helper->argv = xcalloc(4, sizeof(*helper->argv));
-       strbuf_addf(&buf, "remote-%s", data->name);
+       strbuf_addf(&buf, "git-remote-%s", data->name);
        helper->argv[0] = strbuf_detach(&buf, NULL);
        helper->argv[1] = transport->remote->name;
        helper->argv[2] = remove_ext_force(transport->url);
-       helper->git_cmd = 1;
-       if (start_command(helper))
-               die("Unable to run helper: git %s", helper->argv[0]);
+       helper->git_cmd = 0;
+       helper->silent_exec_failure = 1;
+       code = start_command(helper);
+       if (code < 0 && errno == ENOENT)
+               die("Unable to find remote helper for '%s'", data->name);
+       else if (code != 0)
+               exit(code);
        data->helper = helper;
        data->no_disconnect_req = 0;
  
@@@ -273,7 -279,7 +279,7 @@@ static void standard_options(struct tra
        char buf[16];
        int n;
        int v = t->verbose;
 -      int no_progress = v < 0 || (!t->progress && !isatty(1));
 +      int no_progress = v < 0 || (!t->progress && !isatty(2));
  
        set_helper_option(t, "progress", !no_progress ? "true" : "false");
  
@@@ -528,27 -534,24 +534,27 @@@ static int push_refs(struct transport *
                return transport->push_refs(transport, remote_refs, flags);
        }
  
 -      if (!remote_refs)
 +      if (!remote_refs) {
 +              fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
 +                      "Perhaps you should specify a branch such as 'master'.\n");
                return 0;
 +      }
  
        helper = get_helper(transport);
        if (!data->push)
                return 1;
  
        for (ref = remote_refs; ref; ref = ref->next) {
 -              if (ref->peer_ref)
 -                      hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
 -              else if (!mirror)
 +              if (!ref->peer_ref && !mirror)
                        continue;
  
 -              ref->deletion = is_null_sha1(ref->new_sha1);
 -              if (!ref->deletion &&
 -                      !hashcmp(ref->old_sha1, ref->new_sha1)) {
 -                      ref->status = REF_STATUS_UPTODATE;
 +              /* Check for statuses set by set_ref_status_for_push() */
 +              switch (ref->status) {
 +              case REF_STATUS_REJECT_NONFASTFORWARD:
 +              case REF_STATUS_UPTODATE:
                        continue;
 +              default:
 +                      ; /* do nothing */
                }
  
                if (force_all)
                        continue;
                }
  
 +              if (ref->status != REF_STATUS_NONE) {
 +                      /*
 +                       * Earlier, the ref was marked not to be pushed, so ignore the ref
 +                       * status reported by the remote helper if the latter is 'no match'.
 +                       */
 +                      if (status == REF_STATUS_NONE)
 +                              continue;
 +              }
 +
                ref->status = status;
                ref->remote_status = msg;
        }