Merge branch 'jk/argv-array' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 26 Oct 2011 23:13:31 +0000 (16:13 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 26 Oct 2011 23:13:31 +0000 (16:13 -0700)
* jk/argv-array:
run_hook: use argv_array API
checkout: use argv_array API
bisect: use argv_array API
quote: provide sq_dequote_to_argv_array
refactor argv_array into generic code
quote.h: fix bogus comment
add sha1_array API docs

1  2 
Makefile
bisect.c
builtin/checkout.c
quote.c
run-command.c
submodule.c
diff --combined Makefile
index 924749edae3b515b31a48bb3db85f44a40529b9d,ec7d06c00cf249be01abbf47bc027e6e35e1160b..71b853ffaf740aaba2415a18377326f37c27d584
+++ b/Makefile
@@@ -30,15 -30,15 +30,15 @@@ all:
  # Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
  # /foo/bar/include and /foo/bar/lib directories.
  #
 -# Define NO_CURL if you do not have libcurl installed.  git-http-pull and
 +# Define NO_CURL if you do not have libcurl installed.  git-http-fetch and
  # git-http-push are not built, and you cannot use http:// and https://
 -# transports.
 +# transports (neither smart nor dumb).
  #
  # Define CURLDIR=/foo/bar if your curl header and library files are in
  # /foo/bar/include and /foo/bar/lib directories.
  #
  # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 -# not built, and you cannot push using http:// and https:// transports.
 +# not built, and you cannot push using http:// and https:// transports (dumb).
  #
  # Define EXPATDIR=/foo/bar if your expat header and library files are in
  # /foo/bar/include and /foo/bar/lib directories.
  #
  # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
  #
 +# Define NEEDS_SSL_WITH_CURL if you need -lssl with -lcurl (Minix).
 +#
 +# Define NEEDS_IDN_WITH_CURL if you need -lidn when using -lcurl (Minix).
 +#
  # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
  #
  # Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
  # that tells runtime paths to dynamic libraries;
  # "-Wl,-rpath=/path/lib" is used instead.
  #
 +# Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
 +# as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
 +#
  # Define USE_NSEC below if you want git to care about sub-second file mtimes
  # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
  # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
@@@ -302,7 -295,6 +302,7 @@@ bindir = $(prefix)/$(bindir_relative
  mandir = share/man
  infodir = share/info
  gitexecdir = libexec/git-core
 +mergetoolsdir = $(gitexecdir)/mergetools
  sharedir = $(prefix)/share
  gitwebdir = $(sharedir)/gitweb
  template_dir = share/git-core/templates
@@@ -505,6 -497,7 +505,7 @@@ VCSSVN_LIB=vcs-svn/lib.
  
  LIB_H += advice.h
  LIB_H += archive.h
+ LIB_H += argv-array.h
  LIB_H += attr.h
  LIB_H += blob.h
  LIB_H += builtin.h
@@@ -515,7 -508,6 +516,7 @@@ LIB_H += commit.
  LIB_H += compat/bswap.h
  LIB_H += compat/cygwin.h
  LIB_H += compat/mingw.h
 +LIB_H += compat/obstack.h
  LIB_H += compat/win32/pthread.h
  LIB_H += compat/win32/syslog.h
  LIB_H += compat/win32/sys/poll.h
@@@ -534,7 -526,6 +535,7 @@@ LIB_H += graph.
  LIB_H += grep.h
  LIB_H += hash.h
  LIB_H += help.h
 +LIB_H += kwset.h
  LIB_H += levenshtein.h
  LIB_H += list-objects.h
  LIB_H += ll-merge.h
@@@ -566,7 -557,6 +567,7 @@@ LIB_H += sha1-lookup.
  LIB_H += sideband.h
  LIB_H += sigchain.h
  LIB_H += strbuf.h
 +LIB_H += streaming.h
  LIB_H += string-list.h
  LIB_H += submodule.h
  LIB_H += tag.h
@@@ -586,6 -576,7 +587,7 @@@ LIB_OBJS += alloc.
  LIB_OBJS += archive.o
  LIB_OBJS += archive-tar.o
  LIB_OBJS += archive-zip.o
+ LIB_OBJS += argv-array.o
  LIB_OBJS += attr.o
  LIB_OBJS += base85.o
  LIB_OBJS += bisect.o
@@@ -596,7 -587,6 +598,7 @@@ LIB_OBJS += cache-tree.
  LIB_OBJS += color.o
  LIB_OBJS += combine-diff.o
  LIB_OBJS += commit.o
 +LIB_OBJS += compat/obstack.o
  LIB_OBJS += config.o
  LIB_OBJS += connect.o
  LIB_OBJS += convert.o
@@@ -626,7 -616,6 +628,7 @@@ LIB_OBJS += hash.
  LIB_OBJS += help.o
  LIB_OBJS += hex.o
  LIB_OBJS += ident.o
 +LIB_OBJS += kwset.o
  LIB_OBJS += levenshtein.o
  LIB_OBJS += list-objects.o
  LIB_OBJS += ll-merge.o
@@@ -647,7 -636,6 +649,7 @@@ LIB_OBJS += pack-revindex.
  LIB_OBJS += pack-write.o
  LIB_OBJS += pager.o
  LIB_OBJS += parse-options.o
 +LIB_OBJS += parse-options-cb.o
  LIB_OBJS += patch-delta.o
  LIB_OBJS += patch-ids.o
  LIB_OBJS += path.o
@@@ -676,7 -664,6 +678,7 @@@ LIB_OBJS += shallow.
  LIB_OBJS += sideband.o
  LIB_OBJS += sigchain.o
  LIB_OBJS += strbuf.o
 +LIB_OBJS += streaming.o
  LIB_OBJS += string-list.o
  LIB_OBJS += submodule.o
  LIB_OBJS += symlinks.o
@@@ -820,7 -807,6 +822,7 @@@ ifeq ($(uname_S),GNU/kFreeBSD
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
        HAVE_PATHS_H = YesPlease
 +      DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
  endif
  ifeq ($(uname_S),UnixWare)
        CC = cc
@@@ -1140,6 -1126,8 +1142,6 @@@ endi
        X = .exe
  endif
  ifeq ($(uname_S),Interix)
 -      NO_SYS_POLL_H = YesPlease
 -      NO_INTTYPES_H = YesPlease
        NO_INITGROUPS = YesPlease
        NO_IPV6 = YesPlease
        NO_MEMMEM = YesPlease
        ifeq ($(uname_R),3.5)
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
 +              NO_SOCKADDR_STORAGE = YesPlease
 +              NO_FNMATCH_CASEFOLD = YesPlease
        endif
        ifeq ($(uname_R),5.2)
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
 +              NO_SOCKADDR_STORAGE = YesPlease
 +              NO_FNMATCH_CASEFOLD = YesPlease
        endif
  endif
 +ifeq ($(uname_S),Minix)
 +      NO_IPV6 = YesPlease
 +      NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 +      NO_NSEC = YesPlease
 +      NEEDS_LIBGEN =
 +      NEEDS_CRYPTO_WITH_SSL = YesPlease
 +      NEEDS_IDN_WITH_CURL = YesPlease
 +      NEEDS_SSL_WITH_CURL = YesPlease
 +      NEEDS_RESOLV =
 +      NO_HSTRERROR = YesPlease
 +      NO_MMAP = YesPlease
 +      NO_CURL =
 +      NO_EXPAT =
 +endif
  ifneq (,$(findstring MINGW,$(uname_S)))
        pathsep = ;
        NO_PREAD = YesPlease
        else
                CURL_LIBCURL = -lcurl
        endif
 +      ifdef NEEDS_SSL_WITH_CURL
 +              CURL_LIBCURL += -lssl
 +              ifdef NEEDS_CRYPTO_WITH_SSL
 +                      CURL_LIBCURL += -lcrypto
 +              endif
 +      endif
 +      ifdef NEEDS_IDN_WITH_CURL
 +              CURL_LIBCURL += -lidn
 +      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)
@@@ -1364,7 -1324,7 +1366,7 @@@ ifndef NO_OPENSS
                OPENSSL_LINK =
        endif
        ifdef NEEDS_CRYPTO_WITH_SSL
 -              OPENSSL_LINK += -lcrypto
 +              OPENSSL_LIBSSL += -lcrypto
        endif
  else
        BASIC_CFLAGS += -DNO_OPENSSL
@@@ -1416,9 -1376,6 +1418,9 @@@ endi
  ifdef USE_ST_TIMESPEC
        BASIC_CFLAGS += -DUSE_ST_TIMESPEC
  endif
 +ifdef NO_NORETURN
 +      BASIC_CFLAGS += -DNO_NORETURN
 +endif
  ifdef NO_NSEC
        BASIC_CFLAGS += -DNO_NSEC
  endif
@@@ -1751,7 -1708,7 +1753,7 @@@ git.sp git.s git.o: EXTRA_CPPFLAGS = -D
        '-DGIT_MAN_PATH="$(mandir_SQ)"' \
        '-DGIT_INFO_PATH="$(infodir_SQ)"'
  
 -git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
 +git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
  
@@@ -1883,7 -1840,7 +1885,7 @@@ ifndef NO_CUR
        GIT_OBJS += http.o http-walker.o remote-curl.o
  endif
  XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
 -      xdiff/xmerge.o xdiff/xpatience.o
 +      xdiff/xmerge.o xdiff/xpatience.o xdiff/xhistogram.o
  VCSSVN_OBJS = vcs-svn/string_pool.o vcs-svn/line_buffer.o \
        vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/svndump.o
  VCSSVN_TEST_OBJS = test-obj-pool.o test-string-pool.o \
@@@ -2049,17 -2006,17 +2051,17 @@@ compat/nedmalloc/nedmalloc.sp compat/ne
        -DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR
  endif
  
 -git-%$X: %.o $(GITLIBS)
 +git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
  
 -git-imap-send$X: imap-send.o $(GITLIBS)
 +git-imap-send$X: imap-send.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
  
 -git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o $(GITLIBS)
 +git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL)
 -git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
 +git-http-push$X: revision.o http.o http-push.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
@@@ -2069,7 -2026,7 +2071,7 @@@ $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_P
        ln -s $< $@ 2>/dev/null || \
        cp $< $@
  
 -$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o $(GITLIBS)
 +$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
@@@ -2139,15 -2096,6 +2141,15 @@@ GIT-CFLAGS: FORC
                echo "$$FLAGS" >GIT-CFLAGS; \
              fi
  
 +TRACK_LDFLAGS = $(subst ','\'',$(ALL_LDFLAGS))
 +
 +GIT-LDFLAGS: FORCE
 +      @FLAGS='$(TRACK_LDFLAGS)'; \
 +          if test x"$$FLAGS" != x"`cat GIT-LDFLAGS 2>/dev/null`" ; then \
 +              echo 1>&2 "    * new link flags"; \
 +              echo "$$FLAGS" >GIT-LDFLAGS; \
 +            fi
 +
  # 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".
@@@ -2211,7 -2159,7 +2213,7 @@@ test-delta$X: diff-delta.o patch-delta.
  
  test-line-buffer$X: vcs-svn/lib.a
  
 -test-parse-options$X: parse-options.o
 +test-parse-options$X: parse-options.o parse-options-cb.o
  
  test-string-pool$X: vcs-svn/lib.a
  
@@@ -2219,7 -2167,7 +2221,7 @@@ test-svn-fe$X: vcs-svn/lib.
  
  .PRECIOUS: $(TEST_OBJS)
  
 -test-%$X: test-%.o $(GITLIBS)
 +test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
  
  check-sha1:: test-sha1$X
@@@ -2264,13 -2212,6 +2266,13 @@@ endi
  gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
  export gitexec_instdir
  
 +ifneq ($(filter /%,$(firstword $(mergetoolsdir))),)
 +mergetools_instdir = $(mergetoolsdir)
 +else
 +mergetools_instdir = $(prefix)/$(mergetoolsdir)
 +endif
 +mergetools_instdir_SQ = $(subst ','\'',$(mergetools_instdir))
 +
  install_bindir_programs := $(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X)) $(BINDIR_PROGRAMS_NO_X)
  
  install: all
        $(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 +      $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mergetools_instdir_SQ)'
 +      (cd mergetools && $(TAR) cf - .) | \
 +      (cd '$(DESTDIR_SQ)$(mergetools_instdir_SQ)' && umask 022 && $(TAR) xof -)
  ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
        $(MAKE) -C gitweb install
@@@ -2439,7 -2377,7 +2441,7 @@@ ifndef NO_TCLT
        $(MAKE) -C gitk-git clean
        $(MAKE) -C git-gui clean
  endif
 -      $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
 +      $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
  
  .PHONY: all install clean strip
  .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
@@@ -2550,19 -2488,3 +2552,19 @@@ cover_db: coverage-repor
  
  cover_db_html: cover_db
        cover -report html -outputdir cover_db_html cover_db
 +
 +### profile feedback build
 +#
 +.PHONY: profile-all profile-clean
 +
 +PROFILE_GEN_CFLAGS := $(CFLAGS) -fprofile-generate -DNO_NORETURN=1
 +PROFILE_USE_CFLAGS := $(CFLAGS) -fprofile-use -fprofile-correction -DNO_NORETURN=1
 +
 +profile-clean:
 +      $(RM) $(addsuffix *.gcda,$(object_dirs))
 +      $(RM) $(addsuffix *.gcno,$(object_dirs))
 +
 +profile-all: profile-clean
 +      $(MAKE) CFLAGS="$(PROFILE_GEN_CFLAGS)" all
 +      $(MAKE) CFLAGS="$(PROFILE_GEN_CFLAGS)" -j1 test
 +      $(MAKE) CFLAGS="$(PROFILE_USE_CFLAGS)" all
diff --combined bisect.c
index c7b7d7913dc444926b0260211e8bc3aa75eeffdb,ef92871e3cf8f29fa4c7f84702a4367610295cae..de05bf824620324e42b7a27571ecd3e763cf7e8f
+++ b/bisect.c
  #include "log-tree.h"
  #include "bisect.h"
  #include "sha1-array.h"
+ #include "argv-array.h"
  
  static struct sha1_array good_revs;
  static struct sha1_array skipped_revs;
  
  static const unsigned char *current_bad_sha1;
  
- struct argv_array {
-       const char **argv;
-       int argv_nr;
-       int argv_alloc;
- };
  static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
  static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
 +static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
  
  /* bits #0-15 in revision.h */
  
@@@ -405,21 -399,6 +400,6 @@@ struct commit_list *find_bisection(stru
        return best;
  }
  
- static void argv_array_push(struct argv_array *array, const char *string)
- {
-       ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc);
-       array->argv[array->argv_nr++] = string;
- }
- static void argv_array_push_sha1(struct argv_array *array,
-                                const unsigned char *sha1,
-                                const char *format)
- {
-       struct strbuf buf = STRBUF_INIT;
-       strbuf_addf(&buf, format, sha1_to_hex(sha1));
-       argv_array_push(array, strbuf_detach(&buf, NULL));
- }
  static int register_ref(const char *refname, const unsigned char *sha1,
                        int flags, void *cb_data)
  {
@@@ -449,16 -428,10 +429,10 @@@ static void read_bisect_paths(struct ar
                die_errno("Could not open file '%s'", filename);
  
        while (strbuf_getline(&str, fp, '\n') != EOF) {
-               char *quoted;
-               int res;
                strbuf_trim(&str);
-               quoted = strbuf_detach(&str, NULL);
-               res = sq_dequote_to_argv(quoted, &array->argv,
-                                        &array->argv_nr, &array->argv_alloc);
-               if (res)
+               if (sq_dequote_to_argv_array(str.buf, array))
                        die("Badly quoted content in file '%s': %s",
-                           filename, quoted);
+                           filename, str.buf);
        }
  
        strbuf_release(&str);
@@@ -623,7 -596,7 +597,7 @@@ static void bisect_rev_setup(struct rev
                             const char *bad_format, const char *good_format,
                             int read_paths)
  {
-       struct argv_array rev_argv = { NULL, 0, 0 };
+       struct argv_array rev_argv = ARGV_ARRAY_INIT;
        int i;
  
        init_revisions(revs, prefix);
        revs->commit_format = CMIT_FMT_UNSPECIFIED;
  
        /* rev_argv.argv[0] will be ignored by setup_revisions */
-       argv_array_push(&rev_argv, xstrdup("bisect_rev_setup"));
-       argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format);
+       argv_array_push(&rev_argv, "bisect_rev_setup");
+       argv_array_pushf(&rev_argv, bad_format, sha1_to_hex(current_bad_sha1));
        for (i = 0; i < good_revs.nr; i++)
-               argv_array_push_sha1(&rev_argv, good_revs.sha1[i],
-                                    good_format);
-       argv_array_push(&rev_argv, xstrdup("--"));
+               argv_array_pushf(&rev_argv, good_format,
+                                sha1_to_hex(good_revs.sha1[i]));
+       argv_array_push(&rev_argv, "--");
        if (read_paths)
                read_bisect_paths(&rev_argv);
-       argv_array_push(&rev_argv, NULL);
  
-       setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL);
+       setup_revisions(rev_argv.argc, rev_argv.argv, revs, NULL);
+       /* XXX leak rev_argv, as "revs" may still be pointing to it */
  }
  
  static void bisect_common(struct rev_info *revs)
@@@ -708,23 -681,16 +682,23 @@@ static void mark_expected_rev(char *bis
                die("closing file %s: %s", filename, strerror(errno));
  }
  
 -static int bisect_checkout(char *bisect_rev_hex)
 +static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
  {
        int res;
  
        mark_expected_rev(bisect_rev_hex);
  
        argv_checkout[2] = bisect_rev_hex;
 -      res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
 -      if (res)
 -              exit(res);
 +      if (no_checkout) {
 +              argv_update_ref[3] = bisect_rev_hex;
 +              if (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))
 +                      die("update-ref --no-deref HEAD failed on %s",
 +                          bisect_rev_hex);
 +      } else {
 +              res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
 +              if (res)
 +                      exit(res);
 +      }
  
        argv_show_branch[1] = bisect_rev_hex;
        return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
@@@ -796,7 -762,7 +770,7 @@@ static void handle_skipped_merge_base(c
   * - If one is "skipped", we can't know but we should warn.
   * - If we don't know, we should check it out and ask the user to test.
   */
 -static void check_merge_bases(void)
 +static void check_merge_bases(int no_checkout)
  {
        struct commit_list *result;
        int rev_nr;
                        handle_skipped_merge_base(mb);
                } else {
                        printf("Bisecting: a merge base must be tested\n");
 -                      exit(bisect_checkout(sha1_to_hex(mb)));
 +                      exit(bisect_checkout(sha1_to_hex(mb), no_checkout));
                }
        }
  
@@@ -857,7 -823,7 +831,7 @@@ static int check_ancestors(const char *
   * If a merge base must be tested by the user, its source code will be
   * checked out to be tested by the user and we will exit.
   */
 -static void check_good_are_ancestors_of_bad(const char *prefix)
 +static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
  {
        const char *filename = git_path("BISECT_ANCESTORS_OK");
        struct stat st;
  
        /* Check if all good revs are ancestor of the bad rev. */
        if (check_ancestors(prefix))
 -              check_merge_bases();
 +              check_merge_bases(no_checkout);
  
        /* Create file BISECT_ANCESTORS_OK. */
        fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
@@@ -916,11 -882,8 +890,11 @@@ static void show_diff_tree(const char *
   * We use the convention that exiting with an exit code 10 means that
   * the bisection process finished successfully.
   * In this case the calling shell script should exit 0.
 + *
 + * If no_checkout is non-zero, the bisection process does not
 + * checkout the trial commit but instead simply updates BISECT_HEAD.
   */
 -int bisect_next_all(const char *prefix)
 +int bisect_next_all(const char *prefix, int no_checkout)
  {
        struct rev_info revs;
        struct commit_list *tried;
        if (read_bisect_refs())
                die("reading bisect refs failed");
  
 -      check_good_are_ancestors_of_bad(prefix);
 +      check_good_are_ancestors_of_bad(prefix, no_checkout);
  
        bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
        revs.limited = 1;
               "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
               steps, (steps == 1 ? "" : "s"));
  
 -      return bisect_checkout(bisect_rev_hex);
 +      return bisect_checkout(bisect_rev_hex, no_checkout);
  }
  
diff --combined builtin/checkout.c
index 75dbe761361aa5f5dec2b7ed83556a7035868625,bb056e43bf96ffb2ad84b2a4d05c75e783345b60..a7a493cecf08b20f32e4161dd5009fd2c3b97448
@@@ -19,6 -19,7 +19,7 @@@
  #include "ll-merge.h"
  #include "resolve-undo.h"
  #include "submodule.h"
+ #include "argv-array.h"
  
  static const char * const checkout_usage[] = {
        "git checkout [options] <branch>",
@@@ -71,7 -72,7 +72,7 @@@ static int update_some(const unsigned c
        hashcpy(ce->sha1, sha1);
        memcpy(ce->name, base, baselen);
        memcpy(ce->name + baselen, pathname, len - baselen);
 -      ce->ce_flags = create_ce_flags(len, 0);
 +      ce->ce_flags = create_ce_flags(len, 0) | CE_UPDATE;
        ce->ce_mode = create_ce_mode(mode);
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        return 0;
@@@ -201,7 -202,7 +202,7 @@@ static int checkout_merged(int pos, str
  }
  
  static int checkout_paths(struct tree *source_tree, const char **pathspec,
 -                        struct checkout_opts *opts)
 +                        const char *prefix, struct checkout_opts *opts)
  {
        int pos;
        struct checkout state;
  
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
 +              if (source_tree && !(ce->ce_flags & CE_UPDATE))
 +                      continue;
                match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
        }
  
 -      if (report_path_error(ps_matched, pathspec, 0))
 +      if (report_path_error(ps_matched, pathspec, prefix))
                return 1;
  
        /* "checkout -m path" to recreate conflicted state */
        state.refresh_cache = 1;
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
 +              if (source_tree && !(ce->ce_flags & CE_UPDATE))
 +                      continue;
                if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
                        if (!ce_stage(ce)) {
                                errs |= checkout_entry(ce, &state, NULL);
@@@ -592,24 -589,12 +593,12 @@@ static void update_refs_for_switch(stru
                report_tracking(new);
  }
  
- struct rev_list_args {
-       int argc;
-       int alloc;
-       const char **argv;
- };
- static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
- {
-       ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
-       args->argv[args->argc++] = s;
- }
  static int add_one_ref_to_rev_list_arg(const char *refname,
                                       const unsigned char *sha1,
                                       int flags,
                                       void *cb_data)
  {
-       add_one_rev_list_arg(cb_data, refname);
+       argv_array_push(cb_data, refname);
        return 0;
  }
  
@@@ -661,25 -646,24 +650,25 @@@ static void suggest_reattach(struct com
                "Warning: you are leaving %d commit behind, "
                "not connected to\n"
                "any of your branches:\n\n"
 -              "%s\n"
 -              "If you want to keep it by creating a new branch, "
 -              "this may be a good time\nto do so with:\n\n"
 -              " git branch new_branch_name %s\n\n",
 +              "%s\n",
                /* The plural version */
                "Warning: you are leaving %d commits behind, "
                "not connected to\n"
                "any of your branches:\n\n"
 -              "%s\n"
 -              "If you want to keep them by creating a new branch, "
 -              "this may be a good time\nto do so with:\n\n"
 -              " git branch new_branch_name %s\n\n",
 +              "%s\n",
                /* Give ngettext() the count */
                lost),
                lost,
 -              sb.buf,
 -              sha1_to_hex(commit->object.sha1));
 +              sb.buf);
        strbuf_release(&sb);
 +
 +      if (advice_detached_head)
 +              fprintf(stderr,
 +                      _(
 +                      "If you want to keep them by creating a new branch, "
 +                      "this may be a good time\nto do so with:\n\n"
 +                      " git branch new_branch_name %s\n\n"),
 +                      sha1_to_hex(commit->object.sha1));
  }
  
  /*
   */
  static void orphaned_commit_warning(struct commit *commit)
  {
-       struct rev_list_args args = { 0, 0, NULL };
+       struct argv_array args = ARGV_ARRAY_INIT;
        struct rev_info revs;
  
-       add_one_rev_list_arg(&args, "(internal)");
-       add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1));
-       add_one_rev_list_arg(&args, "--not");
+       argv_array_push(&args, "(internal)");
+       argv_array_push(&args, sha1_to_hex(commit->object.sha1));
+       argv_array_push(&args, "--not");
        for_each_ref(add_one_ref_to_rev_list_arg, &args);
-       add_one_rev_list_arg(&args, "--");
-       add_one_rev_list_arg(&args, NULL);
+       argv_array_push(&args, "--");
  
        init_revisions(&revs, NULL);
        if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
        else
                describe_detached_head(_("Previous HEAD position was"), commit);
  
+       argv_array_clear(&args);
        clear_commit_marks(commit, -1);
        for_each_ref(clear_commit_marks_from_one_ref, NULL);
  }
@@@ -720,12 -704,10 +709,12 @@@ static int switch_branches(struct check
        unsigned char rev[20];
        int flag;
        memset(&old, 0, sizeof(old));
 -      old.path = resolve_ref("HEAD", rev, 0, &flag);
 +      old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
        old.commit = lookup_commit_reference_gently(rev, 1);
 -      if (!(flag & REF_ISSYMREF))
 +      if (!(flag & REF_ISSYMREF)) {
 +              free((char *)old.path);
                old.path = NULL;
 +      }
  
        if (old.path && !prefixcmp(old.path, "refs/heads/"))
                old.name = old.path + strlen("refs/heads/");
        update_refs_for_switch(opts, &old, new);
  
        ret = post_checkout_hook(old.commit, new->commit, 1);
 +      free((char *)old.path);
        return ret || opts->writeout_error;
  }
  
@@@ -1068,7 -1049,7 +1057,7 @@@ int cmd_checkout(int argc, const char *
                if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
                        die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."));
  
 -              return checkout_paths(source_tree, pathspec, &opts);
 +              return checkout_paths(source_tree, pathspec, prefix, &opts);
        }
  
        if (patch_mode)
  
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
 -              if (strbuf_check_branch_ref(&buf, opts.new_branch))
 -                      die(_("git checkout: we do not like '%s' as a branch name."),
 -                          opts.new_branch);
 -              if (!get_sha1(buf.buf, rev)) {
 -                      opts.branch_exists = 1;
 -                      if (!opts.new_branch_force)
 -                              die(_("git checkout: branch %s already exists"),
 -                                  opts.new_branch);
 -              }
 +
 +              opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
 +                                                           !!opts.new_branch_force, 0);
 +
                strbuf_release(&buf);
        }
  
diff --combined quote.c
index 532fd3b7b7a0c7b6766a7af742b0c7362f307221,87bc65eed8bb2cc177fb39d73a193f0a037524c7..911229fdf3caffe29a87aea76f38dc50863797d4
+++ b/quote.c
@@@ -1,5 -1,6 +1,6 @@@
  #include "cache.h"
  #include "quote.h"
+ #include "argv-array.h"
  
  int quote_path_fully = 1;
  
@@@ -120,7 -121,9 +121,9 @@@ char *sq_dequote(char *arg
        return sq_dequote_step(arg, NULL);
  }
  
- int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
+ static int sq_dequote_to_argv_internal(char *arg,
+                                      const char ***argv, int *nr, int *alloc,
+                                      struct argv_array *array)
  {
        char *next = arg;
  
                char *dequoted = sq_dequote_step(next, &next);
                if (!dequoted)
                        return -1;
-               ALLOC_GROW(*argv, *nr + 1, *alloc);
-               (*argv)[(*nr)++] = dequoted;
+               if (argv) {
+                       ALLOC_GROW(*argv, *nr + 1, *alloc);
+                       (*argv)[(*nr)++] = dequoted;
+               }
+               if (array)
+                       argv_array_push(array, dequoted);
        } while (next);
  
        return 0;
  }
  
+ int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
+ {
+       return sq_dequote_to_argv_internal(arg, argv, nr, alloc, NULL);
+ }
+ int sq_dequote_to_argv_array(char *arg, struct argv_array *array)
+ {
+       return sq_dequote_to_argv_internal(arg, NULL, NULL, NULL, array);
+ }
  /* 1 means: quote as octal
   * 0 means: quote as octal if (quote_path_fully)
   * -1 means: never quote
@@@ -325,12 -342,8 +342,12 @@@ static const char *path_relative(const 
  
        if (len < 0)
                len = strlen(in);
 -      if (prefix && prefix_len < 0)
 -              prefix_len = strlen(prefix);
 +      if (prefix_len < 0) {
 +              if (prefix)
 +                      prefix_len = strlen(prefix);
 +              else
 +                      prefix_len = 0;
 +      }
  
        off = 0;
        i = 0;
diff --combined run-command.c
index a2796c4caecc0fae6ea1a95b2c965b92d98f8a31,73e013ea3a003afa22ef769f0a5459c390c1d106..1c5104388429e50722769358d5d89948c55bb3aa
@@@ -1,6 -1,7 +1,7 @@@
  #include "cache.h"
  #include "run-command.h"
  #include "exec_cmd.h"
+ #include "argv-array.h"
  
  static inline void close_pair(int fd[2])
  {
@@@ -77,14 -78,16 +78,14 @@@ static void notify_parent(void
  
  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_in_full(child_err, "fatal: ", 7);
 -      write_in_full(child_err, msg, len);
 -      write_in_full(child_err, "\n", 1);
 +      vwritef(child_err, "fatal: ", err, params);
        exit(128);
  }
 +
 +static void error_child(const char *err, va_list params)
 +{
 +      vwritef(child_err, "error: ", err, params);
 +}
  #endif
  
  static inline void set_cloexec(int fd)
@@@ -125,6 -128,9 +126,6 @@@ static int wait_or_whine(pid_t pid, con
                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);
@@@ -212,7 -218,6 +213,7 @@@ fail_pipe
                        set_cloexec(child_err);
                }
                set_die_routine(die_child);
 +              set_error_routine(error_child);
  
                close(notify_pipe[0]);
                set_cloexec(notify_pipe[1]);
                } else {
                        execvp(cmd->argv[0], (char *const*) cmd->argv);
                }
 -              /*
 -               * Do not check for cmd->silent_exec_failure; the parent
 -               * process will check it when it sees this exit code.
 -               */
 -              if (errno == ENOENT)
 +              if (errno == ENOENT) {
 +                      if (!cmd->silent_exec_failure)
 +                              error("cannot run %s: %s", cmd->argv[0],
 +                                      strerror(ENOENT));
                        exit(127);
 -              else
 +              } else {
                        die_errno("cannot exec '%s'", cmd->argv[0]);
 +              }
        }
        if (cmd->pid < 0)
                error("cannot fork() for %s: %s", cmd->argv[0],
@@@ -605,26 -610,23 +606,23 @@@ int finish_async(struct async *async
  int run_hook(const char *index_file, const char *name, ...)
  {
        struct child_process hook;
-       const char **argv = NULL, *env[2];
+       struct argv_array argv = ARGV_ARRAY_INIT;
+       const char *p, *env[2];
        char index[PATH_MAX];
        va_list args;
        int ret;
-       size_t i = 0, alloc = 0;
  
        if (access(git_path("hooks/%s", name), X_OK) < 0)
                return 0;
  
        va_start(args, name);
-       ALLOC_GROW(argv, i + 1, alloc);
-       argv[i++] = git_path("hooks/%s", name);
-       while (argv[i-1]) {
-               ALLOC_GROW(argv, i + 1, alloc);
-               argv[i++] = va_arg(args, const char *);
-       }
+       argv_array_push(&argv, git_path("hooks/%s", name));
+       while ((p = va_arg(args, const char *)))
+               argv_array_push(&argv, p);
        va_end(args);
  
        memset(&hook, 0, sizeof(hook));
-       hook.argv = argv;
+       hook.argv = argv.argv;
        hook.no_stdin = 1;
        hook.stdout_to_stderr = 1;
        if (index_file) {
        }
  
        ret = run_command(&hook);
-       free(argv);
+       argv_array_clear(&argv);
        return ret;
  }
diff --combined submodule.c
index 08756387e207700861cfa6dac039334ecd53c7a1,6306737eddd78e51848f0dccbf18efa0843d71b7..0b709bc2914335853e7525076f5e1d026d5dd779
@@@ -9,6 -9,7 +9,7 @@@
  #include "refs.h"
  #include "string-list.h"
  #include "sha1-array.h"
+ #include "argv-array.h"
  
  static struct string_list config_name_for_path;
  static struct string_list config_fetch_recurse_submodules_for_name;
@@@ -37,7 -38,7 +38,7 @@@ static int add_submodule_odb(const cha
        const char *git_dir;
  
        strbuf_addf(&objects_directory, "%s/.git", path);
 -      git_dir = read_gitfile_gently(objects_directory.buf);
 +      git_dir = read_gitfile(objects_directory.buf);
        if (git_dir) {
                strbuf_reset(&objects_directory);
                strbuf_addstr(&objects_directory, git_dir);
@@@ -313,114 -314,6 +314,114 @@@ void set_config_fetch_recurse_submodule
        config_fetch_recurse_submodules = value;
  }
  
 +static int has_remote(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 +{
 +      return 1;
 +}
 +
 +static int submodule_needs_pushing(const char *path, const unsigned char sha1[20])
 +{
 +      if (add_submodule_odb(path) || !lookup_commit_reference(sha1))
 +              return 0;
 +
 +      if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
 +              struct child_process cp;
 +              const char *argv[] = {"rev-list", NULL, "--not", "--remotes", "-n", "1" , NULL};
 +              struct strbuf buf = STRBUF_INIT;
 +              int needs_pushing = 0;
 +
 +              argv[1] = sha1_to_hex(sha1);
 +              memset(&cp, 0, sizeof(cp));
 +              cp.argv = argv;
 +              cp.env = local_repo_env;
 +              cp.git_cmd = 1;
 +              cp.no_stdin = 1;
 +              cp.out = -1;
 +              cp.dir = path;
 +              if (start_command(&cp))
 +                      die("Could not run 'git rev-list %s --not --remotes -n 1' command in submodule %s",
 +                              sha1_to_hex(sha1), path);
 +              if (strbuf_read(&buf, cp.out, 41))
 +                      needs_pushing = 1;
 +              finish_command(&cp);
 +              close(cp.out);
 +              strbuf_release(&buf);
 +              return needs_pushing;
 +      }
 +
 +      return 0;
 +}
 +
 +static void collect_submodules_from_diff(struct diff_queue_struct *q,
 +                                       struct diff_options *options,
 +                                       void *data)
 +{
 +      int i;
 +      int *needs_pushing = data;
 +
 +      for (i = 0; i < q->nr; i++) {
 +              struct diff_filepair *p = q->queue[i];
 +              if (!S_ISGITLINK(p->two->mode))
 +                      continue;
 +              if (submodule_needs_pushing(p->two->path, p->two->sha1)) {
 +                      *needs_pushing = 1;
 +                      break;
 +              }
 +      }
 +}
 +
 +
 +static void commit_need_pushing(struct commit *commit, struct commit_list *parent, int *needs_pushing)
 +{
 +      const unsigned char (*parents)[20];
 +      unsigned int i, n;
 +      struct rev_info rev;
 +
 +      n = commit_list_count(parent);
 +      parents = xmalloc(n * sizeof(*parents));
 +
 +      for (i = 0; i < n; i++) {
 +              hashcpy((unsigned char *)(parents + i), parent->item->object.sha1);
 +              parent = parent->next;
 +      }
 +
 +      init_revisions(&rev, NULL);
 +      rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
 +      rev.diffopt.format_callback = collect_submodules_from_diff;
 +      rev.diffopt.format_callback_data = needs_pushing;
 +      diff_tree_combined(commit->object.sha1, parents, n, 1, &rev);
 +
 +      free(parents);
 +}
 +
 +int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name)
 +{
 +      struct rev_info rev;
 +      struct commit *commit;
 +      const char *argv[] = {NULL, NULL, "--not", "NULL", NULL};
 +      int argc = ARRAY_SIZE(argv) - 1;
 +      char *sha1_copy;
 +      int needs_pushing = 0;
 +      struct strbuf remotes_arg = STRBUF_INIT;
 +
 +      strbuf_addf(&remotes_arg, "--remotes=%s", remotes_name);
 +      init_revisions(&rev, NULL);
 +      sha1_copy = xstrdup(sha1_to_hex(new_sha1));
 +      argv[1] = sha1_copy;
 +      argv[3] = remotes_arg.buf;
 +      setup_revisions(argc, argv, &rev, NULL);
 +      if (prepare_revision_walk(&rev))
 +              die("revision walk setup failed");
 +
 +      while ((commit = get_revision(&rev)) && !needs_pushing)
 +              commit_need_pushing(commit, commit->parents, &needs_pushing);
 +
 +      free(sha1_copy);
 +      strbuf_release(&remotes_arg);
 +
 +      return needs_pushing;
 +}
 +
  static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
  {
        int is_present = 0;
@@@ -496,56 -389,22 +497,26 @@@ void check_for_new_submodule_commits(un
        sha1_array_append(&ref_tips_after_fetch, new_sha1);
  }
  
- struct argv_array {
-       const char **argv;
-       unsigned int argc;
-       unsigned int alloc;
- };
- static void init_argv(struct argv_array *array)
- {
-       array->argv = NULL;
-       array->argc = 0;
-       array->alloc = 0;
- }
- static void push_argv(struct argv_array *array, const char *value)
- {
-       ALLOC_GROW(array->argv, array->argc + 2, array->alloc);
-       array->argv[array->argc++] = xstrdup(value);
-       array->argv[array->argc] = NULL;
- }
- static void clear_argv(struct argv_array *array)
- {
-       int i;
-       for (i = 0; i < array->argc; i++)
-               free((char **)array->argv[i]);
-       free(array->argv);
-       init_argv(array);
- }
  static void add_sha1_to_argv(const unsigned char sha1[20], void *data)
  {
-       push_argv(data, sha1_to_hex(sha1));
+       argv_array_push(data, sha1_to_hex(sha1));
  }
  
  static void calculate_changed_submodule_paths(void)
  {
        struct rev_info rev;
        struct commit *commit;
-       struct argv_array argv;
+       struct argv_array argv = ARGV_ARRAY_INIT;
  
 +      /* No need to check if there are no submodules configured */
 +      if (!config_name_for_path.nr)
 +              return;
 +
        init_revisions(&rev, NULL);
-       init_argv(&argv);
-       push_argv(&argv, "--"); /* argv[0] program name */
+       argv_array_push(&argv, "--"); /* argv[0] program name */
        sha1_array_for_each_unique(&ref_tips_after_fetch,
                                   add_sha1_to_argv, &argv);
-       push_argv(&argv, "--not");
+       argv_array_push(&argv, "--not");
        sha1_array_for_each_unique(&ref_tips_before_fetch,
                                   add_sha1_to_argv, &argv);
        setup_revisions(argv.argc, argv.argv, &rev, NULL);
                while (parent) {
                        struct diff_options diff_opts;
                        diff_setup(&diff_opts);
 +                      DIFF_OPT_SET(&diff_opts, RECURSIVE);
                        diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
                        diff_opts.format_callback = submodule_collect_changed_cb;
                        if (diff_setup_done(&diff_opts) < 0)
                }
        }
  
-       clear_argv(&argv);
+       argv_array_clear(&argv);
        sha1_array_clear(&ref_tips_before_fetch);
        sha1_array_clear(&ref_tips_after_fetch);
        initialized_fetch_ref_tips = 0;
@@@ -658,7 -516,7 +629,7 @@@ int fetch_populated_submodules(int num_
                strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name);
                strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
                strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name);
 -              git_dir = read_gitfile_gently(submodule_git_dir.buf);
 +              git_dir = read_gitfile(submodule_git_dir.buf);
                if (!git_dir)
                        git_dir = submodule_git_dir.buf;
                if (is_directory(git_dir)) {
@@@ -696,7 -554,7 +667,7 @@@ unsigned is_submodule_modified(const ch
        const char *git_dir;
  
        strbuf_addf(&buf, "%s/.git", path);
 -      git_dir = read_gitfile_gently(buf.buf);
 +      git_dir = read_gitfile(buf.buf);
        if (!git_dir)
                git_dir = buf.buf;
        if (!is_directory(git_dir)) {