Merge branch 'ps/stash-in-c'
authorJunio C Hamano <gitster@pobox.com>
Mon, 22 Apr 2019 02:14:43 +0000 (11:14 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 22 Apr 2019 02:14:43 +0000 (11:14 +0900)
"git stash" rewritten in C.

* ps/stash-in-c: (28 commits)
  tests: add a special setup where stash.useBuiltin is off
  stash: optionally use the scripted version again
  stash: add back the original, scripted `git stash`
  stash: convert `stash--helper.c` into `stash.c`
  stash: replace all `write-tree` child processes with API calls
  stash: optimize `get_untracked_files()` and `check_changes()`
  stash: convert save to builtin
  stash: make push -q quiet
  stash: convert push to builtin
  stash: convert create to builtin
  stash: convert store to builtin
  stash: convert show to builtin
  stash: convert list to builtin
  stash: convert pop to builtin
  stash: convert branch to builtin
  stash: convert drop and clear to builtin
  stash: convert apply to builtin
  stash: mention options in `show` synopsis
  stash: add tests for `git stash show` config
  stash: rename test cases to be more descriptive
  ...

1  2 
.gitignore
Makefile
builtin/stash.c
cache.h
git.c
ident.c
sha1-name.c
strbuf.c
strbuf.h
t/README

diff --combined .gitignore
@@@ -1,4 -1,3 +1,4 @@@
 +/fuzz-commit-graph
  /fuzz_corpora
  /fuzz-pack-headers
  /fuzz-pack-idx
@@@ -82,6 -81,8 +82,7 @@@
  /git-init-db
  /git-interpret-trailers
  /git-instaweb
 -/git-legacy-rebase
+ /git-legacy-stash
  /git-log
  /git-ls-files
  /git-ls-remote
  /git-rebase--am
  /git-rebase--common
  /git-rebase--interactive
 -/git-rebase--merge
  /git-rebase--preserve-merges
  /git-receive-pack
  /git-reflog
  *.pdb
  /Debug/
  /Release/
 +*.dSYM
diff --combined Makefile
+++ b/Makefile
@@@ -186,12 -186,6 +186,12 @@@ all:
  # in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO
  # wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined.
  #
 +# Define BLK_SHA256 to use the built-in SHA-256 routines.
 +#
 +# Define GCRYPT_SHA256 to use the SHA-256 routines in libgcrypt.
 +#
 +# Define OPENSSL_SHA256 to use the SHA-256 routines in OpenSSL.
 +#
  # Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
  #
  # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
  # Define OLD_ICONV if your library has an old iconv(), where the second
  # (input buffer pointer) parameter is declared with type (const char **).
  #
 +# Define ICONV_OMITS_BOM if your iconv implementation does not write a
 +# byte-order mark (BOM) when writing UTF-16 or UTF-32 and always writes in
 +# big-endian format.
 +#
  # Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
  #
  # Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib"
  #
  # Define HAVE_GETDELIM if your system has the getdelim() function.
  #
 +# Define FILENO_IS_A_MACRO if fileno() is a macro, not a real function.
 +#
  # Define PAGER_ENV to a SP separated VAR=VAL pairs to define
  # default environment variables to be passed when a pager is spawned, e.g.
  #
  #
  # Define DEVELOPER to enable more compiler warnings. Compiler version
  # and family are auto detected, but could be overridden by defining
 -# COMPILER_FEATURES (see config.mak.dev)
 +# COMPILER_FEATURES (see config.mak.dev). You can still set
 +# CFLAGS="..." in combination with DEVELOPER enables, whether that's
 +# for tweaking something unrelated (e.g. optimization level), or for
 +# selectively overriding something DEVELOPER or one of the DEVOPTS
 +# (see just below) brings in.
  #
  # When DEVELOPER is set, DEVOPTS can be used to control compiler
  # options.  This variable contains keywords separated by
@@@ -510,8 -494,17 +510,8 @@@ GIT-VERSION-FILE: FORC
        @$(SHELL_PATH) ./GIT-VERSION-GEN
  -include GIT-VERSION-FILE
  
 -# CFLAGS and LDFLAGS are for the users to override from the command line.
 -
 -CFLAGS = -g -O2 -Wall
 -LDFLAGS =
 -ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
 -ALL_LDFLAGS = $(LDFLAGS)
 -STRIP ?= strip
 -
 -# Create as necessary, replace existing, make ranlib unneeded.
 -ARFLAGS = rcs
 -
 +# Set our default configuration.
 +#
  # Among the variables below, these:
  #   gitexecdir
  #   template_dir
@@@ -556,7 -549,6 +556,7 @@@ perllibdir_relative = $(patsubst $(pref
  
  export prefix bindir sharedir sysconfdir gitwebdir perllibdir localedir
  
 +# Set our default programs
  CC = cc
  AR = ar
  RM = rm -f
@@@ -569,14 -561,25 +569,14 @@@ TCLTK_PATH = wis
  XGETTEXT = xgettext
  MSGFMT = msgfmt
  CURL_CONFIG = curl-config
 -PTHREAD_LIBS = -lpthread
 -PTHREAD_CFLAGS =
  GCOV = gcov
 +STRIP = strip
  SPATCH = spatch
  
  export TCL_PATH TCLTK_PATH
  
 -SPARSE_FLAGS =
 -SPATCH_FLAGS = --all-includes --patch .
 -
 -
 -
 -### --- END CONFIGURATION SECTION ---
 -
 -# Those must not be GNU-specific; they are shared with perl/ which may
 -# be built by a different compiler. (Note that this is an artifact now
 -# but it still might be nice to keep that distinction.)
 -BASIC_CFLAGS = -I.
 -BASIC_LDFLAGS =
 +# Set our default LIBS variables
 +PTHREAD_LIBS = -lpthread
  
  # Guard against environment variables
  BUILTIN_OBJS =
@@@ -613,9 -616,10 +613,9 @@@ SCRIPT_SH += git-merge-one-file.s
  SCRIPT_SH += git-merge-resolve.sh
  SCRIPT_SH += git-mergetool.sh
  SCRIPT_SH += git-quiltimport.sh
 -SCRIPT_SH += git-legacy-rebase.sh
+ SCRIPT_SH += git-legacy-stash.sh
  SCRIPT_SH += git-remote-testgit.sh
  SCRIPT_SH += git-request-pull.sh
- SCRIPT_SH += git-stash.sh
  SCRIPT_SH += git-submodule.sh
  SCRIPT_SH += git-web--browse.sh
  
@@@ -624,6 -628,7 +624,6 @@@ SCRIPT_LIB += git-parse-remot
  SCRIPT_LIB += git-rebase--am
  SCRIPT_LIB += git-rebase--common
  SCRIPT_LIB += git-rebase--preserve-merges
 -SCRIPT_LIB += git-rebase--merge
  SCRIPT_LIB += git-sh-setup
  SCRIPT_LIB += git-sh-i18n
  
@@@ -679,7 -684,6 +679,7 @@@ SCRIPTS = $(SCRIPT_SH_INS) 
  
  ETAGS_TARGET = TAGS
  
 +FUZZ_OBJS += fuzz-commit-graph.o
  FUZZ_OBJS += fuzz-pack-headers.o
  FUZZ_OBJS += fuzz-pack-idx.o
  
@@@ -720,10 -724,7 +720,10 @@@ TEST_BUILTINS_OBJS += test-dump-split-i
  TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
  TEST_BUILTINS_OBJS += test-example-decorate.o
  TEST_BUILTINS_OBJS += test-genrandom.o
 +TEST_BUILTINS_OBJS += test-genzeros.o
 +TEST_BUILTINS_OBJS += test-hash.o
  TEST_BUILTINS_OBJS += test-hashmap.o
 +TEST_BUILTINS_OBJS += test-hash-speed.o
  TEST_BUILTINS_OBJS += test-index-version.o
  TEST_BUILTINS_OBJS += test-json-writer.o
  TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@@ -746,16 -747,13 +746,16 @@@ TEST_BUILTINS_OBJS += test-run-command.
  TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
  TEST_BUILTINS_OBJS += test-sha1.o
  TEST_BUILTINS_OBJS += test-sha1-array.o
 +TEST_BUILTINS_OBJS += test-sha256.o
  TEST_BUILTINS_OBJS += test-sigchain.o
  TEST_BUILTINS_OBJS += test-strcmp-offset.o
  TEST_BUILTINS_OBJS += test-string-list.o
  TEST_BUILTINS_OBJS += test-submodule-config.o
  TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
  TEST_BUILTINS_OBJS += test-subprocess.o
 +TEST_BUILTINS_OBJS += test-trace2.o
  TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
 +TEST_BUILTINS_OBJS += test-xml-encode.o
  TEST_BUILTINS_OBJS += test-wildmatch.o
  TEST_BUILTINS_OBJS += test-windows-named-pipe.o
  TEST_BUILTINS_OBJS += test-write-cache.o
@@@ -822,8 -820,7 +822,8 @@@ VCSSVN_LIB = vcs-svn/lib.
  
  GENERATED_H += command-list.h
  
 -LIB_H = $(shell $(FIND) . \
 +LIB_H := $(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
 +      $(FIND) . \
        -name .git -prune -o \
        -name t -prune -o \
        -name Documentation -prune -o \
@@@ -999,16 -996,6 +999,16 @@@ LIB_OBJS += tempfile.
  LIB_OBJS += thread-utils.o
  LIB_OBJS += tmp-objdir.o
  LIB_OBJS += trace.o
 +LIB_OBJS += trace2.o
 +LIB_OBJS += trace2/tr2_cfg.o
 +LIB_OBJS += trace2/tr2_cmd_name.o
 +LIB_OBJS += trace2/tr2_dst.o
 +LIB_OBJS += trace2/tr2_sid.o
 +LIB_OBJS += trace2/tr2_tbuf.o
 +LIB_OBJS += trace2/tr2_tgt_event.o
 +LIB_OBJS += trace2/tr2_tgt_normal.o
 +LIB_OBJS += trace2/tr2_tgt_perf.o
 +LIB_OBJS += trace2/tr2_tls.o
  LIB_OBJS += trailer.o
  LIB_OBJS += transport.o
  LIB_OBJS += transport-helper.o
@@@ -1130,6 -1117,7 +1130,7 @@@ BUILTIN_OBJS += builtin/shortlog.
  BUILTIN_OBJS += builtin/show-branch.o
  BUILTIN_OBJS += builtin/show-index.o
  BUILTIN_OBJS += builtin/show-ref.o
+ BUILTIN_OBJS += builtin/stash.o
  BUILTIN_OBJS += builtin/stripspace.o
  BUILTIN_OBJS += builtin/submodule--helper.o
  BUILTIN_OBJS += builtin/symbolic-ref.o
@@@ -1157,25 -1145,6 +1158,25 @@@ ifeq ($(wildcard sha1collisiondetection
  DC_SHA1_SUBMODULE = auto
  endif
  
 +# Set CFLAGS, LDFLAGS and other *FLAGS variables. These might be
 +# tweaked by config.* below as well as the command-line, both of
 +# which'll override these defaults.
 +CFLAGS = -g -O2 -Wall
 +LDFLAGS =
 +BASIC_CFLAGS = -I.
 +BASIC_LDFLAGS =
 +
 +# library flags
 +ARFLAGS = rcs
 +PTHREAD_CFLAGS =
 +
 +# For the 'sparse' target
 +SPARSE_FLAGS ?=
 +SP_EXTRA_FLAGS =
 +
 +# For the 'coccicheck' target
 +SPATCH_FLAGS = --all-includes --patch .
 +
  include config.mak.uname
  -include config.mak.autogen
  -include config.mak
@@@ -1184,9 -1153,6 +1185,9 @@@ ifdef DEVELOPE
  include config.mak.dev
  endif
  
 +ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)
 +ALL_LDFLAGS = $(LDFLAGS)
 +
  comma := ,
  empty :=
  space := $(empty) $(empty)
@@@ -1197,7 -1163,6 +1198,7 @@@ BASIC_CFLAGS += -fsanitize=$(SANITIZE) 
  BASIC_CFLAGS += -fno-omit-frame-pointer
  ifneq ($(filter undefined,$(SANITIZERS)),)
  BASIC_CFLAGS += -DNO_UNALIGNED_LOADS
 +BASIC_CFLAGS += -DSHA1DC_FORCE_ALIGNED_ACCESS
  endif
  ifneq ($(filter leak,$(SANITIZERS)),)
  BASIC_CFLAGS += -DSUPPRESS_ANNOTATED_LEAKS
@@@ -1437,9 -1402,6 +1438,9 @@@ ifndef NO_ICON
                EXTLIBS += $(ICONV_LINK) -liconv
        endif
  endif
 +ifdef ICONV_OMITS_BOM
 +      BASIC_CFLAGS += -DICONV_OMITS_BOM
 +endif
  ifdef NEEDS_LIBGEN
        EXTLIBS += -lgen
  endif
@@@ -1611,9 -1573,7 +1612,9 @@@ ifdef NO_INET_PTO
        LIB_OBJS += compat/inet_pton.o
        BASIC_CFLAGS += -DNO_INET_PTON
  endif
 -ifndef NO_UNIX_SOCKETS
 +ifdef NO_UNIX_SOCKETS
 +      BASIC_CFLAGS += -DNO_UNIX_SOCKETS
 +else
        LIB_OBJS += unix-socket.o
        PROGRAM_OBJS += credential-cache.o
        PROGRAM_OBJS += credential-cache--daemon.o
@@@ -1687,19 -1647,6 +1688,19 @@@ endi
  endif
  endif
  
 +ifdef OPENSSL_SHA256
 +      EXTLIBS += $(LIB_4_CRYPTO)
 +      BASIC_CFLAGS += -DSHA256_OPENSSL
 +else
 +ifdef GCRYPT_SHA256
 +      BASIC_CFLAGS += -DSHA256_GCRYPT
 +      EXTLIBS += -lgcrypt
 +else
 +      LIB_OBJS += sha256/block/sha256.o
 +      BASIC_CFLAGS += -DSHA256_BLK
 +endif
 +endif
 +
  ifdef SHA1_MAX_BLOCK_SIZE
        LIB_OBJS += compat/sha1-chunked.o
        BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
@@@ -1827,11 -1774,6 +1828,11 @@@ ifdef HAVE_WPGMPT
        BASIC_CFLAGS += -DHAVE_WPGMPTR
  endif
  
 +ifdef FILENO_IS_A_MACRO
 +      COMPAT_CFLAGS += -DFILENO_IS_A_MACRO
 +      COMPAT_OBJS += compat/fileno.o
 +endif
 +
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK = NoThanks
  endif
@@@ -2380,7 -2322,7 +2381,7 @@@ els
  # should _not_ be included here, since they are necessary even when
  # building an object for the first time.
  
 -$(OBJECTS): $(LIB_H)
 +$(OBJECTS): $(LIB_H) $(GENERATED_H)
  endif
  
  exec-cmd.sp exec-cmd.s exec-cmd.o: GIT-PREFIX
@@@ -2406,10 -2348,10 +2407,10 @@@ gettext.sp gettext.s gettext.o: GIT-PRE
  gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
        -DGIT_LOCALE_PATH='"$(localedir_relative_SQ)"'
  
 -http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \
 +http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SP_EXTRA_FLAGS += \
        -DCURL_DISABLE_TYPECHECK
  
 -pack-revindex.sp: SPARSE_FLAGS += -Wno-memcpy-max-count
 +pack-revindex.sp: SP_EXTRA_FLAGS += -Wno-memcpy-max-count
  
  ifdef NO_EXPAT
  http-walker.sp http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
@@@ -2423,7 -2365,7 +2424,7 @@@ endi
  ifdef USE_NED_ALLOCATOR
  compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
        -DNDEBUG -DREPLACE_SYSTEM_ALLOCATOR
 -compat/nedmalloc/nedmalloc.sp: SPARSE_FLAGS += -Wno-non-pointer-null
 +compat/nedmalloc/nedmalloc.sp: SP_EXTRA_FLAGS += -Wno-non-pointer-null
  endif
  
  git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
@@@ -2747,16 -2689,13 +2748,16 @@@ SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ)
  
  $(SP_OBJ): %.sp: %.c GIT-CFLAGS FORCE
        $(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) \
 -              $(SPARSE_FLAGS) $<
 +              $(SPARSE_FLAGS) $(SP_EXTRA_FLAGS) $<
  
  .PHONY: sparse $(SP_OBJ)
  sparse: $(SP_OBJ)
  
  GEN_HDRS := command-list.h unicode-width.h
 -EXCEPT_HDRS := $(GEN_HDRS) compat% xdiff%
 +EXCEPT_HDRS := $(GEN_HDRS) compat/% xdiff/%
 +ifndef GCRYPT_SHA256
 +      EXCEPT_HDRS += sha256/gcrypt.h
 +endif
  CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(patsubst ./%,%,$(LIB_H)))
  HCO = $(patsubst %.h,%.hco,$(CHK_HDRS))
  
@@@ -2988,16 -2927,6 +2989,16 @@@ rpm:
        @false
  .PHONY: rpm
  
 +artifacts-tar:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) \
 +              GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
 +              $(NO_INSTALL) $(MOFILES)
 +      $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
 +              SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
 +      test -n "$(ARTIFACTS_DIRECTORY)"
 +      mkdir -p "$(ARTIFACTS_DIRECTORY)"
 +      $(TAR) czf "$(ARTIFACTS_DIRECTORY)/artifacts.tar.gz" $^ templates/blt/
 +.PHONY: artifacts-tar
 +
  htmldocs = git-htmldocs-$(GIT_VERSION)
  manpages = git-manpages-$(GIT_VERSION)
  .PHONY: dist-doc distclean
@@@ -3149,11 -3078,6 +3150,11 @@@ coverage-test: coverage-clean-results c
        $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
                DEFAULT_TEST_TARGET=test -j1 test
  
 +coverage-prove: coverage-clean-results coverage-compile
 +      $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
 +              DEFAULT_TEST_TARGET=prove GIT_PROVE_OPTS="$(GIT_PROVE_OPTS) -j1" \
 +              -j1 test
 +
  coverage-report:
        $(QUIET_GCOV)for dir in $(object_dirs); do \
                $(GCOV) $(GCOVFLAGS) --object-directory=$$dir $$dir*.c || exit; \
@@@ -3180,7 -3104,7 +3181,7 @@@ cover_db_html: cover_d
  # An example command to build against libFuzzer from LLVM 4.0.0:
  #
  # make CC=clang CXX=clang++ \
 -#      FUZZ_CXXFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
 +#      CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
  #      LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
  #      fuzz-all
  #
diff --combined builtin/stash.c
index 0000000,1bfa240..51df092
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1633 +1,1635 @@@
 -      init_merge_options(&o);
++#define USE_THE_INDEX_COMPATIBILITY_MACROS
+ #include "builtin.h"
+ #include "config.h"
+ #include "parse-options.h"
+ #include "refs.h"
+ #include "lockfile.h"
+ #include "cache-tree.h"
+ #include "unpack-trees.h"
+ #include "merge-recursive.h"
+ #include "argv-array.h"
+ #include "run-command.h"
+ #include "dir.h"
+ #include "rerere.h"
+ #include "revision.h"
+ #include "log-tree.h"
+ #include "diffcore.h"
+ #include "exec-cmd.h"
+ #define INCLUDE_ALL_FILES 2
+ static const char * const git_stash_usage[] = {
+       N_("git stash list [<options>]"),
+       N_("git stash show [<options>] [<stash>]"),
+       N_("git stash drop [-q|--quiet] [<stash>]"),
+       N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
+       N_("git stash branch <branchname> [<stash>]"),
+       N_("git stash clear"),
+       N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+          "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+          "          [--] [<pathspec>...]]"),
+       N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+          "          [-u|--include-untracked] [-a|--all] [<message>]"),
+       NULL
+ };
+ static const char * const git_stash_list_usage[] = {
+       N_("git stash list [<options>]"),
+       NULL
+ };
+ static const char * const git_stash_show_usage[] = {
+       N_("git stash show [<options>] [<stash>]"),
+       NULL
+ };
+ static const char * const git_stash_drop_usage[] = {
+       N_("git stash drop [-q|--quiet] [<stash>]"),
+       NULL
+ };
+ static const char * const git_stash_pop_usage[] = {
+       N_("git stash pop [--index] [-q|--quiet] [<stash>]"),
+       NULL
+ };
+ static const char * const git_stash_apply_usage[] = {
+       N_("git stash apply [--index] [-q|--quiet] [<stash>]"),
+       NULL
+ };
+ static const char * const git_stash_branch_usage[] = {
+       N_("git stash branch <branchname> [<stash>]"),
+       NULL
+ };
+ static const char * const git_stash_clear_usage[] = {
+       N_("git stash clear"),
+       NULL
+ };
+ static const char * const git_stash_store_usage[] = {
+       N_("git stash store [-m|--message <message>] [-q|--quiet] <commit>"),
+       NULL
+ };
+ static const char * const git_stash_push_usage[] = {
+       N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+          "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+          "          [--] [<pathspec>...]]"),
+       NULL
+ };
+ static const char * const git_stash_save_usage[] = {
+       N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+          "          [-u|--include-untracked] [-a|--all] [<message>]"),
+       NULL
+ };
+ static const char *ref_stash = "refs/stash";
+ static struct strbuf stash_index_path = STRBUF_INIT;
+ /*
+  * w_commit is set to the commit containing the working tree
+  * b_commit is set to the base commit
+  * i_commit is set to the commit containing the index tree
+  * u_commit is set to the commit containing the untracked files tree
+  * w_tree is set to the working tree
+  * b_tree is set to the base tree
+  * i_tree is set to the index tree
+  * u_tree is set to the untracked files tree
+  */
+ struct stash_info {
+       struct object_id w_commit;
+       struct object_id b_commit;
+       struct object_id i_commit;
+       struct object_id u_commit;
+       struct object_id w_tree;
+       struct object_id b_tree;
+       struct object_id i_tree;
+       struct object_id u_tree;
+       struct strbuf revision;
+       int is_stash_ref;
+       int has_u;
+ };
+ static void free_stash_info(struct stash_info *info)
+ {
+       strbuf_release(&info->revision);
+ }
+ static void assert_stash_like(struct stash_info *info, const char *revision)
+ {
+       if (get_oidf(&info->b_commit, "%s^1", revision) ||
+           get_oidf(&info->w_tree, "%s:", revision) ||
+           get_oidf(&info->b_tree, "%s^1:", revision) ||
+           get_oidf(&info->i_tree, "%s^2:", revision))
+               die(_("'%s' is not a stash-like commit"), revision);
+ }
+ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
+ {
+       int ret;
+       char *end_of_rev;
+       char *expanded_ref;
+       const char *revision;
+       const char *commit = NULL;
+       struct object_id dummy;
+       struct strbuf symbolic = STRBUF_INIT;
+       if (argc > 1) {
+               int i;
+               struct strbuf refs_msg = STRBUF_INIT;
+               for (i = 0; i < argc; i++)
+                       strbuf_addf(&refs_msg, " '%s'", argv[i]);
+               fprintf_ln(stderr, _("Too many revisions specified:%s"),
+                          refs_msg.buf);
+               strbuf_release(&refs_msg);
+               return -1;
+       }
+       if (argc == 1)
+               commit = argv[0];
+       strbuf_init(&info->revision, 0);
+       if (!commit) {
+               if (!ref_exists(ref_stash)) {
+                       free_stash_info(info);
+                       fprintf_ln(stderr, _("No stash entries found."));
+                       return -1;
+               }
+               strbuf_addf(&info->revision, "%s@{0}", ref_stash);
+       } else if (strspn(commit, "0123456789") == strlen(commit)) {
+               strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit);
+       } else {
+               strbuf_addstr(&info->revision, commit);
+       }
+       revision = info->revision.buf;
+       if (get_oid(revision, &info->w_commit)) {
+               error(_("%s is not a valid reference"), revision);
+               free_stash_info(info);
+               return -1;
+       }
+       assert_stash_like(info, revision);
+       info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision);
+       end_of_rev = strchrnul(revision, '@');
+       strbuf_add(&symbolic, revision, end_of_rev - revision);
+       ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);
+       strbuf_release(&symbolic);
+       switch (ret) {
+       case 0: /* Not found, but valid ref */
+               info->is_stash_ref = 0;
+               break;
+       case 1:
+               info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
+               break;
+       default: /* Invalid or ambiguous */
+               free_stash_info(info);
+       }
+       free(expanded_ref);
+       return !(ret == 0 || ret == 1);
+ }
+ static int do_clear_stash(void)
+ {
+       struct object_id obj;
+       if (get_oid(ref_stash, &obj))
+               return 0;
+       return delete_ref(NULL, ref_stash, &obj, 0);
+ }
+ static int clear_stash(int argc, const char **argv, const char *prefix)
+ {
+       struct option options[] = {
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_clear_usage,
+                            PARSE_OPT_STOP_AT_NON_OPTION);
+       if (argc)
+               return error(_("git stash clear with parameters is "
+                              "unimplemented"));
+       return do_clear_stash();
+ }
+ static int reset_tree(struct object_id *i_tree, int update, int reset)
+ {
+       int nr_trees = 1;
+       struct unpack_trees_options opts;
+       struct tree_desc t[MAX_UNPACK_TREES];
+       struct tree *tree;
+       struct lock_file lock_file = LOCK_INIT;
+       read_cache_preload(NULL);
+       if (refresh_cache(REFRESH_QUIET))
+               return -1;
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+       memset(&opts, 0, sizeof(opts));
+       tree = parse_tree_indirect(i_tree);
+       if (parse_tree(tree))
+               return -1;
+       init_tree_desc(t, tree->buffer, tree->size);
+       opts.head_idx = 1;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
+       opts.merge = 1;
+       opts.reset = reset;
+       opts.update = update;
+       opts.fn = oneway_merge;
+       if (unpack_trees(nr_trees, t, &opts))
+               return -1;
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+               return error(_("unable to write new index file"));
+       return 0;
+ }
+ static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       const char *w_commit_hex = oid_to_hex(w_commit);
+       /*
+        * Diff-tree would not be very hard to replace with a native function,
+        * however it should be done together with apply_cached.
+        */
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "diff-tree", "--binary", NULL);
+       argv_array_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
+       return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
+ }
+ static int apply_cached(struct strbuf *out)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       /*
+        * Apply currently only reads either from stdin or a file, thus
+        * apply_all_patches would have to be updated to optionally take a
+        * buffer.
+        */
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "apply", "--cached", NULL);
+       return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
+ }
+ static int reset_head(void)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       /*
+        * Reset is overall quite simple, however there is no current public
+        * API for resetting.
+        */
+       cp.git_cmd = 1;
+       argv_array_push(&cp.args, "reset");
+       return run_command(&cp);
+ }
+ static void add_diff_to_buf(struct diff_queue_struct *q,
+                           struct diff_options *options,
+                           void *data)
+ {
+       int i;
+       for (i = 0; i < q->nr; i++) {
+               strbuf_addstr(data, q->queue[i]->one->path);
+               /* NUL-terminate: will be fed to update-index -z */
+               strbuf_addch(data, '\0');
+       }
+ }
+ static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       const char *c_tree_hex = oid_to_hex(c_tree);
+       /*
+        * diff-index is very similar to diff-tree above, and should be
+        * converted together with update_index.
+        */
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "diff-index", "--cached", "--name-only",
+                        "--diff-filter=A", NULL);
+       argv_array_push(&cp.args, c_tree_hex);
+       return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
+ }
+ static int update_index(struct strbuf *out)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       /*
+        * Update-index is very complicated and may need to have a public
+        * function exposed in order to remove this forking.
+        */
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
+       return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
+ }
+ static int restore_untracked(struct object_id *u_tree)
+ {
+       int res;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       /*
+        * We need to run restore files from a given index, but without
+        * affecting the current index, so we use GIT_INDEX_FILE with
+        * run_command to fork processes that will not interfere.
+        */
+       cp.git_cmd = 1;
+       argv_array_push(&cp.args, "read-tree");
+       argv_array_push(&cp.args, oid_to_hex(u_tree));
+       argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+       if (run_command(&cp)) {
+               remove_path(stash_index_path.buf);
+               return -1;
+       }
+       child_process_init(&cp);
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "checkout-index", "--all", NULL);
+       argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+       res = run_command(&cp);
+       remove_path(stash_index_path.buf);
+       return res;
+ }
+ static int do_apply_stash(const char *prefix, struct stash_info *info,
+                         int index, int quiet)
+ {
+       int ret;
+       int has_index = index;
+       struct merge_options o;
+       struct object_id c_tree;
+       struct object_id index_tree;
+       struct commit *result;
+       const struct object_id *bases[1];
+       read_cache_preload(NULL);
+       if (refresh_cache(REFRESH_QUIET))
+               return -1;
+       if (write_cache_as_tree(&c_tree, 0, NULL))
+               return error(_("cannot apply a stash in the middle of a merge"));
+       if (index) {
+               if (oideq(&info->b_tree, &info->i_tree) ||
+                   oideq(&c_tree, &info->i_tree)) {
+                       has_index = 0;
+               } else {
+                       struct strbuf out = STRBUF_INIT;
+                       if (diff_tree_binary(&out, &info->w_commit)) {
+                               strbuf_release(&out);
+                               return error(_("could not generate diff %s^!."),
+                                            oid_to_hex(&info->w_commit));
+                       }
+                       ret = apply_cached(&out);
+                       strbuf_release(&out);
+                       if (ret)
+                               return error(_("conflicts in index."
+                                              "Try without --index."));
+                       discard_cache();
+                       read_cache();
+                       if (write_cache_as_tree(&index_tree, 0, NULL))
+                               return error(_("could not save index tree"));
+                       reset_head();
+               }
+       }
+       if (info->has_u && restore_untracked(&info->u_tree))
+               return error(_("could not restore untracked files from stash"));
 -      if (get_oid_with_context(argv[0], quiet ? GET_OID_QUIETLY : 0, &obj,
++      init_merge_options(&o, the_repository);
+       o.branch1 = "Updated upstream";
+       o.branch2 = "Stashed changes";
+       if (oideq(&info->b_tree, &c_tree))
+               o.branch1 = "Version stash was based on";
+       if (quiet)
+               o.verbosity = 0;
+       if (o.verbosity >= 3)
+               printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
+       bases[0] = &info->b_tree;
+       ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
+                                     &result);
+       if (ret) {
+               rerere(0);
+               if (index)
+                       fprintf_ln(stderr, _("Index was not unstashed."));
+               return ret;
+       }
+       if (has_index) {
+               if (reset_tree(&index_tree, 0, 0))
+                       return -1;
+       } else {
+               struct strbuf out = STRBUF_INIT;
+               if (get_newly_staged(&out, &c_tree)) {
+                       strbuf_release(&out);
+                       return -1;
+               }
+               if (reset_tree(&c_tree, 0, 1)) {
+                       strbuf_release(&out);
+                       return -1;
+               }
+               ret = update_index(&out);
+               strbuf_release(&out);
+               if (ret)
+                       return -1;
+               discard_cache();
+       }
+       if (quiet) {
+               if (refresh_cache(REFRESH_QUIET))
+                       warning("could not refresh index");
+       } else {
+               struct child_process cp = CHILD_PROCESS_INIT;
+               /*
+                * Status is quite simple and could be replaced with calls to
+                * wt_status in the future, but it adds complexities which may
+                * require more tests.
+                */
+               cp.git_cmd = 1;
+               cp.dir = prefix;
+               argv_array_push(&cp.args, "status");
+               run_command(&cp);
+       }
+       return 0;
+ }
+ static int apply_stash(int argc, const char **argv, const char *prefix)
+ {
+       int ret;
+       int quiet = 0;
+       int index = 0;
+       struct stash_info info;
+       struct option options[] = {
+               OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+               OPT_BOOL(0, "index", &index,
+                        N_("attempt to recreate the index")),
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_apply_usage, 0);
+       if (get_stash_info(&info, argc, argv))
+               return -1;
+       ret = do_apply_stash(prefix, &info, index, quiet);
+       free_stash_info(&info);
+       return ret;
+ }
+ static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)
+ {
+       int ret;
+       struct child_process cp_reflog = CHILD_PROCESS_INIT;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       /*
+        * reflog does not provide a simple function for deleting refs. One will
+        * need to be added to avoid implementing too much reflog code here
+        */
+       cp_reflog.git_cmd = 1;
+       argv_array_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
+                        "--rewrite", NULL);
+       argv_array_push(&cp_reflog.args, info->revision.buf);
+       ret = run_command(&cp_reflog);
+       if (!ret) {
+               if (!quiet)
+                       printf_ln(_("Dropped %s (%s)"), info->revision.buf,
+                                 oid_to_hex(&info->w_commit));
+       } else {
+               return error(_("%s: Could not drop stash entry"),
+                            info->revision.buf);
+       }
+       /*
+        * This could easily be replaced by get_oid, but currently it will throw
+        * a fatal error when a reflog is empty, which we can not recover from.
+        */
+       cp.git_cmd = 1;
+       /* Even though --quiet is specified, rev-parse still outputs the hash */
+       cp.no_stdout = 1;
+       argv_array_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL);
+       argv_array_pushf(&cp.args, "%s@{0}", ref_stash);
+       ret = run_command(&cp);
+       /* do_clear_stash if we just dropped the last stash entry */
+       if (ret)
+               do_clear_stash();
+       return 0;
+ }
+ static void assert_stash_ref(struct stash_info *info)
+ {
+       if (!info->is_stash_ref) {
+               error(_("'%s' is not a stash reference"), info->revision.buf);
+               free_stash_info(info);
+               exit(1);
+       }
+ }
+ static int drop_stash(int argc, const char **argv, const char *prefix)
+ {
+       int ret;
+       int quiet = 0;
+       struct stash_info info;
+       struct option options[] = {
+               OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_drop_usage, 0);
+       if (get_stash_info(&info, argc, argv))
+               return -1;
+       assert_stash_ref(&info);
+       ret = do_drop_stash(prefix, &info, quiet);
+       free_stash_info(&info);
+       return ret;
+ }
+ static int pop_stash(int argc, const char **argv, const char *prefix)
+ {
+       int ret;
+       int index = 0;
+       int quiet = 0;
+       struct stash_info info;
+       struct option options[] = {
+               OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+               OPT_BOOL(0, "index", &index,
+                        N_("attempt to recreate the index")),
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_pop_usage, 0);
+       if (get_stash_info(&info, argc, argv))
+               return -1;
+       assert_stash_ref(&info);
+       if ((ret = do_apply_stash(prefix, &info, index, quiet)))
+               printf_ln(_("The stash entry is kept in case "
+                           "you need it again."));
+       else
+               ret = do_drop_stash(prefix, &info, quiet);
+       free_stash_info(&info);
+       return ret;
+ }
+ static int branch_stash(int argc, const char **argv, const char *prefix)
+ {
+       int ret;
+       const char *branch = NULL;
+       struct stash_info info;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct option options[] = {
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_branch_usage, 0);
+       if (!argc) {
+               fprintf_ln(stderr, _("No branch name specified"));
+               return -1;
+       }
+       branch = argv[0];
+       if (get_stash_info(&info, argc - 1, argv + 1))
+               return -1;
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "checkout", "-b", NULL);
+       argv_array_push(&cp.args, branch);
+       argv_array_push(&cp.args, oid_to_hex(&info.b_commit));
+       ret = run_command(&cp);
+       if (!ret)
+               ret = do_apply_stash(prefix, &info, 1, 0);
+       if (!ret && info.is_stash_ref)
+               ret = do_drop_stash(prefix, &info, 0);
+       free_stash_info(&info);
+       return ret;
+ }
+ static int list_stash(int argc, const char **argv, const char *prefix)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct option options[] = {
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_list_usage,
+                            PARSE_OPT_KEEP_UNKNOWN);
+       if (!ref_exists(ref_stash))
+               return 0;
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "log", "--format=%gd: %gs", "-g",
+                        "--first-parent", "-m", NULL);
+       argv_array_pushv(&cp.args, argv);
+       argv_array_push(&cp.args, ref_stash);
+       argv_array_push(&cp.args, "--");
+       return run_command(&cp);
+ }
+ static int show_stat = 1;
+ static int show_patch;
+ static int git_stash_config(const char *var, const char *value, void *cb)
+ {
+       if (!strcmp(var, "stash.showstat")) {
+               show_stat = git_config_bool(var, value);
+               return 0;
+       }
+       if (!strcmp(var, "stash.showpatch")) {
+               show_patch = git_config_bool(var, value);
+               return 0;
+       }
+       return git_default_config(var, value, cb);
+ }
+ static int show_stash(int argc, const char **argv, const char *prefix)
+ {
+       int i;
+       int opts = 0;
+       int ret = 0;
+       struct stash_info info;
+       struct rev_info rev;
+       struct argv_array stash_args = ARGV_ARRAY_INIT;
+       struct option options[] = {
+               OPT_END()
+       };
+       init_diff_ui_defaults();
+       git_config(git_diff_ui_config, NULL);
+       init_revisions(&rev, prefix);
+       for (i = 1; i < argc; i++) {
+               if (argv[i][0] != '-')
+                       argv_array_push(&stash_args, argv[i]);
+               else
+                       opts++;
+       }
+       ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
+       argv_array_clear(&stash_args);
+       if (ret)
+               return -1;
+       /*
+        * The config settings are applied only if there are not passed
+        * any options.
+        */
+       if (!opts) {
+               git_config(git_stash_config, NULL);
+               if (show_stat)
+                       rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
+               if (show_patch)
+                       rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+               if (!show_stat && !show_patch) {
+                       free_stash_info(&info);
+                       return 0;
+               }
+       }
+       argc = setup_revisions(argc, argv, &rev, NULL);
+       if (argc > 1) {
+               free_stash_info(&info);
+               usage_with_options(git_stash_show_usage, options);
+       }
+       rev.diffopt.flags.recursive = 1;
+       setup_diff_pager(&rev.diffopt);
+       diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
+       log_tree_diff_flush(&rev);
+       free_stash_info(&info);
+       return diff_result_code(&rev.diffopt, 0);
+ }
+ static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
+                         int quiet)
+ {
+       if (!stash_msg)
+               stash_msg = "Created via \"git stash store\".";
+       if (update_ref(stash_msg, ref_stash, w_commit, NULL,
+                      REF_FORCE_CREATE_REFLOG,
+                      quiet ? UPDATE_REFS_QUIET_ON_ERR :
+                      UPDATE_REFS_MSG_ON_ERR)) {
+               if (!quiet) {
+                       fprintf_ln(stderr, _("Cannot update %s with %s"),
+                                  ref_stash, oid_to_hex(w_commit));
+               }
+               return -1;
+       }
+       return 0;
+ }
+ static int store_stash(int argc, const char **argv, const char *prefix)
+ {
+       int quiet = 0;
+       const char *stash_msg = NULL;
+       struct object_id obj;
+       struct object_context dummy;
+       struct option options[] = {
+               OPT__QUIET(&quiet, N_("be quiet")),
+               OPT_STRING('m', "message", &stash_msg, "message",
+                          N_("stash message")),
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_store_usage,
+                            PARSE_OPT_KEEP_UNKNOWN);
+       if (argc != 1) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("\"git stash store\" requires one "
+                                            "<commit> argument"));
+               return -1;
+       }
++      if (get_oid_with_context(the_repository,
++                               argv[0], quiet ? GET_OID_QUIETLY : 0, &obj,
+                                &dummy)) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot update %s with %s"),
+                                            ref_stash, argv[0]);
+               return -1;
+       }
+       return do_store_stash(&obj, stash_msg, quiet);
+ }
+ static void add_pathspecs(struct argv_array *args,
+                         struct pathspec ps) {
+       int i;
+       for (i = 0; i < ps.nr; i++)
+               argv_array_push(args, ps.items[i].match);
+ }
+ /*
+  * `untracked_files` will be filled with the names of untracked files.
+  * The return value is:
+  *
+  * = 0 if there are not any untracked files
+  * > 0 if there are untracked files
+  */
+ static int get_untracked_files(struct pathspec ps, int include_untracked,
+                              struct strbuf *untracked_files)
+ {
+       int i;
+       int max_len;
+       int found = 0;
+       char *seen;
+       struct dir_struct dir;
+       memset(&dir, 0, sizeof(dir));
+       if (include_untracked != INCLUDE_ALL_FILES)
+               setup_standard_excludes(&dir);
+       seen = xcalloc(ps.nr, 1);
+       max_len = fill_directory(&dir, the_repository->index, &ps);
+       for (i = 0; i < dir.nr; i++) {
+               struct dir_entry *ent = dir.entries[i];
+               if (dir_path_match(&the_index, ent, &ps, max_len, seen)) {
+                       found++;
+                       strbuf_addstr(untracked_files, ent->name);
+                       /* NUL-terminate: will be fed to update-index -z */
+                       strbuf_addch(untracked_files, '\0');
+               }
+               free(ent);
+       }
+       free(seen);
+       free(dir.entries);
+       free(dir.ignored);
+       clear_directory(&dir);
+       return found;
+ }
+ /*
+  * The return value of `check_changes_tracked_files()` can be:
+  *
+  * < 0 if there was an error
+  * = 0 if there are no changes.
+  * > 0 if there are changes.
+  */
+ static int check_changes_tracked_files(struct pathspec ps)
+ {
+       int result;
+       struct rev_info rev;
+       struct object_id dummy;
+       /* No initial commit. */
+       if (get_oid("HEAD", &dummy))
+               return -1;
+       if (read_cache() < 0)
+               return -1;
+       init_revisions(&rev, NULL);
+       rev.prune_data = ps;
+       rev.diffopt.flags.quick = 1;
+       rev.diffopt.flags.ignore_submodules = 1;
+       rev.abbrev = 0;
+       add_head_to_pending(&rev);
+       diff_setup_done(&rev.diffopt);
+       result = run_diff_index(&rev, 1);
+       if (diff_result_code(&rev.diffopt, result))
+               return 1;
+       object_array_clear(&rev.pending);
+       result = run_diff_files(&rev, 0);
+       if (diff_result_code(&rev.diffopt, result))
+               return 1;
+       return 0;
+ }
+ /*
+  * The function will fill `untracked_files` with the names of untracked files
+  * It will return 1 if there were any changes and 0 if there were not.
+  */
+ static int check_changes(struct pathspec ps, int include_untracked,
+                        struct strbuf *untracked_files)
+ {
+       int ret = 0;
+       if (check_changes_tracked_files(ps))
+               ret = 1;
+       if (include_untracked && get_untracked_files(ps, include_untracked,
+                                                    untracked_files))
+               ret = 1;
+       return ret;
+ }
+ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
+                               struct strbuf files)
+ {
+       int ret = 0;
+       struct strbuf untracked_msg = STRBUF_INIT;
+       struct child_process cp_upd_index = CHILD_PROCESS_INIT;
+       struct index_state istate = { NULL };
+       cp_upd_index.git_cmd = 1;
+       argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
+                        "--remove", "--stdin", NULL);
+       argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+       strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf);
+       if (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0,
+                        NULL, 0)) {
+               ret = -1;
+               goto done;
+       }
+       if (write_index_as_tree(&info->u_tree, &istate, stash_index_path.buf, 0,
+                               NULL)) {
+               ret = -1;
+               goto done;
+       }
+       if (commit_tree(untracked_msg.buf, untracked_msg.len,
+                       &info->u_tree, NULL, &info->u_commit, NULL, NULL)) {
+               ret = -1;
+               goto done;
+       }
+ done:
+       discard_index(&istate);
+       strbuf_release(&untracked_msg);
+       remove_path(stash_index_path.buf);
+       return ret;
+ }
+ static int stash_patch(struct stash_info *info, struct pathspec ps,
+                      struct strbuf *out_patch, int quiet)
+ {
+       int ret = 0;
+       struct child_process cp_read_tree = CHILD_PROCESS_INIT;
+       struct child_process cp_add_i = CHILD_PROCESS_INIT;
+       struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
+       struct index_state istate = { NULL };
+       remove_path(stash_index_path.buf);
+       cp_read_tree.git_cmd = 1;
+       argv_array_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
+       argv_array_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+       if (run_command(&cp_read_tree)) {
+               ret = -1;
+               goto done;
+       }
+       /* Find out what the user wants. */
+       cp_add_i.git_cmd = 1;
+       argv_array_pushl(&cp_add_i.args, "add--interactive", "--patch=stash",
+                        "--", NULL);
+       add_pathspecs(&cp_add_i.args, ps);
+       argv_array_pushf(&cp_add_i.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+       if (run_command(&cp_add_i)) {
+               ret = -1;
+               goto done;
+       }
+       /* State of the working tree. */
+       if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
+                               NULL)) {
+               ret = -1;
+               goto done;
+       }
+       cp_diff_tree.git_cmd = 1;
+       argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "HEAD",
+                        oid_to_hex(&info->w_tree), "--", NULL);
+       if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
+               ret = -1;
+               goto done;
+       }
+       if (!out_patch->len) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("No changes selected"));
+               ret = 1;
+       }
+ done:
+       discard_index(&istate);
+       remove_path(stash_index_path.buf);
+       return ret;
+ }
+ static int stash_working_tree(struct stash_info *info, struct pathspec ps)
+ {
+       int ret = 0;
+       struct rev_info rev;
+       struct child_process cp_upd_index = CHILD_PROCESS_INIT;
+       struct strbuf diff_output = STRBUF_INIT;
+       struct index_state istate = { NULL };
+       init_revisions(&rev, NULL);
+       set_alternate_index_output(stash_index_path.buf);
+       if (reset_tree(&info->i_tree, 0, 0)) {
+               ret = -1;
+               goto done;
+       }
+       set_alternate_index_output(NULL);
+       rev.prune_data = ps;
+       rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+       rev.diffopt.format_callback = add_diff_to_buf;
+       rev.diffopt.format_callback_data = &diff_output;
+       if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+               ret = -1;
+               goto done;
+       }
+       add_pending_object(&rev, parse_object(the_repository, &info->b_commit),
+                          "");
+       if (run_diff_index(&rev, 0)) {
+               ret = -1;
+               goto done;
+       }
+       cp_upd_index.git_cmd = 1;
+       argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
+                        "--remove", "--stdin", NULL);
+       argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+       if (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len,
+                        NULL, 0, NULL, 0)) {
+               ret = -1;
+               goto done;
+       }
+       if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
+                               NULL)) {
+               ret = -1;
+               goto done;
+       }
+ done:
+       discard_index(&istate);
+       UNLEAK(rev);
+       object_array_clear(&rev.pending);
+       strbuf_release(&diff_output);
+       remove_path(stash_index_path.buf);
+       return ret;
+ }
+ static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,
+                          int include_untracked, int patch_mode,
+                          struct stash_info *info, struct strbuf *patch,
+                          int quiet)
+ {
+       int ret = 0;
+       int flags = 0;
+       int untracked_commit_option = 0;
+       const char *head_short_sha1 = NULL;
+       const char *branch_ref = NULL;
+       const char *branch_name = "(no branch)";
+       struct commit *head_commit = NULL;
+       struct commit_list *parents = NULL;
+       struct strbuf msg = STRBUF_INIT;
+       struct strbuf commit_tree_label = STRBUF_INIT;
+       struct strbuf untracked_files = STRBUF_INIT;
+       prepare_fallback_ident("git stash", "git@stash");
+       read_cache_preload(NULL);
+       refresh_cache(REFRESH_QUIET);
+       if (get_oid("HEAD", &info->b_commit)) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("You do not have "
+                                            "the initial commit yet"));
+               ret = -1;
+               goto done;
+       } else {
+               head_commit = lookup_commit(the_repository, &info->b_commit);
+       }
+       if (!check_changes(ps, include_untracked, &untracked_files)) {
+               ret = 1;
+               goto done;
+       }
+       branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+       if (flags & REF_ISSYMREF)
+               branch_name = strrchr(branch_ref, '/') + 1;
+       head_short_sha1 = find_unique_abbrev(&head_commit->object.oid,
+                                            DEFAULT_ABBREV);
+       strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1);
+       pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);
+       strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
+       commit_list_insert(head_commit, &parents);
+       if (write_cache_as_tree(&info->i_tree, 0, NULL) ||
+           commit_tree(commit_tree_label.buf, commit_tree_label.len,
+                       &info->i_tree, parents, &info->i_commit, NULL, NULL)) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot save the current "
+                                            "index state"));
+               ret = -1;
+               goto done;
+       }
+       if (include_untracked) {
+               if (save_untracked_files(info, &msg, untracked_files)) {
+                       if (!quiet)
+                               fprintf_ln(stderr, _("Cannot save "
+                                                    "the untracked files"));
+                       ret = -1;
+                       goto done;
+               }
+               untracked_commit_option = 1;
+       }
+       if (patch_mode) {
+               ret = stash_patch(info, ps, patch, quiet);
+               if (ret < 0) {
+                       if (!quiet)
+                               fprintf_ln(stderr, _("Cannot save the current "
+                                                    "worktree state"));
+                       goto done;
+               } else if (ret > 0) {
+                       goto done;
+               }
+       } else {
+               if (stash_working_tree(info, ps)) {
+                       if (!quiet)
+                               fprintf_ln(stderr, _("Cannot save the current "
+                                                    "worktree state"));
+                       ret = -1;
+                       goto done;
+               }
+       }
+       if (!stash_msg_buf->len)
+               strbuf_addf(stash_msg_buf, "WIP on %s", msg.buf);
+       else
+               strbuf_insertf(stash_msg_buf, 0, "On %s: ", branch_name);
+       /*
+        * `parents` will be empty after calling `commit_tree()`, so there is
+        * no need to call `free_commit_list()`
+        */
+       parents = NULL;
+       if (untracked_commit_option)
+               commit_list_insert(lookup_commit(the_repository,
+                                                &info->u_commit),
+                                  &parents);
+       commit_list_insert(lookup_commit(the_repository, &info->i_commit),
+                          &parents);
+       commit_list_insert(head_commit, &parents);
+       if (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree,
+                       parents, &info->w_commit, NULL, NULL)) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot record "
+                                            "working tree state"));
+               ret = -1;
+               goto done;
+       }
+ done:
+       strbuf_release(&commit_tree_label);
+       strbuf_release(&msg);
+       strbuf_release(&untracked_files);
+       return ret;
+ }
+ static int create_stash(int argc, const char **argv, const char *prefix)
+ {
+       int ret = 0;
+       struct strbuf stash_msg_buf = STRBUF_INIT;
+       struct stash_info info;
+       struct pathspec ps;
+       /* Starting with argv[1], since argv[0] is "create" */
+       strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
+       memset(&ps, 0, sizeof(ps));
+       if (!check_changes_tracked_files(ps))
+               return 0;
+       ret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info,
+                             NULL, 0);
+       if (!ret)
+               printf_ln("%s", oid_to_hex(&info.w_commit));
+       strbuf_release(&stash_msg_buf);
+       return ret;
+ }
+ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,
+                        int keep_index, int patch_mode, int include_untracked)
+ {
+       int ret = 0;
+       struct stash_info info;
+       struct strbuf patch = STRBUF_INIT;
+       struct strbuf stash_msg_buf = STRBUF_INIT;
+       struct strbuf untracked_files = STRBUF_INIT;
+       if (patch_mode && keep_index == -1)
+               keep_index = 1;
+       if (patch_mode && include_untracked) {
+               fprintf_ln(stderr, _("Can't use --patch and --include-untracked"
+                                    " or --all at the same time"));
+               ret = -1;
+               goto done;
+       }
+       read_cache_preload(NULL);
+       if (!include_untracked && ps.nr) {
+               int i;
+               char *ps_matched = xcalloc(ps.nr, 1);
+               for (i = 0; i < active_nr; i++)
+                       ce_path_match(&the_index, active_cache[i], &ps,
+                                     ps_matched);
+               if (report_path_error(ps_matched, &ps, NULL)) {
+                       fprintf_ln(stderr, _("Did you forget to 'git add'?"));
+                       ret = -1;
+                       free(ps_matched);
+                       goto done;
+               }
+               free(ps_matched);
+       }
+       if (refresh_cache(REFRESH_QUIET)) {
+               ret = -1;
+               goto done;
+       }
+       if (!check_changes(ps, include_untracked, &untracked_files)) {
+               if (!quiet)
+                       printf_ln(_("No local changes to save"));
+               goto done;
+       }
+       if (!reflog_exists(ref_stash) && do_clear_stash()) {
+               ret = -1;
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot initialize stash"));
+               goto done;
+       }
+       if (stash_msg)
+               strbuf_addstr(&stash_msg_buf, stash_msg);
+       if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
+                           &info, &patch, quiet)) {
+               ret = -1;
+               goto done;
+       }
+       if (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {
+               ret = -1;
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot save the current status"));
+               goto done;
+       }
+       if (!quiet)
+               printf_ln(_("Saved working directory and index state %s"),
+                         stash_msg_buf.buf);
+       if (!patch_mode) {
+               if (include_untracked && !ps.nr) {
+                       struct child_process cp = CHILD_PROCESS_INIT;
+                       cp.git_cmd = 1;
+                       argv_array_pushl(&cp.args, "clean", "--force",
+                                        "--quiet", "-d", NULL);
+                       if (include_untracked == INCLUDE_ALL_FILES)
+                               argv_array_push(&cp.args, "-x");
+                       if (run_command(&cp)) {
+                               ret = -1;
+                               goto done;
+                       }
+               }
+               discard_cache();
+               if (ps.nr) {
+                       struct child_process cp_add = CHILD_PROCESS_INIT;
+                       struct child_process cp_diff = CHILD_PROCESS_INIT;
+                       struct child_process cp_apply = CHILD_PROCESS_INIT;
+                       struct strbuf out = STRBUF_INIT;
+                       cp_add.git_cmd = 1;
+                       argv_array_push(&cp_add.args, "add");
+                       if (!include_untracked)
+                               argv_array_push(&cp_add.args, "-u");
+                       if (include_untracked == INCLUDE_ALL_FILES)
+                               argv_array_push(&cp_add.args, "--force");
+                       argv_array_push(&cp_add.args, "--");
+                       add_pathspecs(&cp_add.args, ps);
+                       if (run_command(&cp_add)) {
+                               ret = -1;
+                               goto done;
+                       }
+                       cp_diff.git_cmd = 1;
+                       argv_array_pushl(&cp_diff.args, "diff-index", "-p",
+                                        "--cached", "--binary", "HEAD", "--",
+                                        NULL);
+                       add_pathspecs(&cp_diff.args, ps);
+                       if (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {
+                               ret = -1;
+                               goto done;
+                       }
+                       cp_apply.git_cmd = 1;
+                       argv_array_pushl(&cp_apply.args, "apply", "--index",
+                                        "-R", NULL);
+                       if (pipe_command(&cp_apply, out.buf, out.len, NULL, 0,
+                                        NULL, 0)) {
+                               ret = -1;
+                               goto done;
+                       }
+               } else {
+                       struct child_process cp = CHILD_PROCESS_INIT;
+                       cp.git_cmd = 1;
+                       argv_array_pushl(&cp.args, "reset", "--hard", "-q",
+                                        NULL);
+                       if (run_command(&cp)) {
+                               ret = -1;
+                               goto done;
+                       }
+               }
+               if (keep_index == 1 && !is_null_oid(&info.i_tree)) {
+                       struct child_process cp_ls = CHILD_PROCESS_INIT;
+                       struct child_process cp_checkout = CHILD_PROCESS_INIT;
+                       struct strbuf out = STRBUF_INIT;
+                       if (reset_tree(&info.i_tree, 0, 1)) {
+                               ret = -1;
+                               goto done;
+                       }
+                       cp_ls.git_cmd = 1;
+                       argv_array_pushl(&cp_ls.args, "ls-files", "-z",
+                                        "--modified", "--", NULL);
+                       add_pathspecs(&cp_ls.args, ps);
+                       if (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) {
+                               ret = -1;
+                               goto done;
+                       }
+                       cp_checkout.git_cmd = 1;
+                       argv_array_pushl(&cp_checkout.args, "checkout-index",
+                                        "-z", "--force", "--stdin", NULL);
+                       if (pipe_command(&cp_checkout, out.buf, out.len, NULL,
+                                        0, NULL, 0)) {
+                               ret = -1;
+                               goto done;
+                       }
+               }
+               goto done;
+       } else {
+               struct child_process cp = CHILD_PROCESS_INIT;
+               cp.git_cmd = 1;
+               argv_array_pushl(&cp.args, "apply", "-R", NULL);
+               if (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {
+                       if (!quiet)
+                               fprintf_ln(stderr, _("Cannot remove "
+                                                    "worktree changes"));
+                       ret = -1;
+                       goto done;
+               }
+               if (keep_index < 1) {
+                       struct child_process cp = CHILD_PROCESS_INIT;
+                       cp.git_cmd = 1;
+                       argv_array_pushl(&cp.args, "reset", "-q", "--", NULL);
+                       add_pathspecs(&cp.args, ps);
+                       if (run_command(&cp)) {
+                               ret = -1;
+                               goto done;
+                       }
+               }
+               goto done;
+       }
+ done:
+       strbuf_release(&stash_msg_buf);
+       return ret;
+ }
+ static int push_stash(int argc, const char **argv, const char *prefix)
+ {
+       int keep_index = -1;
+       int patch_mode = 0;
+       int include_untracked = 0;
+       int quiet = 0;
+       const char *stash_msg = NULL;
+       struct pathspec ps;
+       struct option options[] = {
+               OPT_BOOL('k', "keep-index", &keep_index,
+                        N_("keep index")),
+               OPT_BOOL('p', "patch", &patch_mode,
+                        N_("stash in patch mode")),
+               OPT__QUIET(&quiet, N_("quiet mode")),
+               OPT_BOOL('u', "include-untracked", &include_untracked,
+                        N_("include untracked files in stash")),
+               OPT_SET_INT('a', "all", &include_untracked,
+                           N_("include ignore files"), 2),
+               OPT_STRING('m', "message", &stash_msg, N_("message"),
+                          N_("stash message")),
+               OPT_END()
+       };
+       if (argc)
+               argc = parse_options(argc, argv, prefix, options,
+                                    git_stash_push_usage,
+                                    0);
+       parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL, prefix, argv);
+       return do_push_stash(ps, stash_msg, quiet, keep_index, patch_mode,
+                            include_untracked);
+ }
+ static int save_stash(int argc, const char **argv, const char *prefix)
+ {
+       int keep_index = -1;
+       int patch_mode = 0;
+       int include_untracked = 0;
+       int quiet = 0;
+       int ret = 0;
+       const char *stash_msg = NULL;
+       struct pathspec ps;
+       struct strbuf stash_msg_buf = STRBUF_INIT;
+       struct option options[] = {
+               OPT_BOOL('k', "keep-index", &keep_index,
+                        N_("keep index")),
+               OPT_BOOL('p', "patch", &patch_mode,
+                        N_("stash in patch mode")),
+               OPT__QUIET(&quiet, N_("quiet mode")),
+               OPT_BOOL('u', "include-untracked", &include_untracked,
+                        N_("include untracked files in stash")),
+               OPT_SET_INT('a', "all", &include_untracked,
+                           N_("include ignore files"), 2),
+               OPT_STRING('m', "message", &stash_msg, "message",
+                          N_("stash message")),
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_save_usage,
+                            PARSE_OPT_KEEP_DASHDASH);
+       if (argc)
+               stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
+       memset(&ps, 0, sizeof(ps));
+       ret = do_push_stash(ps, stash_msg, quiet, keep_index,
+                           patch_mode, include_untracked);
+       strbuf_release(&stash_msg_buf);
+       return ret;
+ }
+ static int use_builtin_stash(void)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf out = STRBUF_INIT;
+       int ret, env = git_env_bool("GIT_TEST_STASH_USE_BUILTIN", -1);
+       if (env != -1)
+               return env;
+       argv_array_pushl(&cp.args,
+                        "config", "--bool", "stash.usebuiltin", NULL);
+       cp.git_cmd = 1;
+       if (capture_command(&cp, &out, 6)) {
+               strbuf_release(&out);
+               return 1;
+       }
+       strbuf_trim(&out);
+       ret = !strcmp("true", out.buf);
+       strbuf_release(&out);
+       return ret;
+ }
+ int cmd_stash(int argc, const char **argv, const char *prefix)
+ {
+       int i = -1;
+       pid_t pid = getpid();
+       const char *index_file;
+       struct argv_array args = ARGV_ARRAY_INIT;
+       struct option options[] = {
+               OPT_END()
+       };
+       if (!use_builtin_stash()) {
+               const char *path = mkpath("%s/git-legacy-stash",
+                                         git_exec_path());
+               if (sane_execvp(path, (char **)argv) < 0)
+                       die_errno(_("could not exec %s"), path);
+               else
+                       BUG("sane_execvp() returned???");
+       }
+       prefix = setup_git_directory();
+       trace_repo_setup(prefix);
+       setup_work_tree();
+       git_config(git_diff_basic_config, NULL);
+       argc = parse_options(argc, argv, prefix, options, git_stash_usage,
+                            PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
+       index_file = get_index_file();
+       strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
+                   (uintmax_t)pid);
+       if (!argc)
+               return !!push_stash(0, NULL, prefix);
+       else if (!strcmp(argv[0], "apply"))
+               return !!apply_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "clear"))
+               return !!clear_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "drop"))
+               return !!drop_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "pop"))
+               return !!pop_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "branch"))
+               return !!branch_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "list"))
+               return !!list_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "show"))
+               return !!show_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "store"))
+               return !!store_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "create"))
+               return !!create_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "push"))
+               return !!push_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "save"))
+               return !!save_stash(argc, argv, prefix);
+       else if (*argv[0] != '-')
+               usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
+                             git_stash_usage, options);
+       if (strcmp(argv[0], "-p")) {
+               while (++i < argc && strcmp(argv[i], "--")) {
+                       /*
+                        * `akpqu` is a string which contains all short options,
+                        * except `-m` which is verified separately.
+                        */
+                       if ((strlen(argv[i]) == 2) && *argv[i] == '-' &&
+                           strchr("akpqu", argv[i][1]))
+                               continue;
+                       if (!strcmp(argv[i], "--all") ||
+                           !strcmp(argv[i], "--keep-index") ||
+                           !strcmp(argv[i], "--no-keep-index") ||
+                           !strcmp(argv[i], "--patch") ||
+                           !strcmp(argv[i], "--quiet") ||
+                           !strcmp(argv[i], "--include-untracked"))
+                               continue;
+                       /*
+                        * `-m` and `--message=` are verified separately because
+                        * they need to be immediately followed by a string
+                        * (i.e.`-m"foobar"` or `--message="foobar"`).
+                        */
+                       if (starts_with(argv[i], "-m") ||
+                           starts_with(argv[i], "--message="))
+                               continue;
+                       usage_with_options(git_stash_usage, options);
+               }
+       }
+       argv_array_push(&args, "push");
+       argv_array_pushv(&args, argv);
+       return !!push_stash(args.argc, args.argv, prefix);
+ }
diff --combined cache.h
+++ b/cache.h
@@@ -9,7 -9,6 +9,7 @@@
  #include "gettext.h"
  #include "convert.h"
  #include "trace.h"
 +#include "trace2.h"
  #include "string-list.h"
  #include "pack-revindex.h"
  #include "hash.h"
@@@ -46,20 -45,10 +46,20 @@@ unsigned long git_deflate_bound(git_zst
  /* The length in bytes and in hex digits of an object name (SHA-1 value). */
  #define GIT_SHA1_RAWSZ 20
  #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
 +/* The block size of SHA-1. */
 +#define GIT_SHA1_BLKSZ 64
 +
 +/* The length in bytes and in hex digits of an object name (SHA-256 value). */
 +#define GIT_SHA256_RAWSZ 32
 +#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
 +/* The block size of SHA-256. */
 +#define GIT_SHA256_BLKSZ 64
  
  /* The length in byte and in hex digits of the largest possible hash value. */
 -#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
 -#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
 +#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
 +#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
 +/* The largest possible block size for any supported hash. */
 +#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
  
  struct object_id {
        unsigned char hash[GIT_MAX_RAWSZ];
@@@ -349,6 -338,8 +349,6 @@@ struct index_state 
        struct mem_pool *ce_mem_pool;
  };
  
 -extern struct index_state the_index;
 -
  /* Name hashing */
  extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
  extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
@@@ -410,20 -401,18 +410,20 @@@ struct cache_entry *dup_cache_entry(con
   */
  void validate_cache_entries(const struct index_state *istate);
  
 -#ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
 +#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
 +extern struct index_state the_index;
 +
  #define active_cache (the_index.cache)
  #define active_nr (the_index.cache_nr)
  #define active_alloc (the_index.cache_alloc)
  #define active_cache_changed (the_index.cache_changed)
  #define active_cache_tree (the_index.cache_tree)
  
 -#define read_cache() read_index(&the_index)
 +#define read_cache() repo_read_index(the_repository)
  #define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
 -#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec), 0)
 +#define read_cache_preload(pathspec) repo_read_index_preload(the_repository, (pathspec), 0)
  #define is_cache_unborn() is_index_unborn(&the_index)
 -#define read_cache_unmerged() read_index_unmerged(&the_index)
 +#define read_cache_unmerged() repo_read_index_unmerged(the_repository)
  #define discard_cache() discard_index(&the_index)
  #define unmerged_cache() unmerged_index(&the_index)
  #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
  #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
  #define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
  #define read_blob_data_from_cache(path, sz) read_blob_data_from_index(&the_index, (path), (sz))
 +#define hold_locked_index(lock_file, flags) repo_hold_locked_index(the_repository, (lock_file), (flags))
  #endif
  
  #define TYPE_BITS 3
@@@ -672,14 -660,19 +672,14 @@@ extern int daemonize(void)
  
  /* Initialize and use the cache information */
  struct lock_file;
 -extern int read_index(struct index_state *);
  extern void preload_index(struct index_state *index,
                          const struct pathspec *pathspec,
                          unsigned int refresh_flags);
 -extern int read_index_preload(struct index_state *,
 -                            const struct pathspec *pathspec,
 -                            unsigned int refresh_flags);
  extern int do_read_index(struct index_state *istate, const char *path,
                         int must_exist); /* for testting only! */
  extern int read_index_from(struct index_state *, const char *path,
                           const char *gitdir);
  extern int is_index_unborn(struct index_state *);
 -extern int read_index_unmerged(struct index_state *);
  
  /* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
@@@ -717,9 -710,9 +717,9 @@@ extern int unmerged_index(const struct 
   * provided, the space-separated list of files that differ will be appended
   * to it.
   */
 -extern int index_has_changes(struct index_state *istate,
 -                           struct tree *tree,
 -                           struct strbuf *sb);
 +extern int repo_index_has_changes(struct repository *repo,
 +                                struct tree *tree,
 +                                struct strbuf *sb);
  
  extern int verify_path(const char *path, unsigned mode);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
@@@ -752,14 -745,13 +752,14 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
  #define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
  #define ADD_CACHE_KEEP_CACHE_TREE 32  /* Do not invalidate cache-tree */
 +#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
  
  /* Remove entry, return true if there are more entries to go. */
  extern int remove_index_entry_at(struct index_state *, int pos);
  
 -extern void remove_marked_cache_entries(struct index_state *istate);
 +extern void remove_marked_cache_entries(struct index_state *istate, int invalidate);
  extern int remove_file_from_index(struct index_state *, const char *path);
  #define ADD_CACHE_VERBOSE 1
  #define ADD_CACHE_PRETEND 2
@@@ -835,6 -827,13 +835,6 @@@ extern void fill_stat_cache_info(struc
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
  
 -/*
 - * Opportunistically update the index but do not complain if we can't.
 - * The lockfile is always committed or rolled back.
 - */
 -extern void update_index_if_able(struct index_state *, struct lock_file *);
 -
 -extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
  extern int verify_index_checksum;
@@@ -962,10 -961,6 +962,10 @@@ extern char *repository_format_partial_
  extern const char *core_partial_clone_filter_default;
  extern int repository_format_worktree_config;
  
 +/*
 + * You _have_ to initialize a `struct repository_format` using
 + * `= REPOSITORY_FORMAT_INIT` before calling `read_repository_format()`.
 + */
  struct repository_format {
        int version;
        int precious_objects;
        struct string_list unknown_extensions;
  };
  
 +/*
 + * Always use this to initialize a `struct repository_format`
 + * to a well-defined, default state before calling
 + * `read_repository()`.
 + */
 +#define REPOSITORY_FORMAT_INIT \
 +{ \
 +      .version = -1, \
 +      .is_bare = -1, \
 +      .hash_algo = GIT_HASH_SHA1, \
 +      .unknown_extensions = STRING_LIST_INIT_DUP, \
 +}
 +
  /*
   * Read the repository format characteristics from the config file "path" into
 - * "format" struct. Returns the numeric version. On error, -1 is returned,
 - * format->version is set to -1, and all other fields in the struct are
 - * undefined.
 + * "format" struct. Returns the numeric version. On error, or if no version is
 + * found in the configuration, -1 is returned, format->version is set to -1,
 + * and all other fields in the struct are set to the default configuration
 + * (REPOSITORY_FORMAT_INIT). Always initialize the struct using
 + * REPOSITORY_FORMAT_INIT before calling this function.
   */
  int read_repository_format(struct repository_format *format, const char *path);
  
 +/*
 + * Free the memory held onto by `format`, but not the struct itself.
 + * (No need to use this after `read_repository_format()` fails.)
 + */
 +void clear_repository_format(struct repository_format *format);
 +
  /*
   * Verify that the repository described by repository_format is something we
   * can read. If it is, return 0. Otherwise, return -1, and "err" will describe
@@@ -1054,12 -1028,16 +1054,12 @@@ extern const struct object_id null_oid
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
        /*
 -       * This is a temporary optimization hack. By asserting the size here,
 -       * we let the compiler know that it's always going to be 20, which lets
 -       * it turn this fixed-size memcmp into a few inline instructions.
 -       *
 -       * This will need to be extended or ripped out when we learn about
 -       * hashes of different sizes.
 +       * Teach the compiler that there are only two possibilities of hash size
 +       * here, so that it can optimize for this case as much as possible.
         */
 -      if (the_hash_algo->rawsz != 20)
 -              BUG("hash size not yet supported by hashcmp");
 -      return memcmp(sha1, sha2, the_hash_algo->rawsz);
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
 +      return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
  
  static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
  {
 -      return !hashcmp(sha1, sha2);
 +      /*
 +       * We write this here instead of deferring to hashcmp so that the
 +       * compiler can properly inline it and avoid calling memcmp.
 +       */
 +      if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
 +              return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
 +      return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
  static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
@@@ -1100,7 -1072,7 +1100,7 @@@ static inline void hashcpy(unsigned cha
  
  static inline void oidcpy(struct object_id *dst, const struct object_id *src)
  {
 -      hashcpy(dst->hash, src->hash);
 +      memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
  }
  
  static inline struct object_id *oiddup(const struct object_id *src)
@@@ -1297,8 -1269,8 +1297,8 @@@ extern char *xdg_cache_home(const char 
  
  extern int git_open_cloexec(const char *name, int flags);
  #define git_open(name) git_open_cloexec(name, O_RDONLY)
 -extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 -extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
 +extern int unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 +extern int parse_loose_header(const char *hdr, unsigned long *sizep);
  
  extern int check_object_signature(const struct object_id *oid, void *buf, unsigned long size, const char *type);
  
@@@ -1360,34 -1332,16 +1360,35 @@@ struct object_context 
        GET_OID_TREE | GET_OID_TREEISH | \
        GET_OID_BLOB)
  
 +enum get_oid_result {
 +      FOUND = 0,
 +      MISSING_OBJECT = -1, /* The requested object is missing */
 +      SHORT_NAME_AMBIGUOUS = -2,
 +      /* The following only apply when symlinks are followed */
 +      DANGLING_SYMLINK = -4, /*
 +                              * The initial symlink is there, but
 +                              * (transitively) points to a missing
 +                              * in-tree file
 +                              */
 +      SYMLINK_LOOP = -5,
 +      NOT_DIR = -6, /*
 +                     * Somewhere along the symlink chain, a path is
 +                     * requested which contains a file as a
 +                     * non-final element.
 +                     */
 +};
 +
  extern int get_oid(const char *str, struct object_id *oid);
+ extern int get_oidf(struct object_id *oid, const char *fmt, ...);
  extern int get_oid_commit(const char *str, struct object_id *oid);
  extern int get_oid_committish(const char *str, struct object_id *oid);
  extern int get_oid_tree(const char *str, struct object_id *oid);
  extern int get_oid_treeish(const char *str, struct object_id *oid);
  extern int get_oid_blob(const char *str, struct object_id *oid);
  extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 -extern int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
 -
 +extern enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
 +                              unsigned flags, struct object_id *oid,
 +                              struct object_context *oc);
  
  typedef int each_abbrev_fn(const struct object_id *oid, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
@@@ -1412,9 -1366,9 +1413,9 @@@ extern int get_oid_hex(const char *hex
  extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
  
  /*
 - * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
 + * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
 - * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
 + * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
   * convenience.
   *
   * The non-`_r` variant returns a static buffer, but uses a ring of 4
   *
   *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
   */
 -extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 -extern char *oid_to_hex_r(char *out, const struct object_id *oid);
 -extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
 -extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
 +char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
 +char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 +char *oid_to_hex_r(char *out, const struct object_id *oid);
 +char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);     /* static buffer result! */
 +char *sha1_to_hex(const unsigned char *sha1);                                         /* same static buffer */
 +char *hash_to_hex(const unsigned char *hash);                                         /* same static buffer */
 +char *oid_to_hex(const struct object_id *oid);                                                /* same static buffer */
  
  /*
   * Parse a 40-character hexadecimal object ID starting from hex, updating the
@@@ -1489,7 -1440,6 +1490,7 @@@ extern struct object *peel_to_type(cons
  
  enum date_mode_type {
        DATE_NORMAL = 0,
 +      DATE_HUMAN,
        DATE_RELATIVE,
        DATE_SHORT,
        DATE_ISO8601,
@@@ -1515,9 -1465,7 +1516,9 @@@ struct date_mode 
  struct date_mode *date_mode_from_type(enum date_mode_type type);
  
  const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
 -void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
 +void show_date_relative(timestamp_t time, const struct timeval *now,
 +                      struct strbuf *timebuf);
 +void show_date_human(timestamp_t time, int tz, const struct timeval *now,
                        struct strbuf *timebuf);
  int parse_date(const char *date, struct strbuf *out);
  int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
@@@ -1532,19 -1480,10 +1533,19 @@@ int date_overflows(timestamp_t date)
  #define IDENT_STRICT         1
  #define IDENT_NO_DATE        2
  #define IDENT_NO_NAME        4
 +
 +enum want_ident {
 +      WANT_BLANK_IDENT,
 +      WANT_AUTHOR_IDENT,
 +      WANT_COMMITTER_IDENT
 +};
 +
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
 -extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 -extern const char *fmt_name(const char *name, const char *email);
 +extern const char *fmt_ident(const char *name, const char *email,
 +              enum want_ident whose_ident,
 +              const char *date_str, int);
 +extern const char *fmt_name(enum want_ident);
  extern const char *ident_default_name(void);
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
@@@ -1552,6 -1491,10 +1553,10 @@@ extern const char *git_sequence_editor(
  extern const char *git_pager(int stdout_is_tty);
  extern int is_terminal_dumb(void);
  extern int git_ident_config(const char *, const char *, void *);
+ /*
+  * Prepare an ident to fall back on if the user didn't configure it.
+  */
+ void prepare_fallback_ident(const char *name, const char *email);
  extern void reset_ident_date(void);
  
  struct ident_split {
@@@ -1601,14 -1544,9 +1606,14 @@@ struct checkout 
  #define CHECKOUT_INIT { NULL, "" }
  
  #define TEMPORARY_FILENAME_LENGTH 25
 -extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
 +extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
  extern void enable_delayed_checkout(struct checkout *state);
 -extern int finish_delayed_checkout(struct checkout *state);
 +extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
 +/*
 + * Unlink the last component and schedule the leading directories for
 + * removal, such that empty directories get removed.
 + */
 +extern void unlink_entry(const struct cache_entry *ce);
  
  struct cache_def {
        struct strbuf path;
@@@ -1659,7 -1597,7 +1664,7 @@@ extern int odb_mkstemp(struct strbuf *t
  extern int odb_pack_keep(const char *name);
  
  /*
 - * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
 + * Set this to 0 to prevent oid_object_info_extended() from fetching missing
   * blobs. This has a difference only if extensions.partialClone is set.
   *
   * Its default value is 1.
@@@ -1855,7 -1793,4 +1860,7 @@@ void safe_create_dir(const char *dir, i
   */
  extern int print_sha1_ellipsis(void);
  
 +/* Return 1 if the file is empty or does not exists, 0 otherwise. */
 +extern int is_empty_or_missing_file(const char *filename);
 +
  #endif /* CACHE_H */
diff --combined git.c
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -62,13 -62,6 +62,13 @@@ static int list_cmds(const char *spec
  {
        struct string_list list = STRING_LIST_INIT_DUP;
        int i;
 +      int nongit;
 +
 +      /*
 +      * Set up the repository so we can pick up any repo-level config (like
 +      * completion.commands).
 +      */
 +      setup_git_directory_gently(&nongit);
  
        while (*spec) {
                const char *sep = strchrnul(spec, ',');
        return 0;
  }
  
 -static void commit_pager_choice(void) {
 +static void commit_pager_choice(void)
 +{
        switch (use_pager) {
        case 0:
                setenv("GIT_PAGER", "cat", 1);
@@@ -154,20 -146,16 +154,20 @@@ static int handle_options(const char **
                                git_set_exec_path(cmd + 1);
                        else {
                                puts(git_exec_path());
 +                              trace2_cmd_name("_query_");
                                exit(0);
                        }
                } else if (!strcmp(cmd, "--html-path")) {
                        puts(system_path(GIT_HTML_PATH));
 +                      trace2_cmd_name("_query_");
                        exit(0);
                } else if (!strcmp(cmd, "--man-path")) {
                        puts(system_path(GIT_MAN_PATH));
 +                      trace2_cmd_name("_query_");
                        exit(0);
                } else if (!strcmp(cmd, "--info-path")) {
                        puts(system_path(GIT_INFO_PATH));
 +                      trace2_cmd_name("_query_");
                        exit(0);
                } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
                        use_pager = 1;
                        (*argv)++;
                        (*argc)--;
                } else if (skip_prefix(cmd, "--list-cmds=", &cmd)) {
 +                      trace2_cmd_name("_query_");
                        if (!strcmp(cmd, "parseopt")) {
                                struct string_list list = STRING_LIST_INIT_DUP;
                                int i;
@@@ -344,39 -331,34 +344,39 @@@ static int handle_alias(int *argcp, con
                        commit_pager_choice();
  
                        child.use_shell = 1;
 +                      child.trace2_child_class = "shell_alias";
                        argv_array_push(&child.args, alias_string + 1);
                        argv_array_pushv(&child.args, (*argv) + 1);
  
 +                      trace2_cmd_alias(alias_command, child.args.argv);
 +                      trace2_cmd_list_config();
 +                      trace2_cmd_name("_run_shell_alias_");
 +
                        ret = run_command(&child);
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
  
 -                      die_errno("while expanding alias '%s': '%s'",
 -                          alias_command, alias_string + 1);
 +                      die_errno(_("while expanding alias '%s': '%s'"),
 +                                alias_command, alias_string + 1);
                }
                count = split_cmdline(alias_string, &new_argv);
                if (count < 0)
 -                      die("Bad alias.%s string: %s", alias_command,
 -                          split_cmdline_strerror(count));
 +                      die(_("bad alias.%s string: %s"), alias_command,
 +                          _(split_cmdline_strerror(count)));
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
 -                      die("alias '%s' changes environment variables.\n"
 -                               "You can use '!git' in the alias to do this",
 -                               alias_command);
 +                      die(_("alias '%s' changes environment variables.\n"
 +                            "You can use '!git' in the alias to do this"),
 +                          alias_command);
                memmove(new_argv - option_count, new_argv,
                                count * sizeof(char *));
                new_argv -= option_count;
  
                if (count < 1)
 -                      die("empty alias for %s", alias_command);
 +                      die(_("empty alias for %s"), alias_command);
  
                if (!strcmp(alias_command, new_argv[0]))
 -                      die("recursive alias: %s", alias_command);
 +                      die(_("recursive alias: %s"), alias_command);
  
                trace_argv_printf(new_argv,
                                  "trace: alias expansion: %s =>",
                /* insert after command name */
                memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
  
 +              trace2_cmd_alias(alias_command, new_argv);
 +              trace2_cmd_list_config();
 +
                *argv = new_argv;
                *argcp += count - 1;
  
@@@ -430,19 -409,17 +430,19 @@@ static int run_builtin(struct cmd_struc
  
        if (!help && get_super_prefix()) {
                if (!(p->option & SUPPORT_SUPER_PREFIX))
 -                      die("%s doesn't support --super-prefix", p->cmd);
 +                      die(_("%s doesn't support --super-prefix"), p->cmd);
        }
  
        if (!help && p->option & NEED_WORK_TREE)
                setup_work_tree();
  
        trace_argv_printf(argv, "trace: built-in: git");
 +      trace2_cmd_name(p->cmd);
 +      trace2_cmd_list_config();
  
 -      validate_cache_entries(&the_index);
 +      validate_cache_entries(the_repository->index);
        status = p->fn(argc, argv, prefix);
 -      validate_cache_entries(&the_index);
 +      validate_cache_entries(the_repository->index);
  
        if (status)
                return status;
  
        /* Check for ENOSPC and EIO errors.. */
        if (fflush(stdout))
 -              die_errno("write failure on standard output");
 +              die_errno(_("write failure on standard output"));
        if (ferror(stdout))
 -              die("unknown write failure on standard output");
 +              die(_("unknown write failure on standard output"));
        if (fclose(stdout))
 -              die_errno("close failed on standard output");
 +              die_errno(_("close failed on standard output"));
        return 0;
  }
  
@@@ -577,6 -554,12 +577,12 @@@ static struct cmd_struct commands[] = 
        { "show-index", cmd_show_index },
        { "show-ref", cmd_show_ref, RUN_SETUP },
        { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+       /*
+        * NEEDSWORK: Until the builtin stash is thoroughly robust and no
+        * longer needs redirection to the stash shell script this is kept as
+        * is, then should be changed to RUN_SETUP | NEED_WORK_TREE
+        */
+       { "stash", cmd_stash },
        { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
        { "stripspace", cmd_stripspace },
        { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
@@@ -677,7 -660,7 +683,7 @@@ static void execv_dashed_external(cons
        int status;
  
        if (get_super_prefix())
 -              die("%s doesn't support --super-prefix", argv[0]);
 +              die(_("%s doesn't support --super-prefix"), argv[0]);
  
        if (use_pager == -1 && !is_builtin(argv[0]))
                use_pager = check_pager_config(argv[0]);
        cmd.clean_on_exit = 1;
        cmd.wait_after_clean = 1;
        cmd.silent_exec_failure = 1;
 +      cmd.trace2_child_class = "dashed";
  
 +      trace2_cmd_name("_run_dashed_");
 +
 +      /*
 +       * The code in run_command() logs trace2 child_start/child_exit
 +       * events, so we do not need to report exec/exec_result events here.
 +       */
        trace_argv_printf(cmd.args.argv, "trace: exec:");
  
        /*
         * the program.
         */
        status = run_command(&cmd);
 +
 +      /*
 +       * If the child process ran and we are now going to exit, emit a
 +       * generic string as our trace2 command verb to indicate that we
 +       * launched a dashed command.
 +       */
        if (status >= 0)
                exit(status);
        else if (errno != ENOENT)
@@@ -736,43 -706,6 +742,43 @@@ static int run_argv(int *argcp, const c
                if (!done_alias)
                        handle_builtin(*argcp, *argv);
  
 +#if 0 // TODO In GFW, need to amend a7924b655e940b06cb547c235d6bed9767929673 to include trace2_ and _tr2 lines.
 +              else if (get_builtin(**argv)) {
 +                      struct argv_array args = ARGV_ARRAY_INIT;
 +                      int i;
 +
 +                      /*
 +                       * The current process is committed to launching a
 +                       * child process to run the command named in (**argv)
 +                       * and exiting.  Log a generic string as the trace2
 +                       * command verb to indicate this.  Note that the child
 +                       * process will log the actual verb when it runs.
 +                       */
 +                      trace2_cmd_name("_run_git_alias_");
 +
 +                      if (get_super_prefix())
 +                              die("%s doesn't support --super-prefix", **argv);
 +
 +                      commit_pager_choice();
 +
 +                      argv_array_push(&args, "git");
 +                      for (i = 0; i < *argcp; i++)
 +                              argv_array_push(&args, (*argv)[i]);
 +
 +                      trace_argv_printf(args.argv, "trace: exec:");
 +
 +                      /*
 +                       * if we fail because the command is not found, it is
 +                       * OK to return. Otherwise, we just pass along the status code.
 +                       */
 +                      i = run_command_v_opt_tr2(args.argv, RUN_SILENT_EXEC_FAILURE |
 +                                                RUN_CLEAN_ON_EXIT, "git_alias");
 +                      if (i >= 0 || errno != ENOENT)
 +                              exit(i);
 +                      die("could not execute builtin %s", **argv);
 +              }
 +#endif // a7924b655e940b06cb547c235d6bed9767929673
 +
                /* .. then try the external ones */
                execv_dashed_external(*argv);
  
@@@ -839,7 -772,7 +845,7 @@@ int cmd_main(int argc, const char **arg
        if (skip_prefix(cmd, "git-", &cmd)) {
                argv[0] = cmd;
                handle_builtin(argc, argv);
 -              die("cannot handle %s as a builtin", cmd);
 +              die(_("cannot handle %s as a builtin"), cmd);
        }
  
        /* Look for flags.. */
        } else {
                /* The user didn't specify a command; give them help */
                commit_pager_choice();
 -              printf("usage: %s\n\n", git_usage_string);
 +              printf(_("usage: %s\n\n"), git_usage_string);
                list_common_cmds_help();
                printf("\n%s\n", _(git_more_info_string));
                exit(1);
diff --combined ident.c
+++ b/ident.c
  static struct strbuf git_default_name = STRBUF_INIT;
  static struct strbuf git_default_email = STRBUF_INIT;
  static struct strbuf git_default_date = STRBUF_INIT;
 +static struct strbuf git_author_name = STRBUF_INIT;
 +static struct strbuf git_author_email = STRBUF_INIT;
 +static struct strbuf git_committer_name = STRBUF_INIT;
 +static struct strbuf git_committer_email = STRBUF_INIT;
  static int default_email_is_bogus;
  static int default_name_is_bogus;
  
@@@ -359,19 -355,13 +359,19 @@@ N_("\n
     "\n");
  
  const char *fmt_ident(const char *name, const char *email,
 -                    const char *date_str, int flag)
 +                    enum want_ident whose_ident, const char *date_str, int flag)
  {
        static struct strbuf ident = STRBUF_INIT;
        int strict = (flag & IDENT_STRICT);
        int want_date = !(flag & IDENT_NO_DATE);
        int want_name = !(flag & IDENT_NO_NAME);
  
 +      if (!email) {
 +              if (whose_ident == WANT_AUTHOR_IDENT && git_author_email.len)
 +                      email = git_author_email.buf;
 +              else if (whose_ident == WANT_COMMITTER_IDENT && git_committer_email.len)
 +                      email = git_committer_email.buf;
 +      }
        if (!email) {
                if (strict && ident_use_config_only
                    && !(ident_config_given & IDENT_MAIL_GIVEN)) {
  
        if (want_name) {
                int using_default = 0;
 +              if (!name) {
 +                      if (whose_ident == WANT_AUTHOR_IDENT && git_author_name.len)
 +                              name = git_author_name.buf;
 +                      else if (whose_ident == WANT_COMMITTER_IDENT &&
 +                                      git_committer_name.len)
 +                              name = git_committer_name.buf;
 +              }
                if (!name) {
                        if (strict && ident_use_config_only
                            && !(ident_config_given & IDENT_NAME_GIVEN)) {
        return ident.buf;
  }
  
 -const char *fmt_name(const char *name, const char *email)
 +const char *fmt_name(enum want_ident whose_ident)
  {
 -      return fmt_ident(name, email, NULL, IDENT_STRICT | IDENT_NO_DATE);
 +      char *name = NULL;
 +      char *email = NULL;
 +
 +      switch (whose_ident) {
 +      case WANT_BLANK_IDENT:
 +              break;
 +      case WANT_AUTHOR_IDENT:
 +              name = getenv("GIT_AUTHOR_NAME");
 +              email = getenv("GIT_AUTHOR_EMAIL");
 +              break;
 +      case WANT_COMMITTER_IDENT:
 +              name = getenv("GIT_COMMITTER_NAME");
 +              email = getenv("GIT_COMMITTER_EMAIL");
 +              break;
 +      }
 +      return fmt_ident(name, email, whose_ident, NULL,
 +                      IDENT_STRICT | IDENT_NO_DATE);
  }
  
  const char *git_author_info(int flag)
                author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
        return fmt_ident(getenv("GIT_AUTHOR_NAME"),
                         getenv("GIT_AUTHOR_EMAIL"),
 +                       WANT_AUTHOR_IDENT,
                         getenv("GIT_AUTHOR_DATE"),
                         flag);
  }
@@@ -484,7 -450,6 +484,7 @@@ const char *git_committer_info(int flag
                committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
        return fmt_ident(getenv("GIT_COMMITTER_NAME"),
                         getenv("GIT_COMMITTER_EMAIL"),
 +                       WANT_COMMITTER_IDENT,
                         getenv("GIT_COMMITTER_DATE"),
                         flag);
  }
@@@ -508,45 -473,10 +508,45 @@@ int author_ident_sufficiently_given(voi
        return ident_is_sufficient(author_ident_explicitly_given);
  }
  
 -int git_ident_config(const char *var, const char *value, void *data)
 +static int set_ident(const char *var, const char *value)
  {
 -      if (!strcmp(var, "user.useconfigonly")) {
 -              ident_use_config_only = git_config_bool(var, value);
 +      if (!strcmp(var, "author.name")) {
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              strbuf_reset(&git_author_name);
 +              strbuf_addstr(&git_author_name, value);
 +              author_ident_explicitly_given |= IDENT_NAME_GIVEN;
 +              ident_config_given |= IDENT_NAME_GIVEN;
 +              return 0;
 +      }
 +
 +      if (!strcmp(var, "author.email")) {
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              strbuf_reset(&git_author_email);
 +              strbuf_addstr(&git_author_email, value);
 +              author_ident_explicitly_given |= IDENT_MAIL_GIVEN;
 +              ident_config_given |= IDENT_MAIL_GIVEN;
 +              return 0;
 +      }
 +
 +      if (!strcmp(var, "committer.name")) {
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              strbuf_reset(&git_committer_name);
 +              strbuf_addstr(&git_committer_name, value);
 +              committer_ident_explicitly_given |= IDENT_NAME_GIVEN;
 +              ident_config_given |= IDENT_NAME_GIVEN;
 +              return 0;
 +      }
 +
 +      if (!strcmp(var, "committer.email")) {
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              strbuf_reset(&git_committer_email);
 +              strbuf_addstr(&git_committer_email, value);
 +              committer_ident_explicitly_given |= IDENT_MAIL_GIVEN;
 +              ident_config_given |= IDENT_MAIL_GIVEN;
                return 0;
        }
  
        return 0;
  }
  
 +int git_ident_config(const char *var, const char *value, void *data)
 +{
 +      if (!strcmp(var, "user.useconfigonly")) {
 +              ident_use_config_only = git_config_bool(var, value);
 +              return 0;
 +      }
 +
 +      return set_ident(var, value);
 +}
 +
+ static void set_env_if(const char *key, const char *value, int *given, int bit)
+ {
+       if ((*given & bit) || getenv(key))
+               return; /* nothing to do */
+       setenv(key, value, 0);
+       *given |= bit;
+ }
+ void prepare_fallback_ident(const char *name, const char *email)
+ {
+       set_env_if("GIT_AUTHOR_NAME", name,
+                  &author_ident_explicitly_given, IDENT_NAME_GIVEN);
+       set_env_if("GIT_AUTHOR_EMAIL", email,
+                  &author_ident_explicitly_given, IDENT_MAIL_GIVEN);
+       set_env_if("GIT_COMMITTER_NAME", name,
+                  &committer_ident_explicitly_given, IDENT_NAME_GIVEN);
+       set_env_if("GIT_COMMITTER_EMAIL", email,
+                  &committer_ident_explicitly_given, IDENT_MAIL_GIVEN);
+ }
  static int buf_cmp(const char *a_begin, const char *a_end,
                   const char *b_begin, const char *b_end)
  {
diff --combined sha1-name.c
@@@ -83,25 -83,51 +83,25 @@@ static void update_candidates(struct di
        /* otherwise, current can be discarded and candidate is still good */
  }
  
 -static int append_loose_object(const struct object_id *oid, const char *path,
 -                             void *data)
 -{
 -      oid_array_append(data, oid);
 -      return 0;
 -}
 -
  static int match_sha(unsigned, const unsigned char *, const unsigned char *);
  
  static void find_short_object_filename(struct disambiguate_state *ds)
  {
 -      int subdir_nr = ds->bin_pfx.hash[0];
 -      struct alternate_object_database *alt;
 -      static struct alternate_object_database *fakeent;
 +      struct object_directory *odb;
  
 -      if (!fakeent) {
 -              /*
 -               * Create a "fake" alternate object database that
 -               * points to our own object database, to make it
 -               * easier to get a temporary working space in
 -               * alt->name/alt->base while iterating over the
 -               * object databases including our own.
 -               */
 -              fakeent = alloc_alt_odb(get_object_directory());
 -      }
 -      fakeent->next = the_repository->objects->alt_odb_list;
 -
 -      for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
 +      for (odb = the_repository->objects->odb;
 +           odb && !ds->ambiguous;
 +           odb = odb->next) {
                int pos;
 +              struct oid_array *loose_objects;
  
 -              if (!alt->loose_objects_subdir_seen[subdir_nr]) {
 -                      struct strbuf *buf = alt_scratch_buf(alt);
 -                      for_each_file_in_obj_subdir(subdir_nr, buf,
 -                                                  append_loose_object,
 -                                                  NULL, NULL,
 -                                                  &alt->loose_objects_cache);
 -                      alt->loose_objects_subdir_seen[subdir_nr] = 1;
 -              }
 -
 -              pos = oid_array_lookup(&alt->loose_objects_cache, &ds->bin_pfx);
 +              loose_objects = odb_loose_cache(odb, &ds->bin_pfx);
 +              pos = oid_array_lookup(loose_objects, &ds->bin_pfx);
                if (pos < 0)
                        pos = -1 - pos;
 -              while (!ds->ambiguous && pos < alt->loose_objects_cache.nr) {
 +              while (!ds->ambiguous && pos < loose_objects->nr) {
                        const struct object_id *oid;
 -                      oid = alt->loose_objects_cache.oid + pos;
 +                      oid = loose_objects->oid + pos;
                        if (!match_sha(ds->len, ds->bin_pfx.hash, oid->hash))
                                break;
                        update_candidates(ds, oid);
@@@ -190,6 -216,9 +190,6 @@@ static void find_short_packed_object(st
                unique_in_pack(p, ds);
  }
  
 -#define SHORT_NAME_NOT_FOUND (-1)
 -#define SHORT_NAME_AMBIGUOUS (-2)
 -
  static int finish_object_disambiguation(struct disambiguate_state *ds,
                                        struct object_id *oid)
  {
                return SHORT_NAME_AMBIGUOUS;
  
        if (!ds->candidate_exists)
 -              return SHORT_NAME_NOT_FOUND;
 +              return MISSING_OBJECT;
  
        if (!ds->candidate_checked)
                /*
@@@ -411,9 -440,8 +411,9 @@@ static int sort_ambiguous(const void *a
        return a_type_sort > b_type_sort ? 1 : -1;
  }
  
 -static int get_short_oid(const char *name, int len, struct object_id *oid,
 -                        unsigned flags)
 +static enum get_oid_result get_short_oid(const char *name, int len,
 +                                       struct object_id *oid,
 +                                       unsigned flags)
  {
        int status;
        struct disambiguate_state ds;
        find_short_packed_object(&ds);
        status = finish_object_disambiguation(&ds, oid);
  
 +      /*
 +       * If we didn't find it, do the usual reprepare() slow-path,
 +       * since the object may have recently been added to the repository
 +       * or migrated from loose to packed.
 +       */
 +      if (status == MISSING_OBJECT) {
 +              reprepare_packed_git(the_repository);
 +              find_short_object_filename(&ds);
 +              find_short_packed_object(&ds);
 +              status = finish_object_disambiguation(&ds, oid);
 +      }
 +
        if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
                struct oid_array collect = OID_ARRAY_INIT;
  
@@@ -743,7 -759,7 +743,7 @@@ static inline int push_mark(const char 
        return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
  }
  
 -static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags);
 +static enum get_oid_result get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags);
  static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
  
  static int get_oid_basic(const char *str, int len, struct object_id *oid,
        return 0;
  }
  
 -static int get_parent(const char *name, int len,
 -                    struct object_id *result, int idx)
 +static enum get_oid_result get_parent(const char *name, int len,
 +                                    struct object_id *result, int idx)
  {
        struct object_id oid;
 -      int ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH);
 +      enum get_oid_result ret = get_oid_1(name, len, &oid,
 +                                          GET_OID_COMMITTISH);
        struct commit *commit;
        struct commit_list *p;
  
                return ret;
        commit = lookup_commit_reference(the_repository, &oid);
        if (parse_commit(commit))
 -              return -1;
 +              return MISSING_OBJECT;
        if (!idx) {
                oidcpy(result, &commit->object.oid);
 -              return 0;
 +              return FOUND;
        }
        p = commit->parents;
        while (p) {
                if (!--idx) {
                        oidcpy(result, &p->item->object.oid);
 -                      return 0;
 +                      return FOUND;
                }
                p = p->next;
        }
 -      return -1;
 +      return MISSING_OBJECT;
  }
  
 -static int get_nth_ancestor(const char *name, int len,
 -                          struct object_id *result, int generation)
 +static enum get_oid_result get_nth_ancestor(const char *name, int len,
 +                                          struct object_id *result,
 +                                          int generation)
  {
        struct object_id oid;
        struct commit *commit;
                return ret;
        commit = lookup_commit_reference(the_repository, &oid);
        if (!commit)
 -              return -1;
 +              return MISSING_OBJECT;
  
        while (generation--) {
                if (parse_commit(commit) || !commit->parents)
 -                      return -1;
 +                      return MISSING_OBJECT;
                commit = commit->parents->item;
        }
        oidcpy(result, &commit->object.oid);
 -      return 0;
 +      return FOUND;
  }
  
  struct object *peel_to_type(const char *name, int namelen,
@@@ -1089,9 -1103,7 +1089,9 @@@ static int get_describe_name(const cha
        return -1;
  }
  
 -static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags)
 +static enum get_oid_result get_oid_1(const char *name, int len,
 +                                   struct object_id *oid,
 +                                   unsigned lookup_flags)
  {
        int ret, has_suffix;
        const char *cp;
  
        ret = peel_onion(name, len, oid, lookup_flags);
        if (!ret)
 -              return 0;
 +              return FOUND;
  
        ret = get_oid_basic(name, len, oid, lookup_flags);
        if (!ret)
 -              return 0;
 +              return FOUND;
  
        /* It could be describe output that is "SOMETHING-gXXXX" */
        ret = get_describe_name(name, len, oid);
        if (!ret)
 -              return 0;
 +              return FOUND;
  
        return get_short_oid(name, len, oid, lookup_flags);
  }
@@@ -1527,9 -1539,28 +1527,28 @@@ int strbuf_check_branch_ref(struct strb
  int get_oid(const char *name, struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(name, 0, oid, &unused);
 +      return get_oid_with_context(the_repository, name, 0, oid, &unused);
  }
  
+ /*
+  * This returns a non-zero value if the string (built using printf
+  * format and the given arguments) is not a valid object.
+  */
+ int get_oidf(struct object_id *oid, const char *fmt, ...)
+ {
+       va_list ap;
+       int ret;
+       struct strbuf sb = STRBUF_INIT;
+       va_start(ap, fmt);
+       strbuf_vaddf(&sb, fmt, ap);
+       va_end(ap);
+       ret = get_oid(sb.buf, oid);
+       strbuf_release(&sb);
+       return ret;
+ }
  
  /*
   * Many callers know that the user meant to name a commit-ish by
  int get_oid_committish(const char *name, struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(name, GET_OID_COMMITTISH,
 +      return get_oid_with_context(the_repository,
 +                                  name, GET_OID_COMMITTISH,
                                    oid, &unused);
  }
  
  int get_oid_treeish(const char *name, struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(name, GET_OID_TREEISH,
 +      return get_oid_with_context(the_repository,
 +                                  name, GET_OID_TREEISH,
                                    oid, &unused);
  }
  
  int get_oid_commit(const char *name, struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(name, GET_OID_COMMIT,
 +      return get_oid_with_context(the_repository,
 +                                  name, GET_OID_COMMIT,
                                    oid, &unused);
  }
  
  int get_oid_tree(const char *name, struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(name, GET_OID_TREE,
 +      return get_oid_with_context(the_repository,
 +                                  name, GET_OID_TREE,
                                    oid, &unused);
  }
  
  int get_oid_blob(const char *name, struct object_id *oid)
  {
        struct object_context unused;
 -      return get_oid_with_context(name, GET_OID_BLOB,
 +      return get_oid_with_context(the_repository,
 +                                  name, GET_OID_BLOB,
                                    oid, &unused);
  }
  
@@@ -1616,8 -1642,7 +1635,8 @@@ static void diagnose_invalid_oid_path(c
  }
  
  /* Must be called only when :stage:filename doesn't exist. */
 -static void diagnose_invalid_index_path(int stage,
 +static void diagnose_invalid_index_path(struct index_state *istate,
 +                                      int stage,
                                        const char *prefix,
                                        const char *filename)
  {
                prefix = "";
  
        /* Wrong stage number? */
 -      pos = cache_name_pos(filename, namelen);
 +      pos = index_name_pos(istate, filename, namelen);
        if (pos < 0)
                pos = -pos - 1;
 -      if (pos < active_nr) {
 -              ce = active_cache[pos];
 +      if (pos < istate->cache_nr) {
 +              ce = istate->cache[pos];
                if (ce_namelen(ce) == namelen &&
                    !memcmp(ce->name, filename, namelen))
                        die("Path '%s' is in the index, but not at stage %d.\n"
        /* Confusion between relative and absolute filenames? */
        strbuf_addstr(&fullname, prefix);
        strbuf_addstr(&fullname, filename);
 -      pos = cache_name_pos(fullname.buf, fullname.len);
 +      pos = index_name_pos(istate, fullname.buf, fullname.len);
        if (pos < 0)
                pos = -pos - 1;
 -      if (pos < active_nr) {
 -              ce = active_cache[pos];
 +      if (pos < istate->cache_nr) {
 +              ce = istate->cache[pos];
                if (ce_namelen(ce) == fullname.len &&
                    !memcmp(ce->name, fullname.buf, fullname.len))
                        die("Path '%s' is in the index, but not '%s'.\n"
@@@ -1684,8 -1709,7 +1703,8 @@@ static char *resolve_relative_path(cons
                           rel);
  }
  
 -static int get_oid_with_context_1(const char *name,
 +static enum get_oid_result get_oid_with_context_1(struct repository *repo,
 +                                const char *name,
                                  unsigned flags,
                                  const char *prefix,
                                  struct object_id *oid,
                if (flags & GET_OID_RECORD_PATH)
                        oc->path = xstrdup(cp);
  
 -              if (!active_cache)
 -                      read_cache();
 -              pos = cache_name_pos(cp, namelen);
 +              if (!repo->index->cache)
 +                      repo_read_index(the_repository);
 +              pos = index_name_pos(repo->index, cp, namelen);
                if (pos < 0)
                        pos = -pos - 1;
 -              while (pos < active_nr) {
 -                      ce = active_cache[pos];
 +              while (pos < repo->index->cache_nr) {
 +                      ce = repo->index->cache[pos];
                        if (ce_namelen(ce) != namelen ||
                            memcmp(ce->name, cp, namelen))
                                break;
                        pos++;
                }
                if (only_to_die && name[1] && name[1] != '/')
 -                      diagnose_invalid_index_path(stage, prefix, cp);
 +                      diagnose_invalid_index_path(repo->index, stage, prefix, cp);
                free(new_path);
                return -1;
        }
@@@ -1828,17 -1852,12 +1847,17 @@@ void maybe_die_on_misspelt_object_name(
  {
        struct object_context oc;
        struct object_id oid;
 -      get_oid_with_context_1(name, GET_OID_ONLY_TO_DIE, prefix, &oid, &oc);
 +      get_oid_with_context_1(the_repository, name, GET_OID_ONLY_TO_DIE,
 +                             prefix, &oid, &oc);
  }
  
 -int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc)
 +enum get_oid_result get_oid_with_context(struct repository *repo,
 +                                       const char *str,
 +                                       unsigned flags,
 +                                       struct object_id *oid,
 +                                       struct object_context *oc)
  {
        if (flags & GET_OID_FOLLOW_SYMLINKS && flags & GET_OID_ONLY_TO_DIE)
                BUG("incompatible flags for get_sha1_with_context");
 -      return get_oid_with_context_1(str, flags, NULL, oid, oc);
 +      return get_oid_with_context_1(repo, str, flags, NULL, oid, oc);
  }
diff --combined strbuf.c
+++ b/strbuf.c
@@@ -249,6 -249,42 +249,42 @@@ void strbuf_insert(struct strbuf *sb, s
        strbuf_splice(sb, pos, 0, data, len);
  }
  
+ void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt, va_list ap)
+ {
+       int len, len2;
+       char save;
+       va_list cp;
+       if (pos > sb->len)
+               die("`pos' is too far after the end of the buffer");
+       va_copy(cp, ap);
+       len = vsnprintf(sb->buf + sb->len, 0, fmt, cp);
+       va_end(cp);
+       if (len < 0)
+               BUG("your vsnprintf is broken (returned %d)", len);
+       if (!len)
+               return; /* nothing to do */
+       if (unsigned_add_overflows(sb->len, len))
+               die("you want to use way too much memory");
+       strbuf_grow(sb, len);
+       memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos);
+       /* vsnprintf() will append a NUL, overwriting one of our characters */
+       save = sb->buf[pos + len];
+       len2 = vsnprintf(sb->buf + pos, len + 1, fmt, ap);
+       sb->buf[pos + len] = save;
+       if (len2 != len)
+               BUG("your vsnprintf is broken (returns inconsistent lengths)");
+       strbuf_setlen(sb, sb->len + len);
+ }
+ void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...)
+ {
+       va_list ap;
+       va_start(ap, fmt);
+       strbuf_vinsertf(sb, pos, fmt, ap);
+       va_end(ap);
+ }
  void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
  {
        strbuf_splice(sb, pos, len, "", 0);
@@@ -268,6 -304,21 +304,21 @@@ void strbuf_addbuf(struct strbuf *sb, c
        strbuf_setlen(sb, sb->len + sb2->len);
  }
  
+ const char *strbuf_join_argv(struct strbuf *buf,
+                            int argc, const char **argv, char delim)
+ {
+       if (!argc)
+               return buf->buf;
+       strbuf_addstr(buf, *argv);
+       while (--argc) {
+               strbuf_addch(buf, delim);
+               strbuf_addstr(buf, *(++argv));
+       }
+       return buf->buf;
+ }
  void strbuf_addchars(struct strbuf *sb, int c, size_t n)
  {
        strbuf_grow(sb, n);
@@@ -380,27 -431,6 +431,27 @@@ void strbuf_expand(struct strbuf *sb, c
        }
  }
  
 +size_t strbuf_expand_literal_cb(struct strbuf *sb,
 +                              const char *placeholder,
 +                              void *context)
 +{
 +      int ch;
 +
 +      switch (placeholder[0]) {
 +      case 'n':               /* newline */
 +              strbuf_addch(sb, '\n');
 +              return 1;
 +      case 'x':
 +              /* %x00 == NUL, %x0a == LF, etc. */
 +              ch = hex2chr(placeholder + 1);
 +              if (ch < 0)
 +                      return 0;
 +              strbuf_addch(sb, ch);
 +              return 3;
 +      }
 +      return 0;
 +}
 +
  size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
                void *context)
  {
diff --combined strbuf.h
+++ b/strbuf.h
@@@ -244,6 -244,15 +244,15 @@@ void strbuf_addchars(struct strbuf *sb
   */
  void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);
  
+ /**
+  * Insert data to the given position of the buffer giving a printf format
+  * string. The contents will be shifted, not overwritten.
+  */
+ void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt,
+                    va_list ap);
+ void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...);
  /**
   * Remove given amount of data from a given position of the buffer.
   */
@@@ -288,6 -297,13 +297,13 @@@ static inline void strbuf_addstr(struc
   */
  void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
  
+ /**
+  * Join the arguments into a buffer. `delim` is put between every
+  * two arguments.
+  */
+ const char *strbuf_join_argv(struct strbuf *buf, int argc,
+                            const char **argv, char delim);
  /**
   * This function can be used to expand a format string containing
   * placeholders. To that end, it parses the string and calls the specified
@@@ -320,14 -336,6 +336,14 @@@ void strbuf_expand(struct strbuf *sb
                   expand_fn_t fn,
                   void *context);
  
 +/**
 + * Used as callback for `strbuf_expand` to only expand literals
 + * (i.e. %n and %xNN). The context argument is ignored.
 + */
 +size_t strbuf_expand_literal_cb(struct strbuf *sb,
 +                              const char *placeholder,
 +                              void *context);
 +
  /**
   * Used as callback for `strbuf_expand()`, expects an array of
   * struct strbuf_expand_dict_entry as context, i.e. pairs of
diff --combined t/README
+++ b/t/README
@@@ -170,15 -170,6 +170,15 @@@ appropriately before running "make"
        implied by other options like --valgrind and
        GIT_TEST_INSTALLED.
  
 +--no-bin-wrappers::
 +      By default, the test suite uses the wrappers in
 +      `../bin-wrappers/` to execute `git` and friends. With this option,
 +      `../git` and friends are run directly. This is not recommended
 +      in general, as the wrappers contain safeguards to ensure that no
 +      files from an installed Git are used, but can speed up test runs
 +      especially on platforms where running shell scripts is expensive
 +      (most notably, Windows).
 +
  --root=<directory>::
        Create "trash" directories used to store all temporary data during
        testing under <directory>, instead of the t/ directory.
        this feature by setting the GIT_TEST_CHAIN_LINT environment
        variable to "1" or "0", respectively.
  
 +--stress::
 +      Run the test script repeatedly in multiple parallel jobs until
 +      one of them fails.  Useful for reproducing rare failures in
 +      flaky tests.  The number of parallel jobs is, in order of
 +      precedence: the value of the GIT_TEST_STRESS_LOAD
 +      environment variable, or twice the number of available
 +      processors (as shown by the 'getconf' utility), or 8.
 +      Implies `--verbose -x --immediate` to get the most information
 +      about the failure.  Note that the verbose output of each test
 +      job is saved to 't/test-results/$TEST_NAME.stress-<nr>.out',
 +      and only the output of the failed test job is shown on the
 +      terminal.  The names of the trash directories get a
 +      '.stress-<nr>' suffix, and the trash directory of the failed
 +      test job is renamed to end with a '.stress-failed' suffix.
 +
 +--stress-jobs=<N>::
 +      Override the number of parallel jobs. Implies `--stress`.
 +
 +--stress-limit=<N>::
 +      When combined with --stress run the test script repeatedly
 +      this many times in each of the parallel jobs or until one of
 +      them fails, whichever comes first. Implies `--stress`.
 +
  You can also set the GIT_TEST_INSTALLED environment variable to
  the bindir of an existing git installation to test that installation.
  You still need to have built this git sandbox, from which various
@@@ -343,9 -311,6 +343,9 @@@ marked strings" in po/README for detail
  GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole
  test suite. Accept any boolean values that are accepted by git-config.
  
 +GIT_TEST_PROTOCOL_VERSION=<n>, when set, overrides the
 +'protocol.version' setting to n if it is less than n.
 +
  GIT_TEST_FULL_IN_PACK_ARRAY=<boolean> exercises the uncommon
  pack-objects code path where there are more than 1024 packs even if
  the actual number of packs in repository is below this limit. Accept
@@@ -377,13 -342,17 +377,17 @@@ GIT_TEST_INDEX_VERSION=<n> exercises th
  for the index version specified.  Can be set to any valid version
  (currently 2, 3, or 4).
  
 +GIT_TEST_PACK_SPARSE=<boolean> if enabled will default the pack-objects
 +builtin to use the sparse object walk. This can still be overridden by
 +the --no-sparse command-line argument.
 +
  GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
  by overriding the minimum number of cache entries required per thread.
  
 -GIT_TEST_REBASE_USE_BUILTIN=<boolean>, when false, disables the
 -builtin version of git-rebase. See 'rebase.useBuiltin' in
 -git-config(1).
 -
+ GIT_TEST_STASH_USE_BUILTIN=<boolean>, when false, disables the
+ built-in version of git-stash. See 'stash.useBuiltin' in
+ git-config(1).
  GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
  of the index for the whole test suite by bypassing the default number of
  cache entries and thread minimums. Setting this to 1 will make the
@@@ -393,11 -362,6 +397,11 @@@ GIT_TEST_MULTI_PACK_INDEX=<boolean>, wh
  index to be written after every 'git repack' command, and overrides the
  'core.multiPackIndex' setting to true.
  
 +GIT_TEST_SIDEBAND_ALL=<boolean>, when true, overrides the
 +'uploadpack.allowSidebandAll' setting to true, and when false, forces
 +fetch-pack to not request sideband-all (even if the server advertises
 +sideband-all).
 +
  Naming Tests
  ------------
  
@@@ -465,8 -429,7 +469,8 @@@ This test harness library does the foll
   - Creates an empty test directory with an empty .git/objects database
     and chdir(2) into it.  This directory is 't/trash
     directory.$test_name_without_dotsh', with t/ subject to change by
 -   the --root option documented above.
 +   the --root option documented above, and a '.stress-<N>' suffix
 +   appended by the --stress option.
  
   - Defines standard test helper functions for your scripts to
     use.  These functions are designed to make all scripts behave
@@@ -933,15 -896,6 +937,15 @@@ library for your script to use
     test_oid_init or test_oid_cache.  Providing an unknown key is an
     error.
  
 + - yes [<string>]
 +
 +   This is often seen in modern UNIX but some platforms lack it, so
 +   the test harness overrides the platform implementation with a
 +   more limited one.  Use this only when feeding a handful lines of
 +   output to the downstream---unlike the real version, it generates
 +   only up to 99 lines.
 +
 +
  Prerequisites
  -------------