diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 52251a33315776..4dc6fea129a413 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -164,7 +164,7 @@ phases:
   displayName: Windows
   condition: succeeded()
   queue:
-    name: Hosted VS2017
+    name: Hosted
     timeoutInMinutes: 240
   steps:
   - powershell: |
@@ -215,7 +215,7 @@ phases:
          . ci/lib.sh
 
          make -j10 DEVELOPER=1 NO_PERL=1 || exit 1
-         NO_PERL=1 NO_SVN_TESTS=1 GIT_TEST_OPTS=\"--quiet --write-junit-xml\" time make -j15 -k DEVELOPER=1 test || {
+         NO_PERL=1 NO_SVN_TESTS=1 GIT_TEST_OPTS=\"--no-chain-lint --no-bin-wrappers --quiet --write-junit-xml\" time make -j15 -k DEVELOPER=1 test || {
            NO_PERL=1 NO_SVN_TESTS=1 GIT_TEST_OPTS=\"-i -v -x\" make -k -C t failed; exit 1
          }
 
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 0b4784644351a7..eef6b3836aa4c1 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -22,6 +22,7 @@
 #include "wt-status.h"
 #include "revision.h"
 #include "rerere.h"
+#include "branch.h"
 
 static char const * const builtin_rebase_usage[] = {
 	N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@ -54,7 +55,7 @@ static int use_builtin_rebase(void)
 	cp.git_cmd = 1;
 	if (capture_command(&cp, &out, 6)) {
 		strbuf_release(&out);
-		return 0;
+		return 1;
 	}
 
 	strbuf_trim(&out);
@@ -281,8 +282,10 @@ static int apply_autostash(struct rebase_options *opts)
 	if (!file_exists(path))
 		return 0;
 
-	if (read_one(state_dir_path("autostash", opts), &autostash))
+	if (read_one(path, &autostash))
 		return error(_("Could not read '%s'"), path);
+	/* Ensure that the hash is not mistaken for a number */
+	strbuf_addstr(&autostash, "^0");
 	argv_array_pushl(&stash_apply.args,
 			 "stash", "apply", autostash.buf, NULL);
 	stash_apply.git_cmd = 1;
@@ -364,9 +367,124 @@ N_("Resolve all conflicts manually, mark them as resolved with\n"
 "To abort and get back to the state before \"git rebase\", run "
 "\"git rebase --abort\".");
 
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+#define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
+
 static int reset_head(struct object_id *oid, const char *action,
-		      const char *switch_to_branch, int detach_head,
-		      const char *reflog_orig_head, const char *reflog_head);
+		      const char *switch_to_branch, unsigned flags,
+		      const char *reflog_orig_head, const char *reflog_head)
+{
+	unsigned detach_head = flags & RESET_HEAD_DETACH;
+	unsigned reset_hard = flags & RESET_HEAD_HARD;
+	struct object_id head_oid;
+	struct tree_desc desc[2] = { { NULL }, { NULL } };
+	struct lock_file lock = LOCK_INIT;
+	struct unpack_trees_options unpack_tree_opts;
+	struct tree *tree;
+	const char *reflog_action;
+	struct strbuf msg = STRBUF_INIT;
+	size_t prefix_len;
+	struct object_id *orig = NULL, oid_orig,
+		*old_orig = NULL, oid_old_orig;
+	int ret = 0, nr = 0;
+
+	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
+		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
+
+	if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
+		ret = error(_("could not determine HEAD revision"));
+		goto leave_reset_head;
+	}
+
+	if (!oid)
+		oid = &head_oid;
+
+	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
+	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
+	unpack_tree_opts.head_idx = 1;
+	unpack_tree_opts.src_index = the_repository->index;
+	unpack_tree_opts.dst_index = the_repository->index;
+	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
+	unpack_tree_opts.update = 1;
+	unpack_tree_opts.merge = 1;
+	if (!detach_head)
+		unpack_tree_opts.reset = 1;
+
+	if (read_index_unmerged(the_repository->index) < 0) {
+		ret = error(_("could not read index"));
+		goto leave_reset_head;
+	}
+
+	if (!reset_hard && !fill_tree_descriptor(&desc[nr++], &head_oid)) {
+		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
+		goto leave_reset_head;
+	}
+
+	if (!fill_tree_descriptor(&desc[nr++], oid)) {
+		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
+		goto leave_reset_head;
+	}
+
+	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	tree = parse_tree_indirect(oid);
+	prime_cache_tree(the_repository->index, tree);
+
+	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
+		ret = error(_("could not write index"));
+		goto leave_reset_head;
+	}
+
+	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
+	prefix_len = msg.len;
+
+	if (!get_oid("ORIG_HEAD", &oid_old_orig))
+		old_orig = &oid_old_orig;
+	if (!get_oid("HEAD", &oid_orig)) {
+		orig = &oid_orig;
+		if (!reflog_orig_head) {
+			strbuf_addstr(&msg, "updating ORIG_HEAD");
+			reflog_orig_head = msg.buf;
+		}
+		update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0,
+			   UPDATE_REFS_MSG_ON_ERR);
+	} else if (old_orig)
+		delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+	if (!reflog_head) {
+		strbuf_setlen(&msg, prefix_len);
+		strbuf_addstr(&msg, "updating HEAD");
+		reflog_head = msg.buf;
+	}
+	if (!switch_to_branch)
+		ret = update_ref(reflog_head, "HEAD", oid, orig,
+				 detach_head ? REF_NO_DEREF : 0,
+				 UPDATE_REFS_MSG_ON_ERR);
+	else {
+		ret = create_symref("HEAD", switch_to_branch, msg.buf);
+		if (!ret)
+			ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
+					 UPDATE_REFS_MSG_ON_ERR);
+	}
+
+leave_reset_head:
+	strbuf_release(&msg);
+	rollback_lock_file(&lock);
+	while (nr)
+		free((void *)desc[--nr].buffer);
+	return ret;
+}
+
 
 static int move_to_original_branch(struct rebase_options *opts)
 {
@@ -697,112 +815,6 @@ static int run_specific_rebase(struct rebase_options *opts)
 	return status ? -1 : 0;
 }
 
-#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
-
-static int reset_head(struct object_id *oid, const char *action,
-		      const char *switch_to_branch, int detach_head,
-		      const char *reflog_orig_head, const char *reflog_head)
-{
-	struct object_id head_oid;
-	struct tree_desc desc;
-	struct lock_file lock = LOCK_INIT;
-	struct unpack_trees_options unpack_tree_opts;
-	struct tree *tree;
-	const char *reflog_action;
-	struct strbuf msg = STRBUF_INIT;
-	size_t prefix_len;
-	struct object_id *orig = NULL, oid_orig,
-		*old_orig = NULL, oid_old_orig;
-	int ret = 0;
-
-	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
-		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
-
-	if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
-		return -1;
-
-	if (!oid) {
-		if (get_oid("HEAD", &head_oid)) {
-			rollback_lock_file(&lock);
-			return error(_("could not determine HEAD revision"));
-		}
-		oid = &head_oid;
-	}
-
-	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
-	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
-	unpack_tree_opts.head_idx = 1;
-	unpack_tree_opts.src_index = the_repository->index;
-	unpack_tree_opts.dst_index = the_repository->index;
-	unpack_tree_opts.fn = oneway_merge;
-	unpack_tree_opts.update = 1;
-	unpack_tree_opts.merge = 1;
-	if (!detach_head)
-		unpack_tree_opts.reset = 1;
-
-	if (read_index_unmerged(the_repository->index) < 0) {
-		rollback_lock_file(&lock);
-		return error(_("could not read index"));
-	}
-
-	if (!fill_tree_descriptor(&desc, oid)) {
-		error(_("failed to find tree of %s"), oid_to_hex(oid));
-		rollback_lock_file(&lock);
-		free((void *)desc.buffer);
-		return -1;
-	}
-
-	if (unpack_trees(1, &desc, &unpack_tree_opts)) {
-		rollback_lock_file(&lock);
-		free((void *)desc.buffer);
-		return -1;
-	}
-
-	tree = parse_tree_indirect(oid);
-	prime_cache_tree(the_repository->index, tree);
-
-	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0)
-		ret = error(_("could not write index"));
-	free((void *)desc.buffer);
-
-	if (ret)
-		return ret;
-
-	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
-	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
-	prefix_len = msg.len;
-
-	if (!get_oid("ORIG_HEAD", &oid_old_orig))
-		old_orig = &oid_old_orig;
-	if (!get_oid("HEAD", &oid_orig)) {
-		orig = &oid_orig;
-		if (!reflog_orig_head) {
-			strbuf_addstr(&msg, "updating ORIG_HEAD");
-			reflog_orig_head = msg.buf;
-		}
-		update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0,
-			   UPDATE_REFS_MSG_ON_ERR);
-	} else if (old_orig)
-		delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
-	if (!reflog_head) {
-		strbuf_setlen(&msg, prefix_len);
-		strbuf_addstr(&msg, "updating HEAD");
-		reflog_head = msg.buf;
-	}
-	if (!switch_to_branch)
-		ret = update_ref(reflog_head, "HEAD", oid, orig, REF_NO_DEREF,
-				 UPDATE_REFS_MSG_ON_ERR);
-	else {
-		ret = create_symref("HEAD", switch_to_branch, msg.buf);
-		if (!ret)
-			ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
-					 UPDATE_REFS_MSG_ON_ERR);
-	}
-
-	strbuf_release(&msg);
-	return ret;
-}
-
 static int rebase_config(const char *var, const char *value, void *data)
 {
 	struct rebase_options *opts = data;
@@ -1177,8 +1189,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		rerere_clear(&merge_rr);
 		string_list_clear(&merge_rr, 1);
 
-		if (reset_head(NULL, "reset", NULL, 0, NULL, NULL) < 0)
+		if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
+			       NULL, NULL) < 0)
 			die(_("could not discard worktree changes"));
+		remove_branch_state();
 		if (read_basic_state(&options))
 			exit(1);
 		goto run_rebase;
@@ -1193,9 +1207,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		if (read_basic_state(&options))
 			exit(1);
 		if (reset_head(&options.orig_head, "reset",
-			       options.head_name, 0, NULL, NULL) < 0)
+			       options.head_name, RESET_HEAD_HARD,
+			       NULL, NULL) < 0)
 			die(_("could not move back to %s"),
 			    oid_to_hex(&options.orig_head));
+		remove_branch_state();
 		ret = finish_rebase(&options);
 		goto cleanup;
 	}
@@ -1395,15 +1411,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		 *       git-rebase.txt caveats with "unless you know what you are doing"
 		 */
 		if (options.rebase_merges)
-			die(_("error: cannot combine '--preserve_merges' with "
+			die(_("error: cannot combine '--preserve-merges' with "
 			      "'--rebase-merges'"));
 
 	if (options.rebase_merges) {
 		if (strategy_options.nr)
-			die(_("error: cannot combine '--rebase_merges' with "
+			die(_("error: cannot combine '--rebase-merges' with "
 			      "'--strategy-option'"));
 		if (options.strategy)
-			die(_("error: cannot combine '--rebase_merges' with "
+			die(_("error: cannot combine '--rebase-merges' with "
 			      "'--strategy'"));
 	}
 
@@ -1528,7 +1544,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			update_index_if_able(&the_index, &lock_file);
 		rollback_lock_file(&lock_file);
 
-		if (has_unstaged_changes(0) || has_uncommitted_changes(0)) {
+		if (has_unstaged_changes(1) || has_uncommitted_changes(1)) {
 			const char *autostash =
 				state_dir_path("autostash", &options);
 			struct child_process stash = CHILD_PROCESS_INIT;
@@ -1554,10 +1570,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			if (safe_create_leading_directories_const(autostash))
 				die(_("Could not create directory for '%s'"),
 				    options.state_dir);
-			write_file(autostash, "%s", buf.buf);
+			write_file(autostash, "%s", oid_to_hex(&oid));
 			printf(_("Created autostash: %s\n"), buf.buf);
 			if (reset_head(&head->object.oid, "reset --hard",
-				       NULL, 0, NULL, NULL) < 0)
+				       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
 				die(_("could not reset --hard"));
 			printf(_("HEAD is now at %s"),
 			       find_unique_abbrev(&head->object.oid,
@@ -1677,8 +1693,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			 "it...\n"));
 
 	strbuf_addf(&msg, "rebase: checkout %s", options.onto_name);
-	if (reset_head(&options.onto->object.oid, "checkout", NULL, 1,
-	    NULL, msg.buf))
+	if (reset_head(&options.onto->object.oid, "checkout", NULL,
+		       RESET_HEAD_DETACH, NULL, msg.buf))
 		die(_("Could not detach HEAD"));
 	strbuf_release(&msg);
 
diff --git a/builtin/stash.c b/builtin/stash.c
index 965e938ebd7a23..29d04c9f2fe181 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -15,6 +15,8 @@
 #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>]"),
@@ -70,11 +72,6 @@ static const char * const git_stash_store_usage[] = {
 	NULL
 };
 
-static const char * const git_stash_create_usage[] = {
-	N_("git stash create [<message>]"),
-	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"
@@ -89,7 +86,6 @@ static const char * const git_stash_save_usage[] = {
 };
 
 static const char *ref_stash = "refs/stash";
-static int quiet;
 static struct strbuf stash_index_path = STRBUF_INIT;
 
 /*
@@ -127,27 +123,25 @@ 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)) {
-		free_stash_info(info);
-		error(_("'%s' is not a stash-like commit"), revision);
-		exit(128);
-	}
+	    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)
 {
-	struct strbuf symbolic = STRBUF_INIT;
 	int ret;
-	const char *revision;
-	const char *commit = NULL;
 	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)
+
+		for (i = 0; i < argc; i++)
 			strbuf_addf(&refs_msg, " '%s'", argv[i]);
 
 		fprintf_ln(stderr, _("Too many revisions specified:%s"),
@@ -227,15 +221,16 @@ static int clear_stash(int argc, const char **argv, const char *prefix)
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 
 	if (argc)
-		return error(_("git stash clear with parameters is unimplemented"));
+		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)
 {
-	struct unpack_trees_options opts;
 	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;
@@ -320,10 +315,16 @@ static void add_diff_to_buf(struct diff_queue_struct *q,
 			    void *data)
 {
 	int i;
+
 	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		strbuf_addstr(data, p->one->path);
-		strbuf_addch(data, 0);
+		strbuf_addstr(data, q->queue[i]->one->path);
+
+		/*
+		 * The reason we add "0" at the end of this strbuf
+		 * is because we will pass the output further to
+		 * "git update-index -z ...".
+		 */
+		strbuf_addch(data, '\0');
 	}
 }
 
@@ -358,8 +359,8 @@ static int update_index(struct strbuf *out)
 
 static int restore_untracked(struct object_id *u_tree)
 {
-	struct child_process cp = CHILD_PROCESS_INIT;
 	int res;
+	struct child_process cp = CHILD_PROCESS_INIT;
 
 	/*
 	 * We need to run restore files from a given index, but without
@@ -388,52 +389,53 @@ static int restore_untracked(struct object_id *u_tree)
 }
 
 static int do_apply_stash(const char *prefix, struct stash_info *info,
-	int index)
+			  int index, int quiet)
 {
+	int ret;
+	int has_index = index;
 	struct merge_options o;
 	struct object_id c_tree;
 	struct object_id index_tree;
-	const struct object_id *bases[1];
 	struct commit *result;
-	int ret;
-	int has_index = index;
+	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) || reset_tree(&c_tree, 0, 0))
-		return error(_("Cannot apply a stash in the middle of a merge"));
+		return error(_("cannot apply a stash in the middle of a merge"));
 
 	if (index) {
-		if (!oidcmp(&info->b_tree, &info->i_tree) || !oidcmp(&c_tree,
-			&info->i_tree)) {
+		if (!oidcmp(&info->b_tree, &info->i_tree) ||
+		    !oidcmp(&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^!."),
+				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."));
+				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"));
+				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"));
+		return error(_("could not restore untracked files from stash"));
 
 	init_merge_options(&o);
 
@@ -508,13 +510,14 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 
 static int apply_stash(int argc, const char **argv, const char *prefix)
 {
+	int ret;
+	int quiet = 0;
 	int index = 0;
 	struct stash_info info;
-	int ret;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
-			N_("attempt to recreate the index")),
+			 N_("attempt to recreate the index")),
 		OPT_END()
 	};
 
@@ -524,16 +527,16 @@ static int apply_stash(int argc, const char **argv, const char *prefix)
 	if (get_stash_info(&info, argc, argv))
 		return -1;
 
-	ret = do_apply_stash(prefix, &info, index);
+	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)
+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;
-	int ret;
 
 	/*
 	 * reflog does not provide a simple function for deleting refs. One will
@@ -583,8 +586,9 @@ static void assert_stash_ref(struct stash_info *info)
 
 static int drop_stash(int argc, const char **argv, const char *prefix)
 {
-	struct stash_info info;
 	int ret;
+	int quiet = 0;
+	struct stash_info info;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_END()
@@ -598,19 +602,21 @@ static int drop_stash(int argc, const char **argv, const char *prefix)
 
 	assert_stash_ref(&info);
 
-	ret = do_drop_stash(prefix, &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 index = 0, ret;
+	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")),
+			 N_("attempt to recreate the index")),
 		OPT_END()
 	};
 
@@ -621,10 +627,11 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 		return -1;
 
 	assert_stash_ref(&info);
-	if ((ret = do_apply_stash(prefix, &info, index)))
-		printf_ln(_("The stash entry is kept in case you need it again."));
+	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);
+		ret = do_drop_stash(prefix, &info, quiet);
 
 	free_stash_info(&info);
 	return ret;
@@ -632,10 +639,10 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 
 static int branch_stash(int argc, const char **argv, const char *prefix)
 {
-	const char *branch = NULL;
 	int ret;
-	struct child_process cp = CHILD_PROCESS_INIT;
+	const char *branch = NULL;
 	struct stash_info info;
+	struct child_process cp = CHILD_PROCESS_INIT;
 	struct option options[] = {
 		OPT_END()
 	};
@@ -643,8 +650,10 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options,
 			     git_stash_branch_usage, 0);
 
-	if (!argc)
-		return error(_("No branch name specified"));
+	if (!argc) {
+		fprintf_ln(stderr, _("No branch name specified"));
+		return -1;
+	}
 
 	branch = argv[0];
 
@@ -657,9 +666,9 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	argv_array_push(&cp.args, oid_to_hex(&info.b_commit));
 	ret = run_command(&cp);
 	if (!ret)
-		ret = do_apply_stash(prefix, &info, 1);
+		ret = do_apply_stash(prefix, &info, 1, 0);
 	if (!ret && info.is_stash_ref)
-		ret = do_drop_stash(prefix, &info);
+		ret = do_drop_stash(prefix, &info, 0);
 
 	free_stash_info(&info);
 
@@ -721,7 +730,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 	git_config(git_diff_ui_config, NULL);
 	init_revisions(&rev, prefix);
 
-	for (i = 1; i < argc; ++i) {
+	for (i = 1; i < argc; i++) {
 		if (argv[i][0] != '-')
 			argv_array_push(&stash_args, argv[i]);
 		else
@@ -766,39 +775,36 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 	return diff_result_code(&rev.diffopt, 0);
 }
 
-static int do_store_stash(const char *w_commit, const char *stash_msg,
+static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
 			  int quiet)
 {
-	int ret = 0;
-	int need_to_free = 0;
-	struct object_id obj;
-
-	if (!stash_msg) {
-		need_to_free = 1;
-		stash_msg  = xstrdup("Created via \"git stash store\".");
+	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;
 	}
 
-	ret = get_oid(w_commit, &obj);
-	if (!ret) {
-		ret = update_ref(stash_msg, ref_stash, &obj, NULL,
-				 REF_FORCE_CREATE_REFLOG,
-				 quiet ? UPDATE_REFS_QUIET_ON_ERR :
-				 UPDATE_REFS_MSG_ON_ERR);
-	}
-	if (ret && !quiet)
-		fprintf_ln(stderr, _("Cannot update %s with %s"),
-			   ref_stash, w_commit);
-	if (need_to_free)
-		free((char *) stash_msg);
-	return ret;
+	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, only report errors")),
-		OPT_STRING('m', "message", &stash_msg, "message", N_("stash message")),
+		OPT__QUIET(&quiet, N_("be quiet")),
+		OPT_STRING('m', "message", &stash_msg, "message",
+			   N_("stash message")),
 		OPT_END()
 	};
 
@@ -807,11 +813,29 @@ static int store_stash(int argc, const char **argv, const char *prefix)
 			     PARSE_OPT_KEEP_UNKNOWN);
 
 	if (argc != 1) {
-		fprintf_ln(stderr, _("\"git stash store\" requires one <commit> argument"));
+		if (!quiet)
+			fprintf_ln(stderr, _("\"git stash store\" requires one "
+					     "<commit> argument"));
 		return -1;
 	}
 
-	return do_store_stash(argv[0], stash_msg, quiet);
+	if (get_oid_with_context(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);
 }
 
 /*
@@ -821,17 +845,17 @@ static int store_stash(int argc, const char **argv, const char *prefix)
  * = 0 if there are not any untracked files
  * > 0 if there are untracked files
  */
-static struct strbuf untracked_files = STRBUF_INIT;
-
-static int get_untracked_files(struct pathspec ps, int include_untracked)
+static int get_untracked_files(struct pathspec ps, int include_untracked,
+			       struct strbuf *untracked_files)
 {
-	int max_len;
 	int i;
+	int max_len;
+	int found = 0;
 	char *seen;
 	struct dir_struct dir;
 
 	memset(&dir, 0, sizeof(dir));
-	if (include_untracked != 2)
+	if (include_untracked != INCLUDE_ALL_FILES)
 		setup_standard_excludes(&dir);
 
 	seen = xcalloc(ps.nr, 1);
@@ -839,19 +863,20 @@ static int get_untracked_files(struct pathspec ps, int include_untracked)
 	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)) {
-			free(ent);
-			continue;
+		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);
 		}
-		strbuf_addf(&untracked_files, "%s%c", ent->name, '\0');
 		free(ent);
 	}
 
+	free(seen);
 	free(dir.entries);
 	free(dir.ignored);
 	clear_directory(&dir);
-	free(seen);
-	return untracked_files.len;
+	return found;
 }
 
 /*
@@ -868,6 +893,13 @@ static int check_changes_tracked_files(struct pathspec ps)
 	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;
 
@@ -875,15 +907,9 @@ static int check_changes_tracked_files(struct pathspec ps)
 	rev.diffopt.flags.ignore_submodules = 1;
 	rev.abbrev = 0;
 
-	/* No initial commit. */
-	if (get_oid("HEAD", &dummy))
-		return -1;
-
 	add_head_to_pending(&rev);
 	diff_setup_done(&rev.diffopt);
 
-	if (read_cache() < 0)
-		return 1;
 	result = run_diff_index(&rev, 1);
 	if (diff_result_code(&rev.diffopt, result))
 		return 1;
@@ -896,19 +922,27 @@ static int check_changes_tracked_files(struct pathspec ps)
 	return 0;
 }
 
-static int check_changes(struct pathspec ps, int include_untracked)
+/*
+ * 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))
+	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)
+static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
+				struct strbuf files)
 {
 	int ret = 0;
 	struct strbuf untracked_msg = STRBUF_INIT;
@@ -922,8 +956,8 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg)
 			 stash_index_path.buf);
 
 	strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf);
-	if (pipe_command(&cp_upd_index, untracked_files.buf, untracked_files.len,
-			 NULL, 0, NULL, 0)) {
+	if (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0,
+			 NULL, 0)) {
 		ret = -1;
 		goto done;
 	}
@@ -947,11 +981,9 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg)
 	return ret;
 }
 
-static struct strbuf patch = STRBUF_INIT;
-
-static int stash_patch(struct stash_info *info, struct pathspec ps, int quiet)
+static int stash_patch(struct stash_info *info, struct pathspec ps,
+		       struct strbuf *out_patch, int quiet)
 {
-	int i;
 	int ret = 0;
 	struct child_process cp_read_tree = CHILD_PROCESS_INIT;
 	struct child_process cp_add_i = CHILD_PROCESS_INIT;
@@ -969,11 +1001,11 @@ static int stash_patch(struct stash_info *info, struct pathspec ps, int quiet)
 		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);
-	for (i = 0; i < ps.nr; ++i)
-		argv_array_push(&cp_add_i.args, ps.items[i].match);
+			 "--", 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)) {
@@ -981,6 +1013,7 @@ static int stash_patch(struct stash_info *info, struct pathspec ps, int quiet)
 		goto done;
 	}
 
+	/* State of the working tree. */
 	if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
 				NULL)) {
 		ret = -1;
@@ -990,12 +1023,12 @@ static int stash_patch(struct stash_info *info, struct pathspec ps, int quiet)
 	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, &patch, 0, NULL, 0)) {
+	if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
 		ret = -1;
 		goto done;
 	}
 
-	if (!patch.len) {
+	if (!out_patch->len) {
 		if (!quiet)
 			fprintf_ln(stderr, _("No changes selected"));
 		ret = 1;
@@ -1010,9 +1043,9 @@ static int stash_patch(struct stash_info *info, struct pathspec ps, int quiet)
 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 rev_info rev;
 	struct index_state istate = { NULL };
 
 	set_alternate_index_output(stash_index_path.buf);
@@ -1022,7 +1055,6 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)
 	}
 	set_alternate_index_output(NULL);
 
-	git_config(git_diff_basic_config, NULL);
 	init_revisions(&rev, NULL);
 	rev.prune_data = ps;
 	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
@@ -1034,7 +1066,8 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)
 		goto done;
 	}
 
-	add_pending_object(&rev, parse_object(the_repository, &info->b_commit), "");
+	add_pending_object(&rev, parse_object(the_repository, &info->b_commit),
+			   "");
 	if (run_diff_index(&rev, 0)) {
 		ret = -1;
 		goto done;
@@ -1067,13 +1100,14 @@ static int stash_working_tree(struct stash_info *info, struct pathspec ps)
 	return ret;
 }
 
-static int do_create_stash(struct pathspec ps, const char **stash_msg,
+static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf,
 			   int include_untracked, int patch_mode,
-			   struct stash_info *info, int quiet)
+			   struct stash_info *info, struct strbuf *patch,
+			   int quiet)
 {
-	int untracked_commit_option = 0;
 	int ret = 0;
-	int flags;
+	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)";
@@ -1081,21 +1115,26 @@ static int do_create_stash(struct pathspec ps, const char **stash_msg,
 	struct commit_list *parents = NULL;
 	struct strbuf msg = STRBUF_INIT;
 	struct strbuf commit_tree_label = STRBUF_INIT;
-	struct strbuf stash_msg_buf = STRBUF_INIT;
+	struct strbuf untracked_files = STRBUF_INIT;
 
 	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"));
+			fprintf_ln(stderr, _("You do not have "
+					     "the initial commit yet"));
 		ret = -1;
-		*stash_msg = NULL;
 		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;
@@ -1110,28 +1149,28 @@ static int do_create_stash(struct pathspec ps, const char **stash_msg,
 	    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"));
+			fprintf_ln(stderr, _("Cannot save the current "
+					     "index state"));
 		ret = -1;
-		*stash_msg = NULL;
 		goto done;
 	}
 
 	if (include_untracked) {
-		if (save_untracked_files(info, &msg)) {
+		if (save_untracked_files(info, &msg, untracked_files)) {
 			if (!quiet)
-				fprintf_ln(stderr, _("Cannot save the untracked files"));
+				fprintf_ln(stderr, _("Cannot save "
+						     "the untracked files"));
 			ret = -1;
-			*stash_msg = NULL;
 			goto done;
 		}
 		untracked_commit_option = 1;
 	}
 	if (patch_mode) {
-		ret = stash_patch(info, ps, quiet);
-		*stash_msg = NULL;
+		ret = stash_patch(info, ps, patch, quiet);
 		if (ret < 0) {
 			if (!quiet)
-				fprintf_ln(stderr, _("Cannot save the current worktree state"));
+				fprintf_ln(stderr, _("Cannot save the current "
+						     "worktree state"));
 			goto done;
 		} else if (ret > 0) {
 			goto done;
@@ -1139,19 +1178,23 @@ static int do_create_stash(struct pathspec ps, const char **stash_msg,
 	} else {
 		if (stash_working_tree(info, ps)) {
 			if (!quiet)
-				fprintf_ln(stderr, _("Cannot save the current worktree state"));
+				fprintf_ln(stderr, _("Cannot save the current "
+						     "worktree state"));
 			ret = -1;
-			*stash_msg = NULL;
 			goto done;
 		}
 	}
 
-	if (!*stash_msg || !strlen(*stash_msg))
-		strbuf_addf(&stash_msg_buf, "WIP on %s", msg.buf);
-	else
-		strbuf_addf(&stash_msg_buf, "On %s: %s", branch_name,
-			    *stash_msg);
-	*stash_msg = strbuf_detach(&stash_msg_buf, NULL);
+	if (!stash_msg_buf->len) {
+		strbuf_release(stash_msg_buf);
+		strbuf_addf(stash_msg_buf, "WIP on %s", msg.buf);
+	} else {
+		struct strbuf temp_buf = STRBUF_INIT;
+		strbuf_addf(&temp_buf, "On %s: %s", branch_name,
+			    stash_msg_buf->buf);
+		strbuf_release(stash_msg_buf);
+		*stash_msg_buf = temp_buf;
+	}
 
 	/*
 	 * `parents` will be empty after calling `commit_tree()`, so there is
@@ -1159,14 +1202,18 @@ static int do_create_stash(struct pathspec ps, const char **stash_msg,
 	 */
 	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(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, strlen(*stash_msg), &info->w_tree,
+	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"));
+			fprintf_ln(stderr, _("Cannot record "
+					     "working tree state"));
 		ret = -1;
 		goto done;
 	}
@@ -1174,62 +1221,48 @@ static int do_create_stash(struct pathspec ps, const char **stash_msg,
 done:
 	strbuf_release(&commit_tree_label);
 	strbuf_release(&msg);
-	strbuf_release(&stash_msg_buf);
+	strbuf_release(&untracked_files);
 	return ret;
 }
 
 static int create_stash(int argc, const char **argv, const char *prefix)
 {
-	int i;
 	int ret = 0;
-	char *to_free = NULL;
-	const char *stash_msg = NULL;
+	struct strbuf stash_msg_buf = STRBUF_INIT;
 	struct stash_info info;
 	struct pathspec ps;
-	struct strbuf stash_msg_buf = STRBUF_INIT;
-	struct option options[] = {
-		OPT_END()
-	};
 
-	argc = parse_options(argc, argv, prefix, options,
-			     git_stash_create_usage,
-			     0);
+	/* 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;
 
-	for (i = 0; i < argc; ++i)
-		strbuf_addf(&stash_msg_buf, "%s ", argv[i]);
-	stash_msg = strbuf_detach(&stash_msg_buf, NULL);
-	to_free = (char *) stash_msg;
-
-	if (!(ret = do_create_stash(ps, &stash_msg, 0, 0, &info, 0)))
+	if (!(ret = do_create_stash(ps, &stash_msg_buf, 0, 0, &info, NULL, 0)))
 		printf_ln("%s", oid_to_hex(&info.w_commit));
 
-	free(to_free);
-	free((char *) stash_msg);
+	strbuf_release(&stash_msg_buf);
 	return ret;
 }
 
-static void add_ps_items_to_argv_array(struct argv_array *args,
-				       struct pathspec ps) {
-	int i;
-	for (i = 0; i < ps.nr; ++i)
-		argv_array_push(args, ps.items[i].match);
-}
-
 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"));
-		return -1;
+		fprintf_ln(stderr, _("Can't use --patch and --include-untracked"
+				     " or --all at the same time"));
+		ret = -1;
+		goto done;
 	}
 
 	read_cache_preload(NULL);
@@ -1237,49 +1270,55 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,
 		int i;
 		char *ps_matched = xcalloc(ps.nr, 1);
 
-		for (i = 0; i < active_nr; ++i) {
-			const struct cache_entry *ce = active_cache[i];
-			ce_path_match(&the_index, ce, &ps, ps_matched);
-		}
+		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'?"));
-			return -1;
+			ret = -1;
+			free(ps_matched);
+			goto done;
 		}
 		free(ps_matched);
 	}
 
-	if (refresh_cache(REFRESH_QUIET))
-		return -1;
+	if (refresh_cache(REFRESH_QUIET)) {
+		ret = -1;
+		goto done;
+	}
 
-	if (!check_changes(ps, include_untracked)) {
+	if (!check_changes(ps, include_untracked, &untracked_files)) {
 		if (!quiet)
 			printf_ln(_("No local changes to save"));
-		return 0;
+		goto done;
 	}
 
 	if (!reflog_exists(ref_stash) && do_clear_stash()) {
+		ret = -1;
 		if (!quiet)
 			fprintf_ln(stderr, _("Cannot initialize stash"));
-		return -1;
+		goto done;
 	}
 
-	if (do_create_stash(ps, &stash_msg, include_untracked, patch_mode,
-			    &info, quiet)) {
+	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(oid_to_hex(&info.w_commit), stash_msg, 1)) {
+	if (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {
+		ret = -1;
 		if (!quiet)
 			fprintf_ln(stderr, _("Cannot save the current status"));
-		ret = -1;
 		goto done;
 	}
 
 	if (!quiet)
 		printf_ln(_("Saved working directory and index state %s"),
-			stash_msg);
+			  stash_msg_buf.buf);
 
 	if (!patch_mode) {
 		if (include_untracked && !ps.nr) {
@@ -1288,7 +1327,7 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,
 			cp.git_cmd = 1;
 			argv_array_pushl(&cp.args, "clean", "--force",
 					 "--quiet", "-d", NULL);
-			if (include_untracked == 2)
+			if (include_untracked == INCLUDE_ALL_FILES)
 				argv_array_push(&cp.args, "-x");
 			if (run_command(&cp)) {
 				ret = -1;
@@ -1296,39 +1335,39 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,
 			}
 		}
 		if (ps.nr) {
-			struct child_process cp1 = CHILD_PROCESS_INIT;
-			struct child_process cp2 = CHILD_PROCESS_INIT;
-			struct child_process cp3 = CHILD_PROCESS_INIT;
+			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;
 
-			cp1.git_cmd = 1;
-			argv_array_push(&cp1.args, "add");
+			cp_add.git_cmd = 1;
+			argv_array_push(&cp_add.args, "add");
 			if (!include_untracked)
-				argv_array_push(&cp1.args, "-u");
-			if (include_untracked == 2)
-				argv_array_push(&cp1.args, "--force");
-			argv_array_push(&cp1.args, "--");
-			add_ps_items_to_argv_array(&cp1.args, ps);
-			if (run_command(&cp1)) {
+				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;
 			}
 
-			cp2.git_cmd = 1;
-			argv_array_pushl(&cp2.args, "diff-index", "-p",
+			cp_diff.git_cmd = 1;
+			argv_array_pushl(&cp_diff.args, "diff-index", "-p",
 					 "--cached", "--binary", "HEAD", "--",
 					 NULL);
-			add_ps_items_to_argv_array(&cp2.args, ps);
-			if (pipe_command(&cp2, NULL, 0, &out, 0, NULL, 0)) {
+			add_pathspecs(&cp_diff.args, ps);
+			if (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {
 				ret = -1;
 				goto done;
 			}
 
-			cp3.git_cmd = 1;
-			argv_array_pushl(&cp3.args, "apply", "--index", "-R",
-					 NULL);
-			if (pipe_command(&cp3, out.buf, out.len, NULL, 0, NULL,
-					 0)) {
+			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;
 			}
@@ -1344,8 +1383,8 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,
 		}
 
 		if (keep_index == 1 && !is_null_oid(&info.i_tree)) {
-			struct child_process cp1 = CHILD_PROCESS_INIT;
-			struct child_process cp2 = CHILD_PROCESS_INIT;
+			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)) {
@@ -1353,24 +1392,26 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,
 				goto done;
 			}
 
-			cp1.git_cmd = 1;
-			argv_array_pushl(&cp1.args, "ls-files", "-z",
+			cp_ls.git_cmd = 1;
+			argv_array_pushl(&cp_ls.args, "ls-files", "-z",
 					 "--modified", "--", NULL);
-			add_ps_items_to_argv_array(&cp1.args, ps);
-			if (pipe_command(&cp1, NULL, 0, &out, 0, NULL, 0)) {
+
+			add_pathspecs(&cp_ls.args, ps);
+			if (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) {
 				ret = -1;
 				goto done;
 			}
 
-			cp2.git_cmd = 1;
-			argv_array_pushl(&cp2.args, "checkout-index", "-z",
-					 "--force", "--stdin", NULL);
-			if (pipe_command(&cp2, out.buf, out.len, NULL, 0, NULL,
-					 0)) {
+			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;
 
@@ -1379,27 +1420,28 @@ static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet,
 
 		if (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {
 			if (!quiet)
-				fprintf_ln(stderr, _("Cannot remove worktree changes"));
+				fprintf_ln(stderr, _("Cannot remove "
+						     "worktree changes"));
 			ret = -1;
 			goto done;
 		}
 
 		if (keep_index < 1) {
-			int i;
 			struct child_process cp = CHILD_PROCESS_INIT;
 
 			cp.git_cmd = 1;
 			argv_array_pushl(&cp.args, "reset", "-q", "--", NULL);
-			for (i = 0; i < ps.nr; ++i)
-				argv_array_push(&cp.args, ps.items[i].match);
+			add_pathspecs(&cp.args, ps);
 			if (run_command(&cp)) {
 				ret = -1;
 				goto done;
 			}
 		}
+		goto done;
 	}
+
 done:
-	free((char *) stash_msg);
+	strbuf_release(&stash_msg_buf);
 	return ret;
 }
 
@@ -1412,17 +1454,17 @@ static int push_stash(int argc, const char **argv, const char *prefix)
 	const char *stash_msg = NULL;
 	struct pathspec ps;
 	struct option options[] = {
-		OPT_SET_INT('k', "keep-index", &keep_index,
-			N_("keep index"), 1),
+		OPT_BOOL('k', "keep-index", &keep_index,
+			 N_("keep index")),
 		OPT_BOOL('p', "patch", &patch_mode,
-			N_("stash in 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")),
+			   N_("stash message")),
 		OPT_END()
 	};
 
@@ -1438,43 +1480,41 @@ static int push_stash(int argc, const char **argv, const char *prefix)
 
 static int save_stash(int argc, const char **argv, const char *prefix)
 {
-	int i;
 	int keep_index = -1;
 	int patch_mode = 0;
 	int include_untracked = 0;
 	int quiet = 0;
 	int ret = 0;
 	const char *stash_msg = NULL;
-	char *to_free = NULL;
-	struct strbuf stash_msg_buf = STRBUF_INIT;
 	struct pathspec ps;
+	struct strbuf stash_msg_buf = STRBUF_INIT;
 	struct option options[] = {
-		OPT_SET_INT('k', "keep-index", &keep_index,
-			N_("keep index"), 1),
+		OPT_BOOL('k', "keep-index", &keep_index,
+			 N_("keep index")),
 		OPT_BOOL('p', "patch", &patch_mode,
-			N_("stash in 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,
-			     0);
+			     PARSE_OPT_KEEP_DASHDASH);
 
-	for (i = 0; i < argc; ++i)
-		strbuf_addf(&stash_msg_buf, "%s ", argv[i]);
-	stash_msg = strbuf_detach(&stash_msg_buf, NULL);
-	to_free = (char *) stash_msg;
+	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);
+	ret = do_push_stash(ps, stash_msg, quiet, keep_index,
+			    patch_mode, include_untracked);
 
-	free(to_free);
+	strbuf_release(&stash_msg_buf);
 	return ret;
 }
 
@@ -1489,7 +1529,7 @@ static int use_builtin_stash(void)
 	cp.git_cmd = 1;
 	if (capture_command(&cp, &out, 6)) {
 		strbuf_release(&out);
-		return 0;
+		return 1;
 	}
 
 	strbuf_trim(&out);
@@ -1523,7 +1563,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
 	trace_repo_setup(prefix);
 	setup_work_tree();
 
-	git_config(git_default_config, NULL);
+	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);
@@ -1532,7 +1572,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
 	strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
 		    (uintmax_t)pid);
 
-	if (argc == 0)
+	if (!argc)
 		return !!push_stash(0, NULL, prefix);
 	else if (!strcmp(argv[0], "apply"))
 		return !!apply_stash(argc, argv, prefix);
@@ -1583,10 +1623,8 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
 			 * they need to be immediately followed by a string
 			 * (i.e.`-m"foobar"` or `--message="foobar"`).
 			 */
-			if ((strlen(argv[i]) > 2 &&
-			     !strncmp(argv[i], "-m", 2)) ||
-			    (strlen(argv[i]) > 10 &&
-			     !strncmp(argv[i], "--message=", 10)))
+			if (starts_with(argv[i], "-m") ||
+			    starts_with(argv[i], "--message="))
 				continue;
 
 			usage_with_options(git_stash_usage, options);
diff --git a/compat/mingw.c b/compat/mingw.c
index f43146ef18cce2..1ecacb0fa770be 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -8,9 +8,9 @@
 #include "../cache.h"
 #include "win32/exit-process.h"
 #include "win32/lazyload.h"
-#include "../config.h"
 #include "../string-list.h"
 #include "../attr.h"
+#include "../config.h"
 
 #define HCAST(type, handle) ((type)(intptr_t)handle)
 
@@ -819,13 +819,11 @@ static int current_directory_len = 0;
 int mingw_chdir(const char *dirname)
 {
 	int result;
-	DECLARE_PROC_ADDR(kernel32.dll, DWORD, GetFinalPathNameByHandleW,
-			  HANDLE, LPWSTR, DWORD, DWORD);
 	wchar_t wdirname[MAX_LONG_PATH];
 	if (xutftowcs_long_path(wdirname, dirname) < 0)
 		return -1;
 
-	if (has_symlinks && INIT_PROC_ADDR(GetFinalPathNameByHandleW)) {
+	if (has_symlinks) {
 		HANDLE hnd = CreateFileW(wdirname, 0,
 				FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
 				OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
@@ -962,12 +960,15 @@ static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
 		errno = err_win_to_posix(GetLastError());
 		return -1;
 	}
+
 	buf->st_ino = 0;
-	buf->st_dev = buf->st_rdev = 0; /* not used by Git */
-	buf->st_gid = buf->st_uid = 0;
+	buf->st_gid = 0;
+	buf->st_uid = 0;
 	buf->st_nlink = 1;
 	buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes, 0, NULL);
-	buf->st_size = fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
+	buf->st_size = fdata.nFileSizeLow |
+		(((off_t)fdata.nFileSizeHigh)<<32);
+	buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 	filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
 	filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
 	filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
@@ -1176,8 +1177,6 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
 char *mingw_getcwd(char *pointer, int len)
 {
 	wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
-	DECLARE_PROC_ADDR(kernel32.dll, DWORD, GetFinalPathNameByHandleW,
-			  HANDLE, LPWSTR, DWORD, DWORD);
 	DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
 
 	if (!ret || ret >= ARRAY_SIZE(cwd)) {
@@ -1185,8 +1184,7 @@ char *mingw_getcwd(char *pointer, int len)
 		return NULL;
 	}
 	ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
-	if (!ret && GetLastError() == ERROR_ACCESS_DENIED &&
-		INIT_PROC_ADDR(GetFinalPathNameByHandleW)) {
+	if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
 		HANDLE hnd = CreateFileW(cwd, 0,
 			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
 			OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
@@ -1209,8 +1207,8 @@ char *mingw_getcwd(char *pointer, int len)
 }
 
 /*
- * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
- * (Parsing C++ Command-Line Arguments)
+ * See "Parsing C++ Command-Line Arguments" at Microsoft's Docs:
+ * https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
  */
 static const char *quote_arg_msvc(const char *arg)
 {
@@ -1452,34 +1450,37 @@ static char *path_lookup(const char *cmd, int exe_only)
 	return prog;
 }
 
-#if defined(_MSC_VER)
-
-/* We need a stable sort */
-#ifndef INTERNAL_QSORT
-#include "qsort.c"
-#endif
+static const wchar_t *wcschrnul(const wchar_t *s, wchar_t c)
+{
+	while (*s && *s != c)
+		s++;
+	return s;
+}
 
 /* Compare only keys */
 static int wenvcmp(const void *a, const void *b)
 {
 	wchar_t *p = *(wchar_t **)a, *q = *(wchar_t **)b;
 	size_t p_len, q_len;
-	int ret;
 
-	/* Find end of keys */
-	for (p_len = 0; p[p_len] && p[p_len] != L'='; p_len++)
-		; /* do nothing */
-	for (q_len = 0; q[q_len] && q[q_len] != L'='; q_len++)
-		; /* do nothing */
+	/* Find the keys */
+	p_len = wcschrnul(p, L'=') - p;
+	q_len = wcschrnul(q, L'=') - q;
 
-		  /* Are keys identical (modulo case)? */
-	if (p_len == q_len && !_wcsnicmp(p, q, p_len))
-		return 0;
+	/* If the length differs, include the shorter key's NUL */
+	if (p_len < q_len)
+		p_len++;
+	else if (p_len > q_len)
+		p_len = q_len + 1;
 
-	ret = _wcsnicmp(p, q, p_len < q_len ? p_len : q_len);
-	return ret ? ret : (p_len < q_len ? -1 : +1);
+	return _wcsnicmp(p, q, p_len);
 }
 
+/* We need a stable sort to convert the environment between UTF-16 <-> UTF-8 */
+#ifndef INTERNAL_QSORT
+#include "qsort.c"
+#endif
+
 /*
  * Build an environment block combining the inherited environment
  * merged with the given list of settings.
@@ -1494,25 +1495,15 @@ static int wenvcmp(const void *a, const void *b)
  */
 static wchar_t *make_environment_block(char **deltaenv)
 {
-	/*
-	 * The CRT (at least as of UCRT) secretly declares "_wenviron"
-	 * as a function that returns a pointer to a mostly static table.
-	 * Grab the pointer and cache it for the duration of our loop.
-	 */
-	const wchar_t *wenv = GetEnvironmentStringsW(), *p;
-	size_t delta_size = 0, size = 1; /* for extra NUL at the end */
+	wchar_t *wenv = GetEnvironmentStringsW(), *wdeltaenv, *result, *p;
+	size_t wlen, s, delta_size, size;
 
 	wchar_t **array = NULL;
 	size_t alloc = 0, nr = 0, i;
 
-	const char *p2;
-	wchar_t *wdeltaenv;
-
-	wchar_t *result, *p3;
+	size = 1; /* for extra NUL at the end */
 
-	/*
-	 * If there is no deltaenv to apply, simply return a copy
-	 */
+	/* If there is no deltaenv to apply, simply return a copy. */
 	if (!deltaenv || !*deltaenv) {
 		for (p = wenv; p && *p; ) {
 			size_t s = wcslen(p) + 1;
@@ -1531,116 +1522,51 @@ static wchar_t *make_environment_block(char **deltaenv)
 	 * sort them using the stable git_qsort() and then copy, skipping
 	 * duplicate keys
 	 */
-
 	for (p = wenv; p && *p; ) {
-		size_t s = wcslen(p) + 1;
-		size += s;
 		ALLOC_GROW(array, nr + 1, alloc);
+		s = wcslen(p) + 1;
 		array[nr++] = p;
 		p += s;
+		size += s;
 	}
 
 	/* (over-)assess size needed for wchar version of deltaenv */
-	for (i = 0; deltaenv[i]; i++) {
-		size_t s = strlen(deltaenv[i]) + 1;
-		delta_size += s;
-	}
-
+	for (delta_size = 0, i = 0; deltaenv[i]; i++)
+		delta_size += strlen(deltaenv[i]) * 2 + 1;
 	ALLOC_ARRAY(wdeltaenv, delta_size);
 
 	/* convert the deltaenv, appending to array */
-	for (i = 0, p3 = wdeltaenv; deltaenv[i]; i++) {
-		size_t s = strlen(deltaenv[i]) + 1, wlen;
-		wlen = xutftowcs(p3, deltaenv[i], s * 2);
-
+	for (i = 0, p = wdeltaenv; deltaenv[i]; i++) {
 		ALLOC_GROW(array, nr + 1, alloc);
-		array[nr++] = p3;
-
-		p3 += wlen + 1;
+		wlen = xutftowcs(p, deltaenv[i], wdeltaenv + delta_size - p);
+		array[nr++] = p;
+		p += wlen + 1;
 	}
 
 	git_qsort(array, nr, sizeof(*array), wenvcmp);
 	ALLOC_ARRAY(result, size + delta_size);
 
-	for (p3 = result, i = 0; i < nr; i++) {
-		wchar_t *equal = wcschr(array[i], L'=');;
+	for (p = result, i = 0; i < nr; i++) {
+		/* Skip any duplicate keys; last one wins */
+		while (i + 1 < nr && !wenvcmp(array + i, array + i + 1))
+		       i++;
 
 		/* Skip "to delete" entry */
-		if (!equal)
+		if (!wcschr(array[i], L'='))
 			continue;
 
-		p = array[i];
-
-		/* Skip any duplicate */
-		if (i + 1 < nr) {
-			wchar_t *next = array[i + 1];
-			size_t n = equal - p;
-
-			if (!_wcsnicmp(p, next, n) && (!next[n] || next[n] == L'='))
-				continue;
-		}
-
-		size = wcslen(p) + 1;
-		memcpy(p3, p, size * sizeof(*p));
-		p3 += size;
+		size = wcslen(array[i]) + 1;
+		memcpy(p, array[i], size * sizeof(*p));
+		p += size;
 	}
-	*p3 = L'\0';
+	*p = L'\0';
 
 	free(array);
+	free(wdeltaenv);
 	FreeEnvironmentStringsW(wenv);
 	return result;
 }
 
-#else
-
-static int do_putenv(char **env, const char *name, int size, int free_old);
-
-/* used number of elements of environ array, including terminating NULL */
-static int environ_size = 0;
-/* allocated size of environ array, in bytes */
-static int environ_alloc = 0;
-/* used as a indicator when the environment has been changed outside mingw.c */
-static char **saved_environ;
-
-static void maybe_reinitialize_environ(void);
-
-/*
- * Create environment block suitable for CreateProcess. Merges current
- * process environment and the supplied environment changes.
- */
-static wchar_t *make_environment_block(char **deltaenv)
-{
-	wchar_t *wenvblk = NULL;
-	char **tmpenv;
-	int i = 0, size, wenvsz = 0, wenvpos = 0;
-
-	maybe_reinitialize_environ();
-	size = environ_size;
-
-	while (deltaenv && deltaenv[i] && *deltaenv[i])
-		i++;
-
-	/* copy the environment, leaving space for changes */
-	ALLOC_ARRAY(tmpenv, size + i);
-	memcpy(tmpenv, environ, size * sizeof(char*));
-
-	/* merge supplied environment changes into the temporary environment */
-	for (i = 0; deltaenv && deltaenv[i] && *deltaenv[i]; i++)
-		size = do_putenv(tmpenv, deltaenv[i], size, 0);
-
-	/* create environment block from temporary environment */
-	for (i = 0; tmpenv[i] && *tmpenv[i]; i++) {
-		size = 2 * strlen(tmpenv[i]) + 2; /* +2 for final \0 */
-		ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz);
-		wenvpos += xutftowcs(&wenvblk[wenvpos], tmpenv[i], size) + 1;
-	}
-	/* add final \0 terminator */
-	wenvblk[wenvpos] = 0;
-	free(tmpenv);
-	return wenvblk;
-}
-#endif
-
 static void do_unset_environment_variables(void)
 {
 	static int done;
@@ -2141,131 +2067,83 @@ int msc_putenv(const char *name)
 #else
 
 /*
- * Compare environment entries by key (i.e. stopping at '=' or '\0').
+ * UTF-8 versions of getenv(), putenv() and unsetenv().
+ * Internally, they use the CRT's stock UNICODE routines
+ * to avoid data loss.
  */
-static int compareenv(const void *v1, const void *v2)
+char *mingw_getenv(const char *name)
 {
-	const char *e1 = *(const char**)v1;
-	const char *e2 = *(const char**)v2;
-
-	for (;;) {
-		int c1 = *e1++;
-		int c2 = *e2++;
-		c1 = (c1 == '=') ? 0 : tolower(c1);
-		c2 = (c2 == '=') ? 0 : tolower(c2);
-		if (c1 > c2)
-			return 1;
-		if (c1 < c2)
-			return -1;
-		if (c1 == 0)
-			return 0;
-	}
-}
+#define GETENV_MAX_RETAIN 30
+	static char *values[GETENV_MAX_RETAIN];
+	static int value_counter;
+	int len_key, len_value;
+	wchar_t *w_key;
+	char *value;
+	wchar_t w_value[32768];
 
-/*
- * Functions implemented outside Git are able to modify the environment,
- * too. For example, cURL's curl_global_init() function sets the CHARSET
- * environment variable (at least in certain circumstances).
- *
- * Therefore we need to be *really* careful *not* to assume that we have
- * sole control over the environment and reinitialize it when necessary.
- */
-static void maybe_reinitialize_environ(void)
-{
-	int i;
+	if (!name || !*name)
+		return NULL;
 
-	if (!saved_environ) {
-		warning("MinGW environment not initialized yet");
-		return;
+	len_key = strlen(name) + 1;
+	/* We cannot use xcalloc() here because that uses getenv() itself */
+	w_key = calloc(len_key, sizeof(wchar_t));
+	if (!w_key)
+		die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
+	xutftowcs(w_key, name, len_key);
+	len_value = GetEnvironmentVariableW(w_key, w_value, ARRAY_SIZE(w_value));
+	if (!len_value && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+		free(w_key);
+		return NULL;
 	}
+	free(w_key);
 
-	if (environ_size <= 0)
-		return;
-
-	if (saved_environ != environ)
-		/* We have *no* idea how much space was allocated outside */
-		environ_alloc = 0;
-	else if (!environ[environ_size - 1])
-		return; /* still consistent */
-
-	for (i = 0; environ[i] && *environ[i]; i++)
-		; /* continue counting */
-	environ[i] = NULL;
-	environ_size = i + 1;
+	len_value = len_value * 3 + 1;
+	/* We cannot use xcalloc() here because that uses getenv() itself */
+	value = calloc(len_value, sizeof(char));
+	if (!value)
+		die("Out of memory, (tried to allocate %u bytes)", len_value);
+	xwcstoutf(value, w_value, len_value);
 
-	/* sort environment for O(log n) getenv / putenv */
-	qsort(environ, i, sizeof(char*), compareenv);
-}
+	/*
+	 * We return `value` which is an allocated value and the caller is NOT
+	 * expecting to have to free it, so we keep a round-robin array,
+	 * invalidating the buffer after GETENV_MAX_RETAIN getenv() calls.
+	 */
+	free(values[value_counter]);
+	values[value_counter++] = value;
+	if (value_counter >= ARRAY_SIZE(values))
+		value_counter = 0;
 
-static int bsearchenv(char **env, const char *name, size_t size)
-{
-	unsigned low = 0, high = size;
-	while (low < high) {
-		unsigned mid = low + ((high - low) >> 1);
-		int cmp = compareenv(&env[mid], &name);
-		if (cmp < 0)
-			low = mid + 1;
-		else if (cmp > 0)
-			high = mid;
-		else
-			return mid;
-	}
-	return ~low; /* not found, return 1's complement of insert position */
+	return value;
 }
 
-/*
- * If name contains '=', then sets the variable, otherwise it unsets it
- * Size includes the terminating NULL. Env must have room for size + 1 entries
- * (in case of insert). Returns the new size. Optionally frees removed entries.
- */
-static int do_putenv(char **env, const char *name, int size, int free_old)
+int mingw_putenv(const char *namevalue)
 {
-	int i = size <= 0 ? -1 : bsearchenv(env, name, size - 1);
+	int size;
+	wchar_t *wide, *equal;
+	BOOL result;
 
-	/* optionally free removed / replaced entry */
-	if (i >= 0 && free_old)
-		free(env[i]);
+	if (!namevalue || !*namevalue)
+		return 0;
 
-	if (strchr(name, '=')) {
-		/* if new value ('key=value') is specified, insert or replace entry */
-		if (i < 0) {
-			i = ~i;
-			memmove(&env[i + 1], &env[i], (size - i) * sizeof(char*));
-			size++;
-		}
-		env[i] = (char*) name;
-	} else if (i >= 0) {
-		/* otherwise ('key') remove existing entry */
-		size--;
-		memmove(&env[i], &env[i + 1], (size - i) * sizeof(char*));
+	size = strlen(namevalue) * 2 + 1;
+	wide = calloc(size, sizeof(wchar_t));
+	if (!wide)
+		die("Out of memory, (tried to allocate %u wchar_t's)", size);
+	xutftowcs(wide, namevalue, size);
+	equal = wcschr(wide, L'=');
+	if (!equal)
+		result = SetEnvironmentVariableW(wide, NULL);
+	else {
+		*equal = L'\0';
+		result = SetEnvironmentVariableW(wide, equal + 1);
 	}
-	return size;
-}
-
-char *mingw_getenv(const char *name)
-{
-	char *value;
-	int pos;
-
-	if (environ_size <= 0)
-		return NULL;
-
-	maybe_reinitialize_environ();
-	pos = bsearchenv(environ, name, environ_size - 1);
+	free(wide);
 
-	if (pos < 0)
-		return NULL;
-	value = strchr(environ[pos], '=');
-	return value ? &value[1] : NULL;
-}
+	if (!result)
+		errno = err_win_to_posix(GetLastError());
 
-int mingw_putenv(const char *namevalue)
-{
-	maybe_reinitialize_environ();
-	ALLOC_GROW(environ, (environ_size + 1) * sizeof(char*), environ_alloc);
-	saved_environ = environ;
-	environ_size = do_putenv(environ, namevalue, environ_size, 1);
-	return 0;
+	return result ? 0 : -1;
 }
 
 #endif
@@ -3284,42 +3162,35 @@ static BOOL WINAPI handle_ctrl_c(DWORD ctrl_type)
 	return TRUE; /* we did handle this */
 }
 
-#if defined(_MSC_VER)
-
+#ifdef _MSC_VER
 #ifdef _DEBUG
 #include <crtdbg.h>
 #endif
+#endif
 
 /*
- * This routine sits between wmain() and "main" in git.exe.
- * We receive UNICODE (wchar_t) values for argv and env.
+ * We implement wmain() and compile with -municode, which would
+ * normally ignore main(), but we call the latter from the former
+ * so that we can handle non-ASCII command-line parameters
+ * appropriately.
  *
  * To be more compatible with the core git code, we convert
- * argv into UTF8 and pass them directly to the "main" routine.
- *
- * We don't bother converting the given UNICODE env vector,
- * but rather leave them in the CRT.  We replaced the various
- * getenv/putenv routines to pull them directly from the CRT.
- *
- * This is unlike the MINGW version:
- * [] It does the UNICODE-2-UTF8 conversion on both sets and
- *    stuffs the values back into the CRT using exported symbols.
- * [] It also maintains a private copy of the environment and
- *    tries to track external changes to it.
+ * argv into UTF8 and pass them directly to main().
  */
-int msc_startup(int argc, wchar_t **w_argv, wchar_t **w_env)
+int wmain(int argc, const wchar_t **wargv)
 {
-	char **my_utf8_argv = NULL, **save = NULL;
-	char *buffer = NULL;
-	int maxlen;
-	int k, exit_status;
+	int i, maxlen, exit_status;
+	char *buffer, **save;
+	const char **argv;
 
+#ifdef _MSC_VER
 #ifdef _DEBUG
 	_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
 #endif
 
 #ifdef USE_MSVC_CRTDBG
 	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+#endif
 #endif
 
 	SetConsoleCtrlHandler(handle_ctrl_c, TRUE);
@@ -3328,10 +3199,10 @@ int msc_startup(int argc, wchar_t **w_argv, wchar_t **w_env)
 	adjust_symlink_flags();
 	fsync_object_files = 1;
 
-	/* determine size of argv conversion buffer */
-	maxlen = wcslen(_wpgmptr);
-	for (k = 1; k < argc; k++)
-		maxlen = max(maxlen, wcslen(w_argv[k]));
+	/* determine size of argv and environ conversion buffer */
+	maxlen = wcslen(wargv[0]);
+	for (i = 1; i < argc; i++)
+		maxlen = max(maxlen, wcslen(wargv[i]));
 
 	/* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
 	maxlen = 3 * maxlen + 1;
@@ -3342,13 +3213,11 @@ int msc_startup(int argc, wchar_t **w_argv, wchar_t **w_env)
 	 * to remember all the string pointers because parse_options()
 	 * will remove claimed items from the argv that we pass down.
 	 */
-	ALLOC_ARRAY(my_utf8_argv, argc + 1);
+	ALLOC_ARRAY(argv, argc + 1);
 	ALLOC_ARRAY(save, argc + 1);
-	save[0] = my_utf8_argv[0] = wcstoutfdup_startup(buffer, _wpgmptr, maxlen);
-	for (k = 1; k < argc; k++)
-		save[k] = my_utf8_argv[k] = wcstoutfdup_startup(buffer, w_argv[k], maxlen);
-	save[k] = my_utf8_argv[k] = NULL;
-
+	for (i = 0; i < argc; i++)
+		argv[i] = save[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
+	argv[i] = save[i] = NULL;
 	free(buffer);
 
 	/* fix Windows specific environment settings */
@@ -3373,102 +3242,16 @@ int msc_startup(int argc, wchar_t **w_argv, wchar_t **w_env)
 	current_directory_len = GetCurrentDirectoryW(0, NULL);
 
 	/* invoke the real main() using our utf8 version of argv. */
-	exit_status = msc_main(argc, my_utf8_argv);
+	exit_status = main(argc, argv);
 
-	for (k = 0; k < argc; k++)
-		free(save[k]);
+	for (i = 0; i < argc; i++)
+		free(save[i]);
 	free(save);
-	free(my_utf8_argv);
+	free(argv);
 
 	return exit_status;
 }
 
-#else
-
-void mingw_startup(void)
-{
-	int i, maxlen, argc;
-	char *buffer;
-	wchar_t **wenv, **wargv;
-	_startupinfo si;
-
-	SetConsoleCtrlHandler(handle_ctrl_c, TRUE);
-
-	maybe_redirect_std_handles();
-	adjust_symlink_flags();
-	fsync_object_files = 1;
-
-	/* get wide char arguments and environment */
-	si.newmode = 0;
-	if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
-		die_startup();
-
-	/* determine size of argv and environ conversion buffer */
-	maxlen = wcslen(wargv[0]);
-	for (i = 1; i < argc; i++)
-		maxlen = max(maxlen, wcslen(wargv[i]));
-	for (i = 0; wenv[i]; i++)
-		maxlen = max(maxlen, wcslen(wenv[i]));
-
-	/*
-	 * nedmalloc can't free CRT memory, allocate resizable environment
-	 * list. Note that xmalloc / xmemdupz etc. call getenv, so we cannot
-	 * use it while initializing the environment itself.
-	 */
-	environ_size = i + 1;
-	environ_alloc = alloc_nr(environ_size * sizeof(char*));
-	saved_environ = environ = malloc_startup(environ_alloc);
-
-	/* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
-	maxlen = 3 * maxlen + 1;
-	buffer = malloc_startup(maxlen);
-
-	/* convert command line arguments and environment to UTF-8 */
-	for (i = 0; i < argc; i++)
-		__argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
-	for (i = 0; wenv[i]; i++)
-		environ[i] = wcstoutfdup_startup(buffer, wenv[i], maxlen);
-	environ[i] = NULL;
-	free(buffer);
-
-	/* sort environment for O(log n) getenv / putenv */
-	qsort(environ, i, sizeof(char*), compareenv);
-
-	/* fix Windows specific environment settings */
-	setup_windows_environment();
-
-	unset_environment_variables = xstrdup("PERL5LIB");
-
-	/*
-	 * Avoid a segmentation fault when cURL tries to set the CHARSET
-	 * variable and putenv() barfs at our nedmalloc'ed environment.
-	 */
-	if (!getenv("CHARSET")) {
-		struct strbuf buf = STRBUF_INIT;
-		strbuf_addf(&buf, "cp%u", GetACP());
-		setenv("CHARSET", buf.buf, 1);
-		strbuf_release(&buf);
-	}
-
-	/* initialize critical section for waitpid pinfo_t list */
-	InitializeCriticalSection(&pinfo_cs);
-	InitializeCriticalSection(&phantom_symlinks_cs);
-
-	/* set up default file mode and file modes for stdin/out/err */
-	_fmode = _O_BINARY;
-	_setmode(_fileno(stdin), _O_BINARY);
-	_setmode(_fileno(stdout), _O_BINARY);
-	_setmode(_fileno(stderr), _O_BINARY);
-
-	/* initialize Unicode console */
-	winansi_init();
-
-	/* init length of current directory for handle_long_path */
-	current_directory_len = GetCurrentDirectoryW(0, NULL);
-}
-
-#endif
-
 int uname(struct utsname *buf)
 {
 	unsigned v = (unsigned)GetVersion();
diff --git a/compat/mingw.h b/compat/mingw.h
index 99976fe27aa7c9..ffa6831388f17f 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -262,12 +262,11 @@ char *mingw_getcwd(char *pointer, int len);
 #define getcwd mingw_getcwd
 
 #ifdef NO_UNSETENV
-#error "NO_UNSETENV is incompatible with the MinGW startup code!"
+#error "NO_UNSETENV is incompatible with the Windows-specific startup code!"
 #endif
 
-#if defined(_MSC_VER)
 /*
- * We bind *env() routines (even the mingw_ ones) to private msc_ versions.
+ * We bind *env() routines (even the mingw_ ones) to private mingw_ versions.
  * These talk to the CRT using UNICODE/wchar_t, but maintain the original
  * narrow-char API.
  *
@@ -276,7 +275,7 @@ char *mingw_getcwd(char *pointer, int len);
  * (and secretly updates both when you set one or the other), but it uses CP_ACP
  * to do the conversion rather than CP_UTF8.
  *
- * Since everything in the git code base is UTF8, we define the msc_ routines
+ * Since everything in the git code base is UTF8, we define the mingw_ routines
  * to access the CRT using the UNICODE routines and manually convert them to
  * UTF8.  This also avoids round-trip problems.
  *
@@ -284,33 +283,13 @@ char *mingw_getcwd(char *pointer, int len);
  * from the CRT.  But to access "_environ" we would have to statically link
  * to the CRT (/MT).
  *
- * We also use "wmain(argc,argv,env)" and get the initial UNICODE setup for us.
- * This avoids the need for the msc_startup() to import and convert the
- * inherited environment.
- *
- * We require NO_SETENV (and let gitsetenv() call our msc_putenv).
+ * We require NO_SETENV (and let gitsetenv() call our mingw_putenv).
  */
-#define getenv       msc_getenv
-#define putenv       msc_putenv
-#define unsetenv     msc_putenv
-#define mingw_getenv msc_getenv
-#define mingw_putenv msc_putenv
-char *msc_getenv(const char *name);
-int   msc_putenv(const char *name);
-
-#ifndef NO_SETENV
-#error "NO_SETENV is required for MSC startup code!"
-#endif
-
-#else
-
+#define getenv       mingw_getenv
+#define putenv       mingw_putenv
+#define unsetenv     mingw_putenv
 char *mingw_getenv(const char *name);
-#define getenv mingw_getenv
-int mingw_putenv(const char *namevalue);
-#define putenv mingw_putenv
-#define unsetenv mingw_putenv
-
-#endif
+int   mingw_putenv(const char *name);
 
 int mingw_gethostname(char *host, int namelen);
 #define gethostname mingw_gethostname
@@ -687,39 +666,18 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen);
 extern CRITICAL_SECTION pinfo_cs;
 
 /*
- * A replacement of main() that adds win32 specific initialization.
+ * Git, like most portable C applications, implements a main() function. On
+ * Windows, this main() function would receive parameters encoded in the
+ * current locale, but Git for Windows would prefer UTF-8 encoded  parameters.
  *
- * Note that the end of these macros are unterminated so that the
- * brace group following the use of the macro is the body of the
- * function.
+ * To make that happen, we still declare main() here, and then declare and
+ * implement wmain() (which is the Unicode variant of main()) and compile with
+ * -municode. This wmain() function reencodes the parameters from UTF-16 to
+ * UTF-8 format, sets up a couple of other things as required on Windows, and
+ * then hands off to the main() function.
  */
-#if defined(_MSC_VER)
-
-int msc_startup(int argc, wchar_t **w_argv, wchar_t **w_env);
-extern int msc_main(int argc, const char **argv);
-
-#define main(c,v) dummy_decl_msc_main(void);				\
-int wmain(int my_argc,									\
-		  wchar_t **my_w_argv,							\
-		  wchar_t **my_w_env)							\
-{														\
-	return msc_startup(my_argc, my_w_argv, my_w_env);	\
-}														\
-int msc_main(c, v)
-
-#else
-
-void mingw_startup(void);
-#define main(c,v) dummy_decl_mingw_main(void); \
-static int mingw_main(c,v); \
-int main(int argc, const char **argv) \
-{ \
-	mingw_startup(); \
-	return mingw_main(__argc, (void *)__argv); \
-} \
-static int mingw_main(c,v)
-
-#endif
+int wmain(int argc, const wchar_t **w_argv);
+int main(int argc, const char **argv);
 
 /*
  * For debugging: if a problem occurs, say, in a Git process that is spawned
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
index 2ea3da97f4087f..81da36a93bae44 100644
--- a/compat/vcbuild/README
+++ b/compat/vcbuild/README
@@ -1,7 +1,7 @@
 The Steps to Build Git with VS2015 or VS2017 from the command line.
 
 1. Install the "vcpkg" open source package manager and build essential
-   third-party libaries.  The steps for this have been captured in a
+   third-party libraries.  The steps for this have been captured in a
    set of convenience scripts.  These can be run from a stock Command
    Prompt or from an SDK bash window:
 
@@ -45,7 +45,7 @@ Note that this will automatically add and commit the generated
 .sln and .vcxproj files to the repo.  You may want to drop this
 commit before submitting a Pull Request....
 
-Or maybe we should put the .sln/.vcxproj files in the .gitignores
+Or maybe we should put the .sln/.vcxproj files in the .gitignore file
 and not do this.  I'm not sure.
 
 ================================================================
diff --git a/config.mak.uname b/config.mak.uname
index fded5c253012bf..c820bc90458b8e 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -398,12 +398,9 @@ ifeq ($(uname_S),Windows)
 	NO_MKDTEMP = YesPlease
 	NO_INTTYPES_H = YesPlease
 	# VS2015 with UCRT claims that snprintf and friends are C99 compliant,
-	# so we don't need this.
+	# so we don't need this:
 	#
-	# TODO If we want to support older compilers, we need to make this
-	# TODO conditional on the compiler version.
-	#
-	# SNPRINTF_RETURNS_BOGUS = YesPlease
+	#   SNPRINTF_RETURNS_BOGUS = YesPlease
 	NO_SVN_TESTS = YesPlease
 	RUNTIME_PREFIX = YesPlease
 	HAVE_WPGMPTR = YesWeDo
@@ -587,6 +584,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
 	ETAGS_TARGET = ETAGS
 	NO_POSIX_GOODIES = UnfortunatelyYes
 	DEFAULT_HELP_FORMAT = html
+	BASIC_LDFLAGS += -municode
 	COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
 	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
 	COMPAT_OBJS += compat/mingw.o compat/winansi.o \
diff --git a/contrib/buildsystems/Generators/Vcxproj.pm b/contrib/buildsystems/Generators/Vcxproj.pm
index da08ee01d4e1cc..4bdb8008d1a76c 100644
--- a/contrib/buildsystems/Generators/Vcxproj.pm
+++ b/contrib/buildsystems/Generators/Vcxproj.pm
@@ -70,7 +70,7 @@ sub createProject {
     }
     my $defines = join(";", sort(@{$$build_structure{"$prefix${name}_DEFINES"}}));
     my $includes= join(";", sort(map { s/^-I//; s/\//\\/g; File::Spec->file_name_is_absolute($_) ? $_ : "$rel_dir\\$_" } @{$$build_structure{"$prefix${name}_INCLUDES"}}));
-    my $cflags = join(" ", sort(map { s/^-[GLMOZ].*//; s/.* .*/"$&"/; $_; } @{$$build_structure{"$prefix${name}_CFLAGS"}}));
+    my $cflags = join(" ", sort(map { s/^-[GLMOWZ].*//; s/.* .*/"$&"/; $_; } @{$$build_structure{"$prefix${name}_CFLAGS"}}));
     $cflags =~ s/</&lt;/g;
     $cflags =~ s/>/&gt;/g;
 
@@ -179,10 +179,10 @@ sub createProject {
 EOM
     if ($target eq 'libgit') {
         print F << "EOM";
-    <PreBuildEvent Condition="!Exists('$cdup\\compat\\vcbuild\\vcpkg') OR (!Exists('$cdup\\compat\\vcbuild\\vcpkg\\installed\\x64-windows\\include\\openssl\\ssl.h') AND !Exists('$cdup\\compat\\vcbuild\\vcpkg\\installed\\x86-windows\\include\\openssl\\ssl.h'))">
+    <PreBuildEvent Condition="!Exists('$cdup\\compat\\vcbuild\\vcpkg\\installed\\\$(VCPKGArch)\\include\\openssl\\ssl.h')">
       <Message>Initialize VCPKG</Message>
       <Command>del "$cdup\\compat\\vcbuild\\vcpkg"</Command>
-      <Command>call "$cdup\\compat\\vcbuild\\vcpkg_install.bat" </Command>
+      <Command>call "$cdup\\compat\\vcbuild\\vcpkg_install.bat"</Command>
     </PreBuildEvent>
 EOM
     }
diff --git a/sequencer.c b/sequencer.c
index f312ce123e8abb..a54470bb9ba548 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3184,10 +3184,6 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
 	}
 
 	merge_commit = to_merge->item;
-	write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ,
-		      git_path_merge_head(the_repository), 0);
-	write_message("no-ff", 5, git_path_merge_mode(the_repository), 0);
-
 	bases = get_merge_bases(head_commit, merge_commit);
 	if (bases && !oidcmp(&merge_commit->object.oid,
 			     &bases->item->object.oid)) {
@@ -3196,6 +3192,10 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
 		goto leave_merge;
 	}
 
+	write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ,
+		      git_path_merge_head(the_repository), 0);
+	write_message("no-ff", 5, git_path_merge_mode(the_repository), 0);
+
 	for (j = bases; j; j = j->next)
 		commit_list_insert(j->item, &reversed);
 	free_commit_list(bases);
@@ -3434,6 +3434,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
 			unlink(rebase_path_author_script());
 			unlink(rebase_path_stopped_sha());
 			unlink(rebase_path_amend());
+			unlink(git_path_merge_head(the_repository));
 			delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 		}
 		if (item->command <= TODO_SQUASH) {
@@ -3790,6 +3791,7 @@ static int commit_staged_changes(struct replay_opts *opts,
 			   opts, flags))
 		return error(_("could not commit staged changes."));
 	unlink(rebase_path_amend());
+	unlink(git_path_merge_head(the_repository));
 	if (final_fixup) {
 		unlink(rebase_path_fixup_msg());
 		unlink(rebase_path_squash_msg());
diff --git a/strbuf.c b/strbuf.c
index 9be7fe0ca1a398..b1f7219d1e4959 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -268,6 +268,21 @@ void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)
 	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);
diff --git a/strbuf.h b/strbuf.h
index 9043fa17aa4db3..9aee339a3ba7c9 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -287,6 +287,13 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)
  */
 extern 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
diff --git a/t/README b/t/README
index d174a38d91a558..24beceabdd0d1c 100644
--- a/t/README
+++ b/t/README
@@ -169,6 +169,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.
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 7f19368fc10b43..b7a1c45881a844 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -486,7 +486,8 @@ test_expect_success MINGW 'redirect std handles' '
 		GIT_REDIRECT_STDERR="2>&1" \
 		git rev-parse --git-dir --verify refs/invalid &&
 	printf ".git\nfatal: Needed a single revision\n" >expect &&
-	test_cmp expect output.txt
+	sort <output.txt >output.sorted &&
+	test_cmp expect output.sorted
 '
 
 test_done
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index 43ff4939adfb5f..0215c969444e98 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -148,7 +148,8 @@ test_trace () {
 	expect="$1"
 	shift
 	GIT_TRACE=1 test-tool run-command "$@" run-command true 2>&1 >/dev/null | \
-		sed -e 's/.* run_command: //' -e '/trace: .*/d' >actual &&
+		sed -e 's/.* run_command: //' -e '/trace: .*/d' \
+			-e '/RUNTIME_PREFIX requested/d' >actual &&
 	echo "$expect true" >expect &&
 	test_cmp expect actual
 }
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 0c4eefec760efa..4c7494cc8f77a3 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -351,4 +351,22 @@ test_expect_success 'autostash is saved on editor failure with conflict' '
 	test_cmp expected file0
 '
 
+test_expect_success 'autostash with dirty submodules' '
+	test_when_finished "git reset --hard && git checkout master" &&
+	git checkout -b with-submodule &&
+	git submodule add ./ sub &&
+	test_tick &&
+	git commit -m add-submodule &&
+	echo changed >sub/file0 &&
+	git rebase -i --autostash HEAD
+'
+
+test_expect_success 'branch is left alone when possible' '
+	git checkout -b unchanged-branch &&
+	echo changed >file0 &&
+	git rebase --autostash unchanged-branch &&
+	test changed = "$(cat file0)" &&
+	test unchanged-branch = "$(git rev-parse --abbrev-ref HEAD)"
+'
+
 test_done
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index aa7bfc88ece9de..cc5646836fce1a 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -396,4 +396,20 @@ test_expect_success 'with --autosquash and --exec' '
 	grep "G: +G" actual
 '
 
+test_expect_success '--continue after resolving conflicts after a merge' '
+	git checkout -b already-has-g E &&
+	git cherry-pick E..G &&
+	test_commit H2 &&
+
+	git checkout -b conflicts-in-merge H &&
+	test_commit H2 H2.t conflicts H2-conflict &&
+	test_must_fail git rebase -r already-has-g &&
+	grep conflicts H2.t &&
+	echo resolved >H2.t &&
+	git add -u &&
+	git rebase --continue &&
+	test_must_fail git rev-parse --verify HEAD^2 &&
+	test_path_is_missing .git/MERGE_HEAD
+'
+
 test_done
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 9e06494ba0ef19..f1f6ca209edb18 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -36,7 +36,7 @@ EOF
 test_expect_success 'parents of stash' '
 	test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
 	git diff stash^2..stash >output &&
-	test_cmp output expect
+	test_cmp expect output
 '
 
 test_expect_success 'applying bogus stash does nothing' '
@@ -210,9 +210,9 @@ test_expect_success 'stash branch' '
 	test refs/heads/stashbranch = $(git symbolic-ref HEAD) &&
 	test $(git rev-parse HEAD) = $(git rev-parse master^) &&
 	git diff --cached >output &&
-	test_cmp output expect &&
+	test_cmp expect output &&
 	git diff >output &&
-	test_cmp output expect1 &&
+	test_cmp expect1 output &&
 	git add file &&
 	git commit -m alternate\ second &&
 	git diff master..stashbranch >output &&
@@ -747,7 +747,7 @@ test_expect_success 'stash where working directory contains "HEAD" file' '
 	git diff-index --cached --quiet HEAD &&
 	test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" &&
 	git diff stash^..stash >output &&
-	test_cmp output expect
+	test_cmp expect output
 '
 
 test_expect_success 'store called with invalid commit' '
diff --git a/t/t3907-stash-show-config.sh b/t/t3907-stash-show-config.sh
index 8fe369c1a11d44..10914bba7b3737 100755
--- a/t/t3907-stash-show-config.sh
+++ b/t/t3907-stash-show-config.sh
@@ -15,26 +15,28 @@ test_expect_success 'setup' '
 test_stat_and_patch () {
 	if test "<unset>" = "$1"
 	then
-		test_might_fail git config --unset stash.showStat
+		test_unconfig stash.showStat
 	else
 		test_config stash.showStat "$1"
 	fi &&
 
 	if test "<unset>" = "$2"
 	then
-		test_might_fail git config --unset stash.showPatch
+		test_unconfig stash.showPatch
 	else
 		test_config stash.showPatch "$2"
 	fi &&
 
-	shift &&
-	shift &&
+	shift 2 &&
 	echo 2 >file.t &&
-	git diff "$@" >expect &&
+	if test $# != 0
+	then
+		git diff "$@" >expect
+	fi &&
 	git stash &&
 	git stash show >actual &&
 
-	if test -z "$1"
+	if test $# = 0
 	then
 		test_must_be_empty actual
 	else
diff --git a/t/test-lib.sh b/t/test-lib.sh
index bf90aa500c23ca..72604ec4fb2e53 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -259,6 +259,8 @@ do
 		test -z "$HARNESS_ACTIVE" && quiet=t; shift ;;
 	--with-dashes)
 		with_dashes=t; shift ;;
+	--no-bin-wrappers)
+		no_bin_wrappers=t; shift ;;
 	--no-color)
 		color=; shift ;;
 	--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
@@ -1069,20 +1071,25 @@ then
 	PATH=$GIT_TEST_INSTALLED$PATH_SEP$GIT_BUILD_DIR/t/helper$PATH_SEP$PATH
 	GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
 else # normal case, use ../bin-wrappers only unless $with_dashes:
-	git_bin_dir="$GIT_BUILD_DIR/bin-wrappers"
-	if ! test -x "$git_bin_dir/git"
+	if test -n "$no_bin_wrappers"
 	then
-		if test -z "$with_dashes"
+		with_dashes=t
+	else
+		git_bin_dir="$GIT_BUILD_DIR/bin-wrappers"
+		if ! test -x "$git_bin_dir/git"
 		then
-			say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
+			if test -z "$with_dashes"
+			then
+				say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
+			fi
+			with_dashes=t
 		fi
-		with_dashes=t
+		PATH="$git_bin_dir$PATH_SEP$PATH"
 	fi
-	PATH="$git_bin_dir$PATH_SEP$PATH"
 	GIT_EXEC_PATH=$GIT_BUILD_DIR
 	if test -n "$with_dashes"
 	then
-		PATH="$GIT_BUILD_DIR$PATH_SEP$PATH"
+		PATH="$GIT_BUILD_DIR$PATH_SEP$GIT_BUILD_DIR/t/helper$PATH_SEP$PATH"
 	fi
 fi
 GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
@@ -1106,7 +1113,7 @@ test -d "$GIT_BUILD_DIR"/templates/blt || {
 	error "You haven't built things yet, have you?"
 }
 
-if ! test -x "$GIT_BUILD_DIR"/t/helper/test-tool
+if ! test -x "$GIT_BUILD_DIR"/t/helper/test-tool$X
 then
 	echo >&2 'You need to build test-tool:'
 	echo >&2 'Run "make t/helper/test-tool" in the source (toplevel) directory'
diff --git a/wt-status.c b/wt-status.c
index 5ffab61015da90..30b81576a3aa72 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1553,6 +1553,7 @@ void wt_status_get_state(struct wt_status_state *state,
 	struct object_id oid;
 
 	if (!stat(git_path_merge_head(the_repository), &st)) {
+		wt_status_check_rebase(NULL, state);
 		state->merge_in_progress = 1;
 	} else if (wt_status_check_rebase(NULL, state)) {
 		;		/* all set */
@@ -1576,9 +1577,13 @@ static void wt_longstatus_print_state(struct wt_status *s,
 				      struct wt_status_state *state)
 {
 	const char *state_color = color(WT_STATUS_HEADER, s);
-	if (state->merge_in_progress)
+	if (state->merge_in_progress) {
+		if (state->rebase_interactive_in_progress) {
+			show_rebase_information(s, state, state_color);
+			fputs("\n", s->fp);
+		}
 		show_merge_in_progress(s, state, state_color);
-	else if (state->am_in_progress)
+	} else if (state->am_in_progress)
 		show_am_in_progress(s, state, state_color);
 	else if (state->rebase_in_progress || state->rebase_interactive_in_progress)
 		show_rebase_in_progress(s, state, state_color);
