Merge branch 'js/mingw-use-utf8'

Windows update.

* js/mingw-use-utf8:
  mingw: fix possible buffer overrun when calling `GetUserNameW()`
  mingw: use Unicode functions explicitly
  mingw: get pw_name in UTF-8 format
diff --git a/.gitattributes b/.gitattributes
index 9fa72ad..b08a141 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -5,6 +5,7 @@
 *.pl eof=lf diff=perl
 *.pm eol=lf diff=perl
 *.py eol=lf diff=python
+*.bat eol=crlf
 /Documentation/**/*.txt eol=lf
 /command-list.txt eol=lf
 /GIT-VERSION-GEN eol=lf
diff --git a/.gitignore b/.gitignore
index 4470d7c..e096e0a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -139,6 +139,7 @@
 /git-request-pull
 /git-rerere
 /git-reset
+/git-restore
 /git-rev-list
 /git-rev-parse
 /git-revert
@@ -163,6 +164,7 @@
 /git-submodule
 /git-submodule--helper
 /git-svn
+/git-switch
 /git-symbolic-ref
 /git-tag
 /git-unpack-file
@@ -223,6 +225,11 @@
 *.user
 *.idb
 *.pdb
+*.ilk
+*.iobj
+*.ipdb
+*.dll
+.vs/
 /Debug/
 /Release/
 *.dSYM
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index 895b7cf..f867037 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -428,7 +428,7 @@
 SYNOPSIS
 --------
 [verse]
-'git-psuh'
+'git-psuh [<arg>...]'
 
 DESCRIPTION
 -----------
@@ -491,14 +491,16 @@
 tool for pulling out options you need to be able to handle, and it takes a
 usage string.
 
-In order to use it, we'll need to prepare a NULL-terminated usage string and a
-`builtin_psuh_options` array. Add a line to `#include "parse-options.h"`.
+In order to use it, we'll need to prepare a NULL-terminated array of usage
+strings and a `builtin_psuh_options` array.
 
-At global scope, add your usage:
+Add a line to `#include "parse-options.h"`.
+
+At global scope, add your array of usage strings:
 
 ----
 static const char * const psuh_usage[] = {
-	N_("git psuh"),
+	N_("git psuh [<arg>...]"),
 	NULL,
 };
 ----
diff --git a/Documentation/RelNotes/2.23.0.txt b/Documentation/RelNotes/2.23.0.txt
index a6cd592..a8c9029 100644
--- a/Documentation/RelNotes/2.23.0.txt
+++ b/Documentation/RelNotes/2.23.0.txt
@@ -46,6 +46,35 @@
  * The pattern "git diff/grep" use to extract funcname and words
    boundary for Rust has been added.
 
+ * "git status" can be told a non-standard default value for the
+   "--[no-]ahead-behind" option with a new configuration variable
+   status.aheadBehind.
+
+ * "git fetch" and "git pull" reports when a fetch results in
+   non-fast-forward updates to let the user notice unusual situation.
+   The commands learned "--no-shown-forced-updates" option to disable
+   this safety feature.
+
+ * Two new commands "git switch" and "git restore" are introduced to
+   split "checking out a branch to work on advancing its history" and
+   "checking out paths out of the index and/or a tree-ish to work on
+   advancing the current history" out of the single "git checkout"
+   command.
+
+ * "git branch --list" learned to always output the detached HEAD as
+   the first item (when the HEAD is detached, of course), regardless
+   of the locale.
+
+ * The conditional inclusion mechanism learned to base the choice on
+   the branch the HEAD currently is on.
+
+ * "git rev-list --objects" learned with "--no-object-names" option to
+   squelch the path to the object that is used as a grouping hint for
+   pack-objects.
+
+ * A new tag.gpgSign configuration variable turns "git tag -a" into
+   "git tag -s".
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -71,6 +100,17 @@
  * A new tutorial targetting specifically aspiring git-core
    developers has been added.
 
+ * Auto-detect how to tell HP-UX aCC where to use dynamically linked
+   libraries from at runtime.
+
+ * "git mergetool" and its tests now spawn fewer subprocesses.
+
+ * Dev support update to help tracing out tests.
+
+ * Support to build with MSVC has been updated.
+
+ * "git fetch" that grabs from a group of remotes learned to run the
+   auto-gc only once at the very end.
 
 
 Fixes since v2.22
@@ -175,9 +215,84 @@
    updated.
    (merge fc7e03aace mo/clang-format-for-each-update later to maint).
 
+ * "git branch --list" learned to show branches that are checked out
+   in other worktrees connected to the same repository prefixed with
+   '+', similar to the way the currently checked out branch is shown
+   with '*' in front.
+   (merge 6e9381469e nb/branch-show-other-worktrees-head later to maint).
+
+ * Code restructuring during 2.20 period broke fetching tags via
+   "import" based transports.
+   (merge f80d922355 fc/fetch-with-import-fix later to maint).
+
+ * The commit-graph file is now part of the "files that the runtime
+   may keep open file descriptors on, all of which would need to be
+   closed when done with the object store", and the file descriptor to
+   an existing commit-graph file now is closed before "gc" finalizes a
+   new instance to replace it.
+   (merge 2d511cfc0b ds/close-object-store later to maint).
+
+ * "git checkout -p" needs to selectively apply a patch in reverse,
+   which did not work well.
+   (merge 2bd69b9024 pw/add-p-recount later to maint).
+
+ * Code clean-up to avoid signed integer wraparounds during binary search.
+   (merge 568a05c5ec rs/avoid-overflow-in-midpoint-computation later to maint).
+
+ * "git interpret-trailers" always treated '#' as the comment
+   character, regardless of core.commentChar setting, which has been
+   corrected.
+   (merge 29c83fc23f jk/trailers-use-config later to maint).
+
+ * "git stash show 23" used to work, but no more after getting
+   rewritten in C; this regression has been corrected.
+   (merge 63b50c8ffe tg/stash-ref-by-index-fix later to maint).
+
+ * "git rebase --abort" used to leave refs/rewritten/ when concluding
+   "git rebase -r", which has been corrected.
+   (merge d559f502c5 pw/rebase-abort-clean-rewritten later to maint).
+
+ * An incorrect list of options was cached after command line
+   completion failed (e.g. trying to complete a command that requires
+   a repository outside one), which has been corrected.
+   (merge 69702523af nd/completion-no-cache-failure later to maint).
+
+ * The code to parse scaled numbers out of configuration files has
+   been made more robust and also easier to follow.
+   (merge 39c575c969 rs/config-unit-parsing later to maint).
+
+ * The codepath to compute delta islands used to spew progress output
+   without giving the callers any way to squelch it, which has been
+   fixed.
+   (merge bdbdf42f8a jk/delta-islands-progress-fix later to maint).
+
+ * Protocol capabilities that go over wire should never be translated,
+   but it was incorrectly marked for translation, which has been
+   corrected.  The output of protocol capabilities for debugging has
+   been tweaked a bit.
+
+ * Use "Erase in Line" CSI sequence that is already used in the editor
+   support to clear cruft in the progress output.
+   (merge 5b12e3123b sg/rebase-progress later to maint).
+
+ * "git submodule foreach" did not protect command line options passed
+   to the command to be run in each submodule correctly, when the
+   "--recursive" option was in use.
+   (merge 30db18b148 ms/submodule-foreach-fix later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge f547101b26 es/git-debugger-doc later to maint).
    (merge 7877ac3d7b js/bisect-helper-check-get-oid-return-value later to maint).
    (merge 0108f47eb3 sw/git-p4-unshelve-branched-files later to maint).
    (merge 9df8f734fd cm/send-email-document-req-modules later to maint).
    (merge afc3bf6eb1 ab/hash-object-doc later to maint).
+   (merge 1fde99cfc7 po/doc-branch later to maint).
+   (merge 459842e1c2 dl/config-alias-doc later to maint).
+   (merge 5d137fc2c7 cb/fsmonitor-intfix later to maint).
+   (merge 921d49be86 rs/copy-array later to maint).
+   (merge cc8d872e69 js/t3404-typofix later to maint).
+   (merge 729a9b558b cb/mkstemps-uint-type-fix later to maint).
+   (merge 9dae4fe79f js/gcc-8-and-9 later to maint).
+   (merge ed33bd8f30 js/t0001-case-insensitive later to maint).
+   (merge dfa880e336 jw/gitweb-sample-update later to maint).
+   (merge e532a90a9f sg/t5551-fetch-smart-error-is-translated later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 7e2a6f6..e3f5bc3 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -144,6 +144,20 @@
 	This is the same as `gitdir` except that matching is done
 	case-insensitively (e.g. on case-insensitive file sytems)
 
+`onbranch`::
+	The data that follows the keyword `onbranch:` is taken to be a
+	pattern with standard globbing wildcards and two additional
+	ones, `**/` and `/**`, that can match multiple path components.
+	If we are in a worktree where the name of the branch that is
+	currently checked out matches the pattern, the include condition
+	is met.
++
+If the pattern ends with `/`, `**` will be automatically added. For
+example, the pattern `foo/` becomes `foo/**`. In other words, it matches
+all branches that begin with `foo/`. This is useful if your branches are
+organized hierarchically and you would like to apply a configuration to
+all the branches in that hierarchy.
+
 A few more notes on matching via `gitdir` and `gitdir/i`:
 
  * Symlinks in `$GIT_DIR` are not resolved before matching.
@@ -206,6 +220,11 @@
 	[includeIf "gitdir:/path/to/group/"]
 		path = foo.inc
 
+	; include only if we are in a worktree where foo-branch is
+	; currently checked out
+	[includeIf "onbranch:foo-branch"]
+		path = foo.inc
+
 Values
 ~~~~~~
 
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index ec4f6ae..ee85c53 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -4,6 +4,10 @@
 	can tell Git that you do not need help by setting these to 'false':
 +
 --
+	fetchShowForcedUpdates::
+		Advice shown when linkgit:git-fetch[1] takes a long time
+		to calculate forced updates after ref updates, or to warn
+		that the check is disabled.
 	pushUpdateRejected::
 		Set this variable to 'false' if you want to disable
 		'pushNonFFCurrent',
@@ -37,12 +41,19 @@
 		we can still suggest that the user push to either
 		refs/heads/* or refs/tags/* based on the type of the
 		source object.
+	statusAheadBehind::
+		Shown when linkgit:git-status[1] computes the ahead/behind
+		counts for a local ref compared to its remote tracking ref,
+		and that calculation takes longer than expected. Will not
+		appear if `status.aheadBehind` is false or the option
+		`--no-ahead-behind` is given.
 	statusHints::
 		Show directions on how to proceed from the current
 		state in the output of linkgit:git-status[1], in
 		the template shown when writing commit messages in
 		linkgit:git-commit[1], and in the help message shown
-		by linkgit:git-checkout[1] when switching branch.
+		by linkgit:git-switch[1] or
+		linkgit:git-checkout[1] when switching branch.
 	statusUoption::
 		Advise to consider using the `-u` option to linkgit:git-status[1]
 		when the command takes more than 2 seconds to enumerate untracked
@@ -62,12 +73,14 @@
 		your information is guessed from the system username and
 		domain name.
 	detachedHead::
-		Advice shown when you used linkgit:git-checkout[1] to
-		move to the detach HEAD state, to instruct how to create
-		a local branch after the fact.
+		Advice shown when you used
+		linkgit:git-switch[1] or linkgit:git-checkout[1]
+		to move to the detach HEAD state, to instruct how to
+		create a local branch after the fact.
 	checkoutAmbiguousRemoteBranchName::
 		Advice shown when the argument to
-		linkgit:git-checkout[1] ambiguously resolves to a
+		linkgit:git-checkout[1] and linkgit:git-switch[1]
+		ambiguously resolves to a
 		remote tracking branch on more than one remote in
 		situations where an unambiguous argument would have
 		otherwise caused a remote-tracking branch to be
diff --git a/Documentation/config/alias.txt b/Documentation/config/alias.txt
index 0b14178..f1ca739 100644
--- a/Documentation/config/alias.txt
+++ b/Documentation/config/alias.txt
@@ -1,18 +1,28 @@
 alias.*::
 	Command aliases for the linkgit:git[1] command wrapper - e.g.
-	after defining "alias.last = cat-file commit HEAD", the invocation
-	"git last" is equivalent to "git cat-file commit HEAD". To avoid
+	after defining `alias.last = cat-file commit HEAD`, the invocation
+	`git last` is equivalent to `git cat-file commit HEAD`. To avoid
 	confusion and troubles with script usage, aliases that
 	hide existing Git commands are ignored. Arguments are split by
 	spaces, the usual shell quoting and escaping is supported.
 	A quote pair or a backslash can be used to quote them.
 +
+Note that the first word of an alias does not necessarily have to be a
+command. It can be a command-line option that will be passed into the
+invocation of `git`. In particular, this is useful when used with `-c`
+to pass in one-time configurations or `-p` to force pagination. For example,
+`loud-rebase = -c commit.verbose=true rebase` can be defined such that
+running `git loud-rebase` would be equivalent to
+`git -c commit.verbose=true rebase`. Also, `ps = -p status` would be a
+helpful alias since `git ps` would paginate the output of `git status`
+where the original command does not.
++
 If the alias expansion is prefixed with an exclamation point,
 it will be treated as a shell command.  For example, defining
-"alias.new = !gitk --all --not ORIG_HEAD", the invocation
-"git new" is equivalent to running the shell command
-"gitk --all --not ORIG_HEAD".  Note that shell commands will be
+`alias.new = !gitk --all --not ORIG_HEAD`, the invocation
+`git new` is equivalent to running the shell command
+`gitk --all --not ORIG_HEAD`.  Note that shell commands will be
 executed from the top-level directory of a repository, which may
 not necessarily be the current directory.
-`GIT_PREFIX` is set as returned by running 'git rev-parse --show-prefix'
+`GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
 from the original current directory. See linkgit:git-rev-parse[1].
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index 8f4b3fa..a592d52 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -1,5 +1,5 @@
 branch.autoSetupMerge::
-	Tells 'git branch' and 'git checkout' to set up new branches
+	Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
 	so that linkgit:git-pull[1] will appropriately merge from the
 	starting point branch. Note that even if this option is not set,
 	this behavior can be chosen per-branch using the `--track`
@@ -11,7 +11,7 @@
 	branch. This option defaults to true.
 
 branch.autoSetupRebase::
-	When a new branch is created with 'git branch' or 'git checkout'
+	When a new branch is created with 'git branch', 'git switch' or 'git checkout'
 	that tracks another branch, this variable tells Git to set
 	up pull to rebase instead of merge (see "branch.<name>.rebase").
 	When `never`, rebase is never automatically set to true.
diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index c4118fa..6b64681 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -1,5 +1,6 @@
 checkout.defaultRemote::
-	When you run 'git checkout <something>' and only have one
+	When you run 'git checkout <something>'
+	or 'git switch <something>' and only have one
 	remote, it may implicitly fall back on checking out and
 	tracking e.g. 'origin/<something>'. This stops working as soon
 	as you have more than one remote with a '<something>'
@@ -8,16 +9,10 @@
 	disambiguation. The typical use-case is to set this to
 	`origin`.
 +
-Currently this is used by linkgit:git-checkout[1] when 'git checkout
-<something>' will checkout the '<something>' branch on another remote,
+Currently this is used by linkgit:git-switch[1] and
+linkgit:git-checkout[1] when 'git checkout <something>'
+or 'git switch <something>'
+will checkout the '<something>' branch on another remote,
 and by linkgit:git-worktree[1] when 'git worktree add' refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
-
-checkout.optimizeNewBranch::
-	Optimizes the performance of "git checkout -b <new_branch>" when
-	using sparse-checkout.  When set to true, git will not update the
-	repo based on the current sparse-checkout settings.  This means it
-	will not update the skip-worktree bit in the index nor add/remove
-	files in the working directory to reflect the current sparse checkout
-	settings nor will it show the local changes.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index 2c4c9ba..5afb5a2 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -78,7 +78,8 @@
 diff.ignoreSubmodules::
 	Sets the default value of --ignore-submodules. Note that this
 	affects only 'git diff' Porcelain, and not lower level 'diff'
-	commands such as 'git diff-files'. 'git checkout' also honors
+	commands such as 'git diff-files'. 'git checkout'
+	and 'git switch' also honor
 	this setting when reporting uncommitted changes. Setting it to
 	'all' disables the submodule summary normally shown by 'git commit'
 	and 'git status' when `status.submoduleSummary` is set unless it is
diff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt
index cbfad6c..ba890b5 100644
--- a/Documentation/config/fetch.txt
+++ b/Documentation/config/fetch.txt
@@ -63,3 +63,8 @@
 	Unknown values will cause 'git fetch' to error out.
 +
 See also the `--negotiation-tip` option for linkgit:git-fetch[1].
+
+fetch.showForcedUpdates::
+	Set to false to enable `--no-show-forced-updates` in
+	linkgit:git-fetch[1] and linkgit:git-pull[1] commands.
+	Defaults to true.
diff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt
index ad846dd..a2d3c7e 100644
--- a/Documentation/config/interactive.txt
+++ b/Documentation/config/interactive.txt
@@ -2,7 +2,8 @@
 	In interactive commands, allow the user to provide one-letter
 	input with a single key (i.e., without hitting enter).
 	Currently this is used by the `--patch` mode of
-	linkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],
+	linkgit:git-add[1], linkgit:git-checkout[1],
+	linkgit:git-restore[1], linkgit:git-commit[1],
 	linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
 	setting is silently ignored if portable keystroke input
 	is not available; requires the Perl module Term::ReadKey.
diff --git a/Documentation/config/status.txt b/Documentation/config/status.txt
index ed72fa7d..0fc704a 100644
--- a/Documentation/config/status.txt
+++ b/Documentation/config/status.txt
@@ -12,6 +12,11 @@
 	Set to true to enable --branch by default in linkgit:git-status[1].
 	The option --no-branch takes precedence over this variable.
 
+status.aheadBehind::
+	Set to true to enable `--ahead-behind` and false to enable
+	`--no-ahead-behind` by default in linkgit:git-status[1] for
+	non-porcelain status formats.  Defaults to true.
+
 status.displayCommentPrefix::
 	If set to true, linkgit:git-status[1] will insert a comment
 	prefix before each output line (starting with
diff --git a/Documentation/config/tag.txt b/Documentation/config/tag.txt
index 663663b..ef5adb3 100644
--- a/Documentation/config/tag.txt
+++ b/Documentation/config/tag.txt
@@ -8,6 +8,14 @@
 	linkgit:git-tag[1]. Without the "--sort=<value>" option provided, the
 	value of this variable will be used as the default.
 
+tag.gpgSign::
+	A boolean to specify whether all tags should be GPG signed.
+	Use of this option when running in an automated script can
+	result in a large number of tags being signed. It is therefore
+	convenient to use an agent to avoid typing your gpg passphrase
+	several times. Note that this option doesn't affects tag signing
+	behavior enabled by "-u <keyid>" or "--local-user=<keyid>" options.
+
 tar.umask::
 	This variable can be used to restrict the permission bits of
 	tar archive entries.  The default is 0002, which turns off the
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 91c4775..3c9b4f9 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -88,6 +88,10 @@
 	Allow several <repository> and <group> arguments to be
 	specified. No <refspec>s may be specified.
 
+--[no-]auto-gc::
+	Run `git gc --auto` at the end to perform garbage collection
+	if needed. This is enabled by default.
+
 -p::
 --prune::
 	Before fetching, remove any remote-tracking references that no
@@ -221,6 +225,19 @@
 	When multiple `--server-option=<option>` are given, they are all
 	sent to the other side in the order listed on the command line.
 
+--show-forced-updates::
+	By default, git checks if a branch is force-updated during
+	fetch. This can be disabled through fetch.showForcedUpdates, but
+	the --show-forced-updates option guarantees this check occurs.
+	See linkgit:git-config[1].
+
+--no-show-forced-updates::
+	By default, git checks if a branch is force-updated during
+	fetch. Pass --no-show-forced-updates or set fetch.showForcedUpdates
+	to false to skip this check for performance reasons. If used during
+	'git-pull' the --ff-only option will still check for forced updates
+	before attempting a fast-forward update. See linkgit:git-config[1].
+
 -4::
 --ipv4::
 	Use IPv4 addresses only, ignoring IPv6 addresses.
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 6ebd512..135206f 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -8,12 +8,14 @@
 SYNOPSIS
 --------
 [verse]
-'git branch' [--color[=<when>] | --no-color] [-r | -a]
-	[--list] [--show-current] [-v [--abbrev=<length> | --no-abbrev]]
+'git branch' [--color[=<when>] | --no-color] [--show-current]
+	[-v [--abbrev=<length> | --no-abbrev]]
 	[--column[=<options>] | --no-column] [--sort=<key>]
 	[(--merged | --no-merged) [<commit>]]
 	[--contains [<commit]] [--no-contains [<commit>]]
-	[--points-at <object>] [--format=<format>] [<pattern>...]
+	[--points-at <object>] [--format=<format>]
+	[(-r | --remotes) | (-a | --all)]
+	[--list] [<pattern>...]
 'git branch' [--track | --no-track] [-f] <branchname> [<start-point>]
 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
 'git branch' --unset-upstream [<branchname>]
@@ -26,13 +28,19 @@
 -----------
 
 If `--list` is given, or if there are no non-option arguments, existing
-branches are listed; the current branch will be highlighted with an
-asterisk.  Option `-r` causes the remote-tracking branches to be listed,
-and option `-a` shows both local and remote branches. If a `<pattern>`
+branches are listed; the current branch will be highlighted in green and
+marked with an asterisk.  Any branches checked out in linked worktrees will
+be highlighted in cyan and marked with a plus sign. Option `-r` causes the
+remote-tracking branches to be listed,
+and option `-a` shows both local and remote branches.
+
+If a `<pattern>`
 is given, it is used as a shell wildcard to restrict the output to
 matching branches. If multiple patterns are given, a branch is shown if
-it matches any of the patterns.  Note that when providing a
-`<pattern>`, you must use `--list`; otherwise the command is interpreted
+it matches any of the patterns.
+
+Note that when providing a
+`<pattern>`, you must use `--list`; otherwise the command may be interpreted
 as branch creation.
 
 With `--contains`, shows only the branches that contain the named commit
@@ -52,7 +60,7 @@
 `HEAD`.
 
 Note that this will create the new branch, but it will not switch the
-working tree to it; use "git checkout <newbranch>" to switch to the
+working tree to it; use "git switch <newbranch>" to switch to the
 new branch.
 
 When a local branch is started off a remote-tracking branch, Git sets up the
@@ -153,10 +161,12 @@
 -r::
 --remotes::
 	List or delete (if used with -d) the remote-tracking branches.
+	Combine with `--list` to match the optional pattern(s).
 
 -a::
 --all::
 	List both remote-tracking branches and local branches.
+	Combine with `--list` to match optional pattern(s).
 
 -l::
 --list::
@@ -174,8 +184,10 @@
 	When in list mode,
 	show sha1 and commit subject line for each head, along with
 	relationship to upstream branch (if any). If given twice, print
-	the name of the upstream branch, as well (see also `git remote
-	show <remote>`).
+	the path of the linked worktree (if any) and the name of the upstream
+	branch, as well (see also `git remote show <remote>`).  Note that the
+	current worktree's HEAD will not have its path printed (it will always
+	be your current directory).
 
 -q::
 --quiet::
@@ -202,7 +214,7 @@
 +
 This behavior is the default when the start point is a remote-tracking branch.
 Set the branch.autoSetupMerge configuration variable to `false` if you
-want `git checkout` and `git branch` to always behave as if `--no-track`
+want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
 were given. Set it to `always` if you want this behavior when the
 start-point is either a local or remote-tracking branch.
 
@@ -301,7 +313,7 @@
 $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
 $ cd my2.6
 $ git branch my2.6.14 v2.6.14   <1>
-$ git checkout my2.6.14
+$ git switch my2.6.14
 ------------
 +
 <1> This step and the next one could be combined into a single step with
@@ -322,13 +334,25 @@
 <2> Delete the "test" branch even if the "master" branch (or whichever branch
     is currently checked out) does not have all commits from the test branch.
 
+Listing branches from a specific remote::
++
+------------
+$ git branch -r -l '<remote>/<pattern>'                 <1>
+$ git for-each-ref 'refs/remotes/<remote>/<pattern>'    <2>
+------------
++
+<1> Using `-a` would conflate <remote> with any local branches you happen to
+    have been prefixed with the same <remote> pattern.
+<2> `for-each-ref` can take a wide range of options. See linkgit:git-for-each-ref[1]
+
+Patterns will normally need quoting.
 
 NOTES
 -----
 
-If you are creating a branch that you want to checkout immediately, it is
-easier to use the git checkout command with its `-b` option to create
-a branch and check it out with a single command.
+If you are creating a branch that you want to switch to immediately,
+it is easier to use the "git switch" command with its `-c` option to
+do the same thing with a single command.
 
 The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
 serve four related but different purposes:
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index d9de992..ee6a414 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -88,7 +88,8 @@
 When run with `--branch` option in a repository, the input is first
 expanded for the ``previous checkout syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
-was checked out using "git checkout" operation. This option should be
+was checked out using "git switch" or "git checkout" operation.
+This option should be
 used by porcelains to accept this syntax anywhere a branch name is
 expected, so they can act as if you typed the branch name. As an
 exception note that, the ``previous checkout operation'' might result
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 964f912..cf3cac0 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -23,31 +23,22 @@
 also update `HEAD` to set the specified branch as the current
 branch.
 
-'git checkout' <branch>::
-	To prepare for working on <branch>, switch to it by updating
+'git checkout' [<branch>]::
+	To prepare for working on `<branch>`, switch to it by updating
 	the index and the files in the working tree, and by pointing
-	HEAD at the branch. Local modifications to the files in the
+	`HEAD` at the branch. Local modifications to the files in the
 	working tree are kept, so that they can be committed to the
-	<branch>.
+	`<branch>`.
 +
-If <branch> is not found but there does exist a tracking branch in
-exactly one remote (call it <remote>) with a matching name, treat as
-equivalent to
+If `<branch>` is not found but there does exist a tracking branch in
+exactly one remote (call it `<remote>`) with a matching name and
+`--no-guess` is not specified, treat as equivalent to
 +
 ------------
 $ git checkout -b <branch> --track <remote>/<branch>
 ------------
 +
-If the branch exists in multiple remotes and one of them is named by
-the `checkout.defaultRemote` configuration variable, we'll use that
-one for the purposes of disambiguation, even if the `<branch>` isn't
-unique across all remotes. Set it to
-e.g. `checkout.defaultRemote=origin` to always checkout remote
-branches from there if `<branch>` is ambiguous but exists on the
-'origin' remote. See also `checkout.defaultRemote` in
-linkgit:git-config[1].
-+
-You could omit <branch>, in which case the command degenerates to
+You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
 if exists, for the current branch.
@@ -61,7 +52,7 @@
 	`--track` without `-b` implies branch creation; see the
 	description of `--track` below.
 +
-If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it
 is reset. This is the transactional equivalent of
 +
 ------------
@@ -75,25 +66,25 @@
 'git checkout' --detach [<branch>]::
 'git checkout' [--detach] <commit>::
 
-	Prepare to work on top of <commit>, by detaching HEAD at it
+	Prepare to work on top of `<commit>`, by detaching `HEAD` at it
 	(see "DETACHED HEAD" section), and updating the index and the
 	files in the working tree.  Local modifications to the files
 	in the working tree are kept, so that the resulting working
 	tree will be the state recorded in the commit plus the local
 	modifications.
 +
-When the <commit> argument is a branch name, the `--detach` option can
-be used to detach HEAD at the tip of the branch (`git checkout
-<branch>` would check out that branch without detaching HEAD).
+When the `<commit>` argument is a branch name, the `--detach` option can
+be used to detach `HEAD` at the tip of the branch (`git checkout
+<branch>` would check out that branch without detaching `HEAD`).
 +
-Omitting <branch> detaches HEAD at the tip of the current branch.
+Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
 
 'git checkout' [<tree-ish>] [--] <pathspec>...::
 
 	Overwrite paths in the working tree by replacing with the
-	contents in the index or in the <tree-ish> (most often a
-	commit).  When a <tree-ish> is given, the paths that
-	match the <pathspec> are updated both in the index and in
+	contents in the index or in the `<tree-ish>` (most often a
+	commit).  When a `<tree-ish>` is given, the paths that
+	match the `<pathspec>` are updated both in the index and in
 	the working tree.
 +
 The index may contain unmerged entries because of a previous failed merge.
@@ -118,7 +109,8 @@
 --quiet::
 	Quiet, suppress feedback messages.
 
---[no-]progress::
+--progress::
+--no-progress::
 	Progress status is reported on the standard error stream
 	by default when it is attached to a terminal, unless `--quiet`
 	is specified. This flag enables progress reporting even if not
@@ -127,7 +119,7 @@
 -f::
 --force::
 	When switching branches, proceed even if the index or the
-	working tree differs from HEAD.  This is used to throw away
+	working tree differs from `HEAD`.  This is used to throw away
 	local changes.
 +
 When checking out paths from the index, do not fail upon unmerged
@@ -154,12 +146,12 @@
 of it").
 
 -b <new_branch>::
-	Create a new branch named <new_branch> and start it at
-	<start_point>; see linkgit:git-branch[1] for details.
+	Create a new branch named `<new_branch>` and start it at
+	`<start_point>`; see linkgit:git-branch[1] for details.
 
 -B <new_branch>::
-	Creates the branch <new_branch> and start it at <start_point>;
-	if it already exists, then reset it to <start_point>. This is
+	Creates the branch `<new_branch>` and start it at `<start_point>`;
+	if it already exists, then reset it to `<start_point>`. This is
 	equivalent to running "git branch" with "-f"; see
 	linkgit:git-branch[1] for details.
 
@@ -172,15 +164,36 @@
 derived from the remote-tracking branch, by looking at the local part of
 the refspec configured for the corresponding remote, and then stripping
 the initial part up to the "*".
-This would tell us to use "hack" as the local branch when branching
-off of "origin/hack" (or "remotes/origin/hack", or even
-"refs/remotes/origin/hack").  If the given name has no slash, or the above
+This would tell us to use `hack` as the local branch when branching
+off of `origin/hack` (or `remotes/origin/hack`, or even
+`refs/remotes/origin/hack`).  If the given name has no slash, or the above
 guessing results in an empty name, the guessing is aborted.  You can
 explicitly give a name with `-b` in such a case.
 
 --no-track::
 	Do not set up "upstream" configuration, even if the
-	branch.autoSetupMerge configuration variable is true.
+	`branch.autoSetupMerge` configuration variable is true.
+
+--guess::
+--no-guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git checkout -b <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to
+e.g. `checkout.defaultRemote=origin` to always checkout remote
+branches from there if `<branch>` is ambiguous but exists on the
+'origin' remote. See also `checkout.defaultRemote` in
+linkgit:git-config[1].
++
+Use `--no-guess` to disable this.
 
 -l::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
@@ -189,21 +202,21 @@
 --detach::
 	Rather than checking out a branch to work on it, check out a
 	commit for inspection and discardable experiments.
-	This is the default behavior of "git checkout <commit>" when
-	<commit> is not a branch name.  See the "DETACHED HEAD" section
+	This is the default behavior of `git checkout <commit>` when
+	`<commit>` is not a branch name.  See the "DETACHED HEAD" section
 	below for details.
 
 --orphan <new_branch>::
-	Create a new 'orphan' branch, named <new_branch>, started from
-	<start_point> and switch to it.  The first commit made on this
+	Create a new 'orphan' branch, named `<new_branch>`, started from
+	`<start_point>` and switch to it.  The first commit made on this
 	new branch will have no parents and it will be the root of a new
 	history totally disconnected from all the other branches and
 	commits.
 +
 The index and the working tree are adjusted as if you had previously run
-"git checkout <start_point>".  This allows you to start a new history
-that records a set of paths similar to <start_point> by easily running
-"git commit -a" to make the root commit.
+`git checkout <start_point>`.  This allows you to start a new history
+that records a set of paths similar to `<start_point>` by easily running
+`git commit -a` to make the root commit.
 +
 This can be useful when you want to publish the tree from a commit
 without exposing its full history. You might want to do this to publish
@@ -212,17 +225,17 @@
 code.
 +
 If you want to start a disconnected history that records a set of paths
-that is totally different from the one of <start_point>, then you should
+that is totally different from the one of `<start_point>`, then you should
 clear the index and the working tree right after creating the orphan
-branch by running "git rm -rf ." from the top level of the working tree.
+branch by running `git rm -rf .` from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
 working tree, by copying them from elsewhere, extracting a tarball, etc.
 
 --ignore-skip-worktree-bits::
 	In sparse checkout mode, `git checkout -- <paths>` would
-	update only entries matched by <paths> and sparse patterns
-	in $GIT_DIR/info/sparse-checkout. This option ignores
-	the sparse patterns and adds back any files in <paths>.
+	update only entries matched by `<paths>` and sparse patterns
+	in `$GIT_DIR/info/sparse-checkout`. This option ignores
+	the sparse patterns and adds back any files in `<paths>`.
 
 -m::
 --merge::
@@ -246,25 +259,25 @@
 When switching branches with `--merge`, staged changes may be lost.
 
 --conflict=<style>::
-	The same as --merge option above, but changes the way the
+	The same as `--merge` option above, but changes the way the
 	conflicting hunks are presented, overriding the
-	merge.conflictStyle configuration variable.  Possible values are
+	`merge.conflictStyle` configuration variable.  Possible values are
 	"merge" (default) and "diff3" (in addition to what is shown by
 	"merge" style, shows the original contents).
 
 -p::
 --patch::
 	Interactively select hunks in the difference between the
-	<tree-ish> (or the index, if unspecified) and the working
+	`<tree-ish>` (or the index, if unspecified) and the working
 	tree.  The chosen hunks are then applied in reverse to the
-	working tree (and if a <tree-ish> was specified, the index).
+	working tree (and if a `<tree-ish>` was specified, the index).
 +
 This means that you can use `git checkout -p` to selectively discard
 edits from your current working tree. See the ``Interactive Mode''
 section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 +
 Note that this option uses the no overlay mode by default (see also
-`--[no-]overlay`), and currently doesn't support overlay mode.
+`--overlay`), and currently doesn't support overlay mode.
 
 --ignore-other-worktrees::
 	`git checkout` refuses when the wanted ref is already checked
@@ -272,38 +285,42 @@
 	out anyway. In other words, the ref can be held by more than one
 	worktree.
 
---[no-]recurse-submodules::
-	Using --recurse-submodules will update the content of all initialized
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files when switching branches. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort
+	the operation when the new branch contains ignored files.
+
+--recurse-submodules::
+--no-recurse-submodules::
+	Using `--recurse-submodules` will update the content of all initialized
 	submodules according to the commit recorded in the superproject. If
 	local modifications in a submodule would be overwritten the checkout
-	will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+	will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
 	is used, the work trees of submodules will not be updated.
-	Just like linkgit:git-submodule[1], this will detach the
-	submodules HEAD.
+	Just like linkgit:git-submodule[1], this will detach `HEAD` of the
+	submodule.
 
---no-guess::
-	Do not attempt to create a branch if a remote tracking branch
-	of the same name exists.
-
---[no-]overlay::
+--overlay::
+--no-overlay::
 	In the default overlay mode, `git checkout` never
 	removes files from the index or the working tree.  When
 	specifying `--no-overlay`, files that appear in the index and
-	working tree, but not in <tree-ish> are removed, to make them
-	match <tree-ish> exactly.
+	working tree, but not in `<tree-ish>` are removed, to make them
+	match `<tree-ish>` exactly.
 
 <branch>::
 	Branch to checkout; if it refers to a branch (i.e., a name that,
 	when prepended with "refs/heads/", is a valid ref), then that
 	branch is checked out. Otherwise, if it refers to a valid
-	commit, your HEAD becomes "detached" and you are no longer on
+	commit, your `HEAD` becomes "detached" and you are no longer on
 	any branch (see below for details).
 +
-You can use the `"@{-N}"` syntax to refer to the N-th last
+You can use the `@{-N}` syntax to refer to the N-th last
 branch/commit checked out using "git checkout" operation. You may
-also specify `-` which is synonymous to `"@{-1}"`.
+also specify `-` which is synonymous to `@{-1}`.
 +
-As a special case, you may use `"A...B"` as a shortcut for the
+As a special case, you may use `A...B` as a shortcut for the
 merge base of `A` and `B` if there is exactly one merge base. You can
 leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
@@ -312,7 +329,7 @@
 
 <start_point>::
 	The name of a commit at which to start the new branch; see
-	linkgit:git-branch[1] for details. Defaults to HEAD.
+	linkgit:git-branch[1] for details. Defaults to `HEAD`.
 +
 As a special case, you may use `"A...B"` as a shortcut for the
 merge base of `A` and `B` if there is exactly one merge base. You can
@@ -326,9 +343,9 @@
 
 DETACHED HEAD
 -------------
-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each
+`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each
 branch refers to a specific commit. Let's look at a repo with three
-commits, one of them tagged, and with branch 'master' checked out:
+commits, one of them tagged, and with branch `master` checked out:
 
 ------------
            HEAD (refers to branch 'master')
@@ -341,10 +358,10 @@
 ------------
 
 When a commit is created in this state, the branch is updated to refer to
-the new commit. Specifically, 'git commit' creates a new commit 'd', whose
-parent is commit 'c', and then updates branch 'master' to refer to new
-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers
-to commit 'd':
+the new commit. Specifically, 'git commit' creates a new commit `d`, whose
+parent is commit `c`, and then updates branch `master` to refer to new
+commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers
+to commit `d`:
 
 ------------
 $ edit; git add; git commit
@@ -361,7 +378,7 @@
 It is sometimes useful to be able to checkout a commit that is not at
 the tip of any named branch, or even to create a new commit that is not
 referenced by a named branch. Let's look at what happens when we
-checkout commit 'b' (here we show two ways this may be done):
+checkout commit `b` (here we show two ways this may be done):
 
 ------------
 $ git checkout v2.0  # or
@@ -376,9 +393,9 @@
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-Notice that regardless of which checkout command we use, HEAD now refers
-directly to commit 'b'. This is known as being in detached HEAD state.
-It means simply that HEAD refers to a specific commit, as opposed to
+Notice that regardless of which checkout command we use, `HEAD` now refers
+directly to commit `b`. This is known as being in detached `HEAD` state.
+It means simply that `HEAD` refers to a specific commit, as opposed to
 referring to a named branch. Let's see what happens when we create a commit:
 
 ------------
@@ -395,7 +412,7 @@
   tag 'v2.0' (refers to commit 'b')
 ------------
 
-There is now a new commit 'e', but it is referenced only by HEAD. We can
+There is now a new commit `e`, but it is referenced only by `HEAD`. We can
 of course add yet another commit in this state:
 
 ------------
@@ -413,7 +430,7 @@
 ------------
 
 In fact, we can perform all the normal Git operations. But, let's look
-at what happens when we then checkout master:
+at what happens when we then checkout `master`:
 
 ------------
 $ git checkout master
@@ -428,9 +445,9 @@
 ------------
 
 It is important to realize that at this point nothing refers to commit
-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted
+`f`. Eventually commit `f` (and by extension commit `e`) will be deleted
 by the routine Git garbage collection process, unless we create a reference
-before that happens. If we have not yet moved away from commit 'f',
+before that happens. If we have not yet moved away from commit `f`,
 any of these will create a reference to it:
 
 ------------
@@ -439,19 +456,19 @@
 $ git tag foo           <3>
 ------------
 
-<1> creates a new branch 'foo', which refers to commit 'f', and then
-    updates HEAD to refer to branch 'foo'. In other words, we'll no longer
-    be in detached HEAD state after this command.
+<1> creates a new branch `foo`, which refers to commit `f`, and then
+    updates `HEAD` to refer to branch `foo`. In other words, we'll no longer
+    be in detached `HEAD` state after this command.
 
-<2> similarly creates a new branch 'foo', which refers to commit 'f',
-    but leaves HEAD detached.
+<2> similarly creates a new branch `foo`, which refers to commit `f`,
+    but leaves `HEAD` detached.
 
-<3> creates a new tag 'foo', which refers to commit 'f',
-    leaving HEAD detached.
+<3> creates a new tag `foo`, which refers to commit `f`,
+    leaving `HEAD` detached.
 
-If we have moved away from commit 'f', then we must first recover its object
+If we have moved away from commit `f`, then we must first recover its object
 name (typically by using git reflog), and then we can create a reference to
-it. For example, to see the last two commits to which HEAD referred, we
+it. For example, to see the last two commits to which `HEAD` referred, we
 can use either of these commands:
 
 ------------
@@ -462,12 +479,12 @@
 ARGUMENT DISAMBIGUATION
 -----------------------
 
-When there is only one argument given and it is not `--` (e.g. "git
-checkout abc"), and when the argument is both a valid `<tree-ish>`
-(e.g. a branch "abc" exists) and a valid `<pathspec>` (e.g. a file
+When there is only one argument given and it is not `--` (e.g. `git
+checkout abc`), and when the argument is both a valid `<tree-ish>`
+(e.g. a branch `abc` exists) and a valid `<pathspec>` (e.g. a file
 or a directory whose name is "abc" exists), Git would usually ask
 you to disambiguate.  Because checking out a branch is so common an
-operation, however, "git checkout abc" takes "abc" as a `<tree-ish>`
+operation, however, `git checkout abc` takes "abc" as a `<tree-ish>`
 in such a situation.  Use `git checkout -- <pathspec>` if you want
 to checkout these paths out of the index.
 
@@ -475,7 +492,7 @@
 --------
 
 . The following sequence checks out the `master` branch, reverts
-  the `Makefile` to two revisions back, deletes hello.c by
+  the `Makefile` to two revisions back, deletes `hello.c` by
   mistake, and gets it back from the index.
 +
 ------------
@@ -487,7 +504,7 @@
 +
 <1> switch branch
 <2> take a file out of another commit
-<3> restore hello.c from the index
+<3> restore `hello.c` from the index
 +
 If you want to check out _all_ C source files out of the index,
 you can say
@@ -516,7 +533,7 @@
 $ git checkout mytopic
 ------------
 +
-However, your "wrong" branch and correct "mytopic" branch may
+However, your "wrong" branch and correct `mytopic` branch may
 differ in files that you have modified locally, in which case
 the above checkout would fail like this:
 +
@@ -557,6 +574,11 @@
 $ git add frotz
 ------------
 
+SEE ALSO
+--------
+linkgit:git-switch[1],
+linkgit:git-restore[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index db876f7..0028ff1 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -63,7 +63,7 @@
 	still use the ignore rules given with `-e` options from the command
 	line.  This allows removing all untracked
 	files, including build products.  This can be used (possibly in
-	conjunction with 'git reset') to create a pristine
+	conjunction with 'git restore' or 'git reset') to create a pristine
 	working directory to test a clean build.
 
 -X::
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 5fc97f1..34011c2 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -23,7 +23,7 @@
 
 Clones a repository into a newly created directory, creates
 remote-tracking branches for each branch in the cloned repository
-(visible using `git branch -r`), and creates and checks out an
+(visible using `git branch --remotes`), and creates and checks out an
 initial branch that is forked from the cloned repository's
 currently active branch.
 
@@ -41,8 +41,8 @@
 
 OPTIONS
 -------
---local::
 -l::
+--local::
 	When the repository to clone from is on a local machine,
 	this flag bypasses the normal "Git aware" transport
 	mechanism and clones the repository by making a copy of
@@ -63,8 +63,8 @@
 	directory instead of using hardlinks. This may be desirable
 	if you are trying to make a back-up of your repository.
 
---shared::
 -s::
+--shared::
 	When the repository to clone is on the local machine,
 	instead of using hard links, automatically setup
 	`.git/objects/info/alternates` to share the objects
@@ -81,13 +81,13 @@
 If these objects are removed and were referenced by the cloned repository,
 then the cloned repository will become corrupt.
 +
-Note that running `git repack` without the `-l` option in a repository
-cloned with `-s` will copy objects from the source repository into a pack
-in the cloned repository, removing the disk space savings of `clone -s`.
-It is safe, however, to run `git gc`, which uses the `-l` option by
+Note that running `git repack` without the `--local` option in a repository
+cloned with `--shared` will copy objects from the source repository into a pack
+in the cloned repository, removing the disk space savings of `clone --shared`.
+It is safe, however, to run `git gc`, which uses the `--local` option by
 default.
 +
-If you want to break the dependency of a repository cloned with `-s` on
+If you want to break the dependency of a repository cloned with `--shared` on
 its source repository, you can simply run `git repack -a` to copy all
 objects from the source repository into a pack in the cloned repository.
 
@@ -116,19 +116,19 @@
 	same repository, and this option can be used to stop the
 	borrowing.
 
---quiet::
 -q::
+--quiet::
 	Operate quietly.  Progress is not reported to the standard
 	error stream.
 
---verbose::
 -v::
+--verbose::
 	Run verbosely. Does not affect the reporting of progress status
 	to the standard error stream.
 
 --progress::
 	Progress status is reported on the standard error stream
-	by default when it is attached to a terminal, unless -q
+	by default when it is attached to a terminal, unless `--quiet`
 	is specified. This flag forces progress status even if the
 	standard error stream is not directed to a terminal.
 
@@ -140,15 +140,15 @@
 	When multiple `--server-option=<option>` are given, they are all
 	sent to the other side in the order listed on the command line.
 
---no-checkout::
 -n::
+--no-checkout::
 	No checkout of HEAD is performed after the clone is complete.
 
 --bare::
 	Make a 'bare' Git repository.  That is, instead of
 	creating `<directory>` and placing the administrative
 	files in `<directory>/.git`, make the `<directory>`
-	itself the `$GIT_DIR`. This obviously implies the `-n`
+	itself the `$GIT_DIR`. This obviously implies the `--no-checkout`
 	because there is nowhere to check out the working tree.
 	Also the branch heads at the remote are copied directly
 	to corresponding local branch heads, without mapping
@@ -164,13 +164,13 @@
 	that all these refs are overwritten by a `git remote update` in the
 	target repository.
 
---origin <name>::
 -o <name>::
+--origin <name>::
 	Instead of using the remote name `origin` to keep track
 	of the upstream repository, use `<name>`.
 
---branch <name>::
 -b <name>::
+--branch <name>::
 	Instead of pointing the newly created HEAD to the branch pointed
 	to by the cloned repository's HEAD, point to `<name>` branch
 	instead. In a non-bare repository, this is the branch that will
@@ -178,8 +178,8 @@
 	`--branch` can also take tags and detaches the HEAD at that commit
 	in the resulting repository.
 
---upload-pack <upload-pack>::
 -u <upload-pack>::
+--upload-pack <upload-pack>::
 	When given, and the repository to clone from is accessed
 	via ssh, this specifies a non-default path for the command
 	run on the other end.
@@ -188,8 +188,8 @@
 	Specify the directory from which templates will be used;
 	(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
 
---config <key>=<value>::
 -c <key>=<value>::
+--config <key>=<value>::
 	Set a configuration variable in the newly-created repository;
 	this takes effect immediately after the repository is
 	initialized, but before the remote history is fetched or any
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index a85c2c2..7628193 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -359,7 +359,7 @@
 your working tree are temporarily stored to a staging area
 called the "index" with 'git add'.  A file can be
 reverted back, only in the index but not in the working tree,
-to that of the last commit with `git reset HEAD -- <file>`,
+to that of the last commit with `git restore --staged <file>`,
 which effectively reverts 'git add' and prevents the changes to
 this file from participating in the next commit.  After building
 the state to be committed incrementally with these commands,
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 774cecc..6dcd39f 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -214,6 +214,11 @@
 	`:lstrip` and `:rstrip` options in the same way as `refname`
 	above.
 
+worktreepath::
+	The absolute path to the worktree in which the ref is checked
+	out, if it is checked out in any linked worktree. Empty string
+	otherwise.
+
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
 be used to specify the value in the header field.
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 9ce5b8a..b9b97e6 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -426,8 +426,8 @@
 * Apply it:
 
     $ git fetch <project> master:test-apply
-    $ git checkout test-apply
-    $ git reset --hard
+    $ git switch test-apply
+    $ git restore --source=HEAD --staged --worktree :/
     $ git am a.patch
 
 If it does not apply correctly, there can be various reasons.
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 9f07f4f..261d5c1 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -149,7 +149,7 @@
 Discussion on fork-point mode
 -----------------------------
 
-After working on the `topic` branch created with `git checkout -b
+After working on the `topic` branch created with `git switch -c
 topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index c01cfa6..01fd52d 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -13,8 +13,7 @@
 	[-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
 	[--[no-]allow-unrelated-histories]
 	[--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...]
-'git merge' --abort
-'git merge' --continue
+'git merge' (--continue | --abort | --quit)
 
 DESCRIPTION
 -----------
@@ -88,6 +87,11 @@
 	Allow the rerere mechanism to update the index with the
 	result of auto-conflict resolution if possible.
 
+--overwrite-ignore::
+--no-overwrite-ignore::
+	Silently overwrite ignored files from the merge result. This
+	is the default behavior. Use `--no-overwrite-ignore` to abort.
+
 --abort::
 	Abort the current conflict resolution process, and
 	try to reconstruct the pre-merge state.
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 5e4e927..6156609 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -12,12 +12,12 @@
 	[<upstream> [<branch>]]
 'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
 	--root [<branch>]
-'git rebase' --continue | --skip | --abort | --quit | --edit-todo | --show-current-patch
+'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
 
 DESCRIPTION
 -----------
 If <branch> is specified, 'git rebase' will perform an automatic
-`git checkout <branch>` before doing anything else.  Otherwise
+`git switch <branch>` before doing anything else.  Otherwise
 it remains on the current branch.
 
 If <upstream> is not specified, the upstream configured in
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 0cad37f..9659abb 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -230,7 +230,7 @@
   staging/master
   staging/staging-linus
   staging/staging-next
-$ git checkout -b staging staging/master
+$ git switch -c staging staging/master
 ...
 ------------
 
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index 95763d7..4cfc883 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -91,7 +91,7 @@
 One way to do it is to pull master into the topic branch:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 
               o---*---o---+ topic
@@ -113,10 +113,10 @@
 in which case the final commit graph would look like this:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o---+---o---o topic
@@ -136,11 +136,11 @@
 top of the tip before the test merge:
 
 ------------
-	$ git checkout topic
+	$ git switch topic
 	$ git merge master
 	$ git reset --hard HEAD^ ;# rewind the test merge
 	$ ... work on both topic and master branches
-	$ git checkout master
+	$ git switch master
 	$ git merge topic
 
               o---*---o-------o---o topic
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 26e746c..97e0544 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -25,12 +25,13 @@
 	the current branch.)
 +
 This means that `git reset <paths>` is the opposite of `git add
-<paths>`.
+<paths>`. This command is equivalent to
+`git restore [--source=<tree-ish>] --staged <paths>...`.
 +
 After running `git reset <paths>` to update the index entry, you can
-use linkgit:git-checkout[1] to check the contents out of the index to
-the working tree.
-Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
+use linkgit:git-restore[1] to check the contents out of the index to
+the working tree. Alternatively, using linkgit:git-restore[1]
+and specifying a commit with `--source`, you
 can copy the contents of a path out of a commit to the index and to the
 working tree in one go.
 
@@ -86,8 +87,8 @@
 	changes, reset is aborted.
 --
 
-If you want to undo a commit other than the latest on a branch,
-linkgit:git-revert[1] is your friend.
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
 
 
 OPTIONS
@@ -149,9 +150,9 @@
 Undo a commit, making it a topic branch::
 +
 ------------
-$ git branch topic/wip     <1>
-$ git reset --hard HEAD~3  <2>
-$ git checkout topic/wip   <3>
+$ git branch topic/wip          <1>
+$ git reset --hard HEAD~3       <2>
+$ git switch topic/wip          <3>
 ------------
 +
 <1> You have made some commits, but realize they were premature
@@ -232,13 +233,13 @@
 need to get to the other branch for a quick bugfix.
 +
 ------------
-$ git checkout feature ;# you were working in "feature" branch and
-$ work work work       ;# got interrupted
+$ git switch feature  ;# you were working in "feature" branch and
+$ work work work      ;# got interrupted
 $ git commit -a -m "snapshot WIP"                 <1>
-$ git checkout master
+$ git switch master
 $ fix fix fix
 $ git commit ;# commit with real log
-$ git checkout feature
+$ git switch feature
 $ git reset --soft HEAD^ ;# go back to WIP state  <2>
 $ git reset                                       <3>
 ------------
@@ -279,18 +280,18 @@
 +
 ------------
 $ git tag start
-$ git checkout -b branch1
+$ git switch -c branch1
 $ edit
 $ git commit ...                            <1>
 $ edit
-$ git checkout -b branch2                   <2>
+$ git switch -c branch2                     <2>
 $ git reset --keep start                    <3>
 ------------
 +
 <1> This commits your first edits in `branch1`.
 <2> In the ideal world, you could have realized that the earlier
     commit did not belong to the new topic when you created and switched
-    to `branch2` (i.e. `git checkout -b branch2 start`), but nobody is
+    to `branch2` (i.e. `git switch -c branch2 start`), but nobody is
     perfect.
 <3> But you can use `reset --keep` to remove the unwanted commit after
     you switched to `branch2`.
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
new file mode 100644
index 0000000..d90093f
--- /dev/null
+++ b/Documentation/git-restore.txt
@@ -0,0 +1,185 @@
+git-restore(1)
+==============
+
+NAME
+----
+git-restore - Restore working tree files
+
+SYNOPSIS
+--------
+[verse]
+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]
+
+DESCRIPTION
+-----------
+Restore specified paths in the working tree with some contents from a
+restore source. If a path is tracked but does not exist in the restore
+source, it will be removed to match the source.
+
+The command can also be used to restore the content in the index with
+`--staged`, or restore both the working tree and the index with
+`--staged --worktree`.
+
+By default, the restore sources for working tree and the index are the
+index and `HEAD` respectively. `--source` could be used to specify a
+commit as the restore source.
+
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
+
+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
+
+OPTIONS
+-------
+-s <tree>::
+--source=<tree>::
+	Restore the working tree files with the content from the given
+	tree. It is common to specify the source tree by naming a
+	commit, branch or tag associated with it.
++
+If not specified, the default restore source for the working tree is
+the index, and the default restore source for the index index is
+`HEAD`. When both `--staged` and `--worktree` are specified,
+`--source` must also be specified.
+
+-p::
+--patch::
+	Interactively select hunks in the difference between the
+	restore source and the restore location. See the ``Interactive
+	Mode'' section of linkgit:git-add[1] to learn how to operate
+	the `--patch` mode.
++
+Note that `--patch` can accept no pathspec and will prompt to restore
+all modified paths.
+
+-W::
+--worktree::
+-S::
+--staged::
+	Specify the restore location. If neither option is specified,
+	by default the working tree is restored. Specifying `--staged`
+	will only restore the index. Specifying both restores both.
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages. Implies `--no-progress`.
+
+--progress::
+--no-progress::
+	Progress status is reported on the standard error stream
+	by default when it is attached to a terminal, unless `--quiet`
+	is specified. This flag enables progress reporting even if not
+	attached to a terminal, regardless of `--quiet`.
+
+--ours::
+--theirs::
+	When restoring files in the working tree from the index, use
+	stage #2 ('ours') or #3 ('theirs') for unmerged paths.
++
+Note that during `git rebase` and `git pull --rebase`, 'ours' and
+'theirs' may appear swapped. See the explanation of the same options
+in linkgit:git-checkout[1] for details.
+
+-m::
+--merge::
+	When restoring files on the working tree from the index,
+	recreate the conflicted merge in the unmerged paths.
+
+--conflict=<style>::
+	The same as `--merge` option above, but changes the way the
+	conflicting hunks are presented, overriding the
+	`merge.conflictStyle` configuration variable.  Possible values
+	are "merge" (default) and "diff3" (in addition to what is
+	shown by "merge" style, shows the original contents).
+
+--ignore-unmerged::
+	When restoring files on the working tree from the index, do
+	not abort the operation if there are unmerged entries and
+	neither `--ours`, `--theirs`, `--merge` or `--conflict` is
+	specified. Unmerged paths on the working tree are left alone.
+
+--ignore-skip-worktree-bits::
+	In sparse checkout mode, by default is to only update entries
+	matched by `<pathspec>` and sparse patterns in
+	$GIT_DIR/info/sparse-checkout. This option ignores the sparse
+	patterns and unconditionally restores any files in
+	`<pathspec>`.
+
+--overlay::
+--no-overlay::
+	In overlay mode, the command never removes files when
+	restoring. In no-overlay mode, tracked files that do not
+	appear in the `--source` tree are removed, to make them match
+	`<tree>` exactly. The default is no-overlay mode.
+
+EXAMPLES
+--------
+
+The following sequence switches to the `master` branch, reverts the
+`Makefile` to two revisions back, deletes hello.c by mistake, and gets
+it back from the index.
+
+------------
+$ git switch master
+$ git restore --source master~2 Makefile  <1>
+$ rm -f hello.c
+$ git restore hello.c                     <2>
+------------
+
+<1> take a file out of another commit
+<2> restore hello.c from the index
+
+If you want to restore _all_ C source files to match the version in
+the index, you can say
+
+------------
+$ git restore '*.c'
+------------
+
+Note the quotes around `*.c`.  The file `hello.c` will also be
+restored, even though it is no longer in the working tree, because the
+file globbing is used to match entries in the index (not in the
+working tree by the shell).
+
+To restore all files in the current directory
+
+------------
+$ git restore .
+------------
+
+or to restore all working tree files with 'top' pathspec magic (see
+linkgit:gitglossary[7])
+
+------------
+$ git restore :/
+------------
+
+To restore a file in the index to match the version in `HEAD` (this is
+the same as using linkgit:git-reset[1])
+
+------------
+$ git restore --staged hello.c
+------------
+
+or you can restore both the index and the working tree (this the same
+as using linkgit:git-checkout[1])
+
+------------
+$ git restore --source=HEAD --staged --worktree hello.c
+------------
+
+or the short form which is more practical but less readable:
+
+------------
+$ git restore -s@ -SW hello.c
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1],
+linkgit:git-reset[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 88609ff..9392760 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -48,6 +48,7 @@
 	     [ --date=<format>]
 	     [ [ --objects | --objects-edge | --objects-edge-aggressive ]
 	       [ --unpacked ]
+	       [ --object-names | --no-object-names ]
 	       [ --filter=<filter-spec> [ --filter-print-omitted ] ] ]
 	     [ --missing=<missing-action> ]
 	     [ --pretty | --header ]
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 0c82ca5..fae4d66 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -26,10 +26,13 @@
 throw away all uncommitted changes in your working directory, you
 should see linkgit:git-reset[1], particularly the `--hard` option.  If
 you want to extract specific files as they were in another commit, you
-should see linkgit:git-checkout[1], specifically the `git checkout
-<commit> -- <filename>` syntax.  Take care with these alternatives as
+should see linkgit:git-restore[1], specifically the `--source`
+option. Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
+See "Reset, restore and revert" in linkgit:git[1] for the differences
+between the three commands.
+
 OPTIONS
 -------
 <commit>...::
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index e31ea7d..8fbe12c 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -235,12 +235,12 @@
 +
 ----------------------------------------------------------------
 # ... hack hack hack ...
-$ git checkout -b my_wip
+$ git switch -c my_wip
 $ git commit -a -m "WIP"
-$ git checkout master
+$ git switch master
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
-$ git checkout my_wip
+$ git switch my_wip
 $ git reset --soft HEAD^
 # ... continue hacking ...
 ----------------------------------------------------------------
@@ -293,7 +293,8 @@
 linkgit:git-checkout[1],
 linkgit:git-commit[1],
 linkgit:git-reflog[1],
-linkgit:git-reset[1]
+linkgit:git-reset[1],
+linkgit:git-switch[1]
 
 GIT
 ---
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
new file mode 100644
index 0000000..1979003
--- /dev/null
+++ b/Documentation/git-switch.txt
@@ -0,0 +1,273 @@
+git-switch(1)
+=============
+
+NAME
+----
+git-switch - Switch branches
+
+SYNOPSIS
+--------
+[verse]
+'git switch' [<options>] [--no-guess] <branch>
+'git switch' [<options>] --detach [<start-point>]
+'git switch' [<options>] (-c|-C) <new-branch> [<start-point>]
+'git switch' [<options>] --orphan <new-branch>
+
+DESCRIPTION
+-----------
+Switch to a specified branch. The working tree and the index are
+updated to match the branch. All new commits will be added to the tip
+of this branch.
+
+Optionally a new branch could be created with either `-c`, `-C`,
+automatically from a remote branch of same name (see `--guess`), or
+detach the working tree from any branch with `--detach`, along with
+switching.
+
+Switching branches does not require a clean index and working tree
+(i.e. no differences compared to `HEAD`). The operation is aborted
+however if the operation leads to loss of local changes, unless told
+otherwise with `--discard-changes` or `--merge`.
+
+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
+
+OPTIONS
+-------
+<branch>::
+	Branch to switch to.
+
+<new-branch>::
+	Name for the new branch.
+
+<start-point>::
+	The starting point for the new branch. Specifying a
+	`<start-point>` allows you to create a branch based on some
+	other point in history than where HEAD currently points. (Or,
+	in the case of `--detach`, allows you to inspect and detach
+	from some other point.)
++
+You can use the `@{-N}` syntax to refer to the N-th last
+branch/commit switched to using "git switch" or "git checkout"
+operation. You may also specify `-` which is synonymous to `@{-1}`.
+This is often used to switch quickly between two branches, or to undo
+a branch switch by mistake.
++
+As a special case, you may use `A...B` as a shortcut for the merge
+base of `A` and `B` if there is exactly one merge base. You can leave
+out at most one of `A` and `B`, in which case it defaults to `HEAD`.
+
+-c <new-branch>::
+--create <new-branch>::
+	Create a new branch named `<new-branch>` starting at
+	`<start-point>` before switching to the branch. This is a
+	convenient shortcut for:
++
+------------
+$ git branch <new-branch>
+$ git switch <new-branch>
+------------
+
+-C <new-branch>::
+--force-create <new-branch>::
+	Similar to `--create` except that if `<new-branch>` already
+	exists, it will be reset to `<start-point>`. This is a
+	convenient shortcut for:
++
+------------
+$ git branch -f <new-branch>
+$ git switch <new-branch>
+------------
+
+-d::
+--detach::
+	Switch to a commit for inspection and discardable
+	experiments. See the "DETACHED HEAD" section in
+	linkgit:git-checkout[1] for details.
+
+--guess::
+--no-guess::
+	If `<branch>` is not found but there does exist a tracking
+	branch in exactly one remote (call it `<remote>`) with a
+	matching name, treat as equivalent to
++
+------------
+$ git switch -c <branch> --track <remote>/<branch>
+------------
++
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to e.g. `checkout.defaultRemote=origin`
+to always checkout remote branches from there if `<branch>` is
+ambiguous but exists on the 'origin' remote. See also
+`checkout.defaultRemote` in linkgit:git-config[1].
++
+`--guess` is the default behavior. Use `--no-guess` to disable it.
+
+-f::
+--force::
+	An alias for `--discard-changes`.
+
+--discard-changes::
+	Proceed even if the index or the working tree differs from
+	`HEAD`. Both the index and working tree are restored to match
+	the switching target. If `--recurse-submodules` is specified,
+	submodule content is also restored to match the switching
+	target. This is used to throw away local changes.
+
+-m::
+--merge::
+	If you have local modifications to one or more files that are
+	different between the current branch and the branch to which
+	you are switching, the command refuses to switch branches in
+	order to preserve your modifications in context.  However,
+	with this option, a three-way merge between the current
+	branch, your working tree contents, and the new branch is
+	done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git add` (or `git rm` if the merge
+should result in deletion of the path).
+
+--conflict=<style>::
+	The same as `--merge` option above, but changes the way the
+	conflicting hunks are presented, overriding the
+	`merge.conflictStyle` configuration variable.  Possible values are
+	"merge" (default) and "diff3" (in addition to what is shown by
+	"merge" style, shows the original contents).
+
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
+--progress::
+--no-progress::
+	Progress status is reported on the standard error stream
+	by default when it is attached to a terminal, unless `--quiet`
+	is specified. This flag enables progress reporting even if not
+	attached to a terminal, regardless of `--quiet`.
+
+-t::
+--track::
+	When creating a new branch, set up "upstream" configuration.
+	`-c` is implied. See `--track` in linkgit:git-branch[1] for
+	details.
++
+If no `-c` option is given, the name of the new branch will be derived
+from the remote-tracking branch, by looking at the local part of the
+refspec configured for the corresponding remote, and then stripping
+the initial part up to the "*".  This would tell us to use `hack` as
+the local branch when branching off of `origin/hack` (or
+`remotes/origin/hack`, or even `refs/remotes/origin/hack`).  If the
+given name has no slash, or the above guessing results in an empty
+name, the guessing is aborted.  You can explicitly give a name with
+`-c` in such a case.
+
+--no-track::
+	Do not set up "upstream" configuration, even if the
+	`branch.autoSetupMerge` configuration variable is true.
+
+--orphan <new-branch>::
+	Create a new 'orphan' branch, named `<new-branch>`. All
+	tracked files are removed.
+
+--ignore-other-worktrees::
+	`git switch` refuses when the wanted ref is already
+	checked out by another worktree. This option makes it check
+	the ref out anyway. In other words, the ref can be held by
+	more than one worktree.
+
+--recurse-submodules::
+--no-recurse-submodules::
+	Using `--recurse-submodules` will update the content of all
+	initialized submodules according to the commit recorded in the
+	superproject. If nothing (or `--no-recurse-submodules`) is
+	used, the work trees of submodules will not be updated. Just
+	like linkgit:git-submodule[1], this will detach `HEAD` of the
+	submodules.
+
+EXAMPLES
+--------
+
+The following command switches to the "master" branch:
+
+------------
+$ git switch master
+------------
+
+After working in the wrong branch, switching to the correct branch
+would be done using:
+
+------------
+$ git switch mytopic
+------------
+
+However, your "wrong" branch and correct "mytopic" branch may differ
+in files that you have modified locally, in which case the above
+switch would fail like this:
+
+------------
+$ git switch mytopic
+error: You have local changes to 'frotz'; not switching branches.
+------------
+
+You can give the `-m` flag to the command, which would try a three-way
+merge:
+
+------------
+$ git switch -m mytopic
+Auto-merging frotz
+------------
+
+After this three-way merge, the local modifications are _not_
+registered in your index file, so `git diff` would show you what
+changes you made since the tip of the new branch.
+
+To switch back to the previous branch before we switched to mytopic
+(i.e. "master" branch):
+
+------------
+$ git switch -
+------------
+
+You can grow a new branch from any commit. For example, switch to
+"HEAD~3" and create branch "fixup":
+
+------------
+$ git switch -c fixup HEAD~3
+Switched to a new branch 'fixup'
+------------
+
+If you want to start a new branch from a remote branch of the same
+name:
+
+------------
+$ git switch new-topic
+Branch 'new-topic' set up to track remote branch 'new-topic' from 'origin'
+Switched to a new branch 'new-topic'
+------------
+
+To check out commit `HEAD~3` for temporary inspection or experiment
+without creating a new branch:
+
+------------
+$ git switch --detach HEAD~3
+HEAD is now at 9fc9555312 Merge branch 'cc/shared-index-permbits'
+------------
+
+If it turns out whatever you have done is worth keeping, you can
+always create a new name for it (without switching away):
+
+------------
+$ git switch -c good-surprises
+------------
+
+SEE ALSO
+--------
+linkgit:git-checkout[1],
+linkgit:git-branch[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index a74e7b9..2e5599a 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -64,6 +64,13 @@
 -s::
 --sign::
 	Make a GPG-signed tag, using the default e-mail address's key.
+	The default behavior of tag GPG-signing is controlled by `tag.gpgSign`
+	configuration variable if it exists, or disabled oder otherwise.
+	See linkgit:git-config[1].
+
+--no-sign::
+	Override `tag.gpgSign` configuration variable that is
+	set to force each and every tag to be signed.
 
 -u <keyid>::
 --local-user=<keyid>::
diff --git a/Documentation/git.txt b/Documentation/git.txt
index f9b09db..9b82564 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -57,7 +57,8 @@
 	Run as if git was started in '<path>' instead of the current working
 	directory.  When multiple `-C` options are given, each subsequent
 	non-absolute `-C <path>` is interpreted relative to the preceding `-C
-	<path>`.
+	<path>`.  If '<path>' is present but empty, e.g. `-C ""`, then the
+	current working directory is left unchanged.
 +
 This option affects options that expect path name like `--git-dir` and
 `--work-tree` in that their interpretations of the path names would be
@@ -211,6 +212,26 @@
 
 include::cmds-foreignscminterface.txt[]
 
+Reset, restore and revert
+~~~~~~~~~~~~~~~~~~~~~~~~~
+There are three commands with similar names: `git reset`,
+`git restore` and `git revert`.
+
+* linkgit:git-revert[1] is about making a new commit that reverts the
+  changes made by other commits.
+
+* linkgit:git-restore[1] is about restoring files in the working tree
+  from either the index or another commit. This command does not
+  update your branch. The command can also be used to restore files in
+  the index from another commit.
+
+* linkgit:git-reset[1] is about updating your branch, moving the tip
+  in order to add or remove commits from the branch. This operation
+  changes the commit history.
++
+`git reset` can also be used to restore the index, overlapping with
+`git restore`.
+
 
 Low-level commands (plumbing)
 -----------------------------
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 2796dfc..fb1d188 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -112,7 +112,8 @@
 
 These attributes affect how the contents stored in the
 repository are copied to the working tree files when commands
-such as 'git checkout' and 'git merge' run.  They also affect how
+such as 'git switch', 'git checkout'  and 'git merge' run.
+They also affect how
 Git stores the contents you prepare in the working tree in the
 repository upon 'git add' and 'git commit'.
 
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 592e06d..1ed3ca3 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -47,8 +47,8 @@
    things:
 +
 --------------------------------
-$ git checkout -- *.c
-$ git checkout -- \*.c
+$ git restore *.c
+$ git restore \*.c
 --------------------------------
 +
 The former lets your shell expand the fileglob, and you are asking
@@ -209,6 +209,18 @@
 http://marc.info/?l=git&m=119150393620273 for further
 information.
 
+Some other commands that also work on files in the working tree and/or
+in the index can take `--staged` and/or `--worktree`.
+
+* `--staged` is exactly like `--cached`, which is used to ask a
+  command to only work on the index, not the working tree.
+
+* `--worktree` is the opposite, to ask a command to work on the
+  working tree only, not the index.
+
+* The two options can be specified together to ask a command to work
+  on both the index and the working tree.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index e29a9ef..f880d21 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -741,7 +741,7 @@
 saying that you want to check out a new branch:
 
 ------------
-$ git checkout -b mybranch
+$ git switch -c mybranch
 ------------
 
 will create a new branch based at the current `HEAD` position, and switch
@@ -755,7 +755,7 @@
 In other words, if you have an earlier tag or branch, you'd just do
 
 ------------
-$ git checkout -b mybranch earlier-commit
+$ git switch -c mybranch earlier-commit
 ------------
 
 and it would create the new branch `mybranch` at the earlier commit,
@@ -765,7 +765,7 @@
 You can always just jump back to your original `master` branch by doing
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 (or any other branch-name, for that matter) and if you forget which
@@ -794,7 +794,7 @@
 
 which will simply _create_ the branch, but will not do anything further.
 You can then later -- once you decide that you want to actually develop
-on that branch -- switch to that branch with a regular 'git checkout'
+on that branch -- switch to that branch with a regular 'git switch'
 with the branchname as the argument.
 
 
@@ -808,7 +808,7 @@
 that branch, and do some work there.
 
 ------------------------------------------------
-$ git checkout mybranch
+$ git switch mybranch
 $ echo "Work, work, work" >>hello
 $ git commit -m "Some work." -i hello
 ------------------------------------------------
@@ -825,7 +825,7 @@
 to the master branch, and editing the same file differently there:
 
 ------------
-$ git checkout master
+$ git switch master
 ------------
 
 Here, take a moment to look at the contents of `hello`, and notice how they
@@ -958,7 +958,7 @@
 'git merge' to get the "upstream changes" back to your branch.
 
 ------------
-$ git checkout mybranch
+$ git switch mybranch
 $ git merge -m "Merge upstream changes." master
 ------------
 
@@ -1133,9 +1133,8 @@
 work." commit.
 
 ------------
-$ git checkout mybranch
-$ git reset --hard master^2
-$ git checkout master
+$ git switch -C mybranch master^2
+$ git switch master
 $ git reset --hard master^
 ------------
 
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index 9f2528f..1bd919f 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -41,7 +41,7 @@
 
   * linkgit:git-log[1] to see what happened.
 
-  * linkgit:git-checkout[1] and linkgit:git-branch[1] to switch
+  * linkgit:git-switch[1] and linkgit:git-branch[1] to switch
     branches.
 
   * linkgit:git-add[1] to manage the index file.
@@ -51,8 +51,7 @@
 
   * linkgit:git-commit[1] to advance the current branch.
 
-  * linkgit:git-reset[1] and linkgit:git-checkout[1] (with
-    pathname parameters) to undo changes.
+  * linkgit:git-restore[1] to undo changes.
 
   * linkgit:git-merge[1] to merge between local branches.
 
@@ -80,9 +79,9 @@
 Create a topic branch and develop.::
 +
 ------------
-$ git checkout -b alsa-audio <1>
+$ git switch -c alsa-audio <1>
 $ edit/compile/test
-$ git checkout -- curses/ux_audio_oss.c <2>
+$ git restore curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
 $ edit/compile/test
 $ git diff HEAD <4>
@@ -90,7 +89,7 @@
 $ edit/compile/test
 $ git diff HEAD^ <6>
 $ git commit -a --amend <7>
-$ git checkout master <8>
+$ git switch master <8>
 $ git merge alsa-audio <9>
 $ git log --since='3 days ago' <10>
 $ git log v2.43.. curses/ <11>
@@ -148,11 +147,11 @@
 ------------
 $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6
 $ cd my2.6
-$ git checkout -b mine master <1>
+$ git switch -c mine master <1>
 $ edit/compile/test; git commit -a -s <2>
 $ git format-patch master <3>
 $ git send-email --to="person <email@example.com>" 00*.patch <4>
-$ git checkout master <5>
+$ git switch master <5>
 $ git pull <6>
 $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>
 $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>
@@ -194,7 +193,7 @@
 satellite$ git push origin <4>
 
 mothership$ cd frotz
-mothership$ git checkout master
+mothership$ git switch master
 mothership$ git merge satellite/master <5>
 ------------
 +
@@ -216,7 +215,7 @@
 Branch off of a specific tag.::
 +
 ------------
-$ git checkout -b private2.6.14 v2.6.14 <1>
+$ git switch -c private2.6.14 v2.6.14 <1>
 $ edit/compile/test; git commit -a
 $ git checkout master
 $ git cherry-pick v2.6.14..private2.6.14 <2>
@@ -274,14 +273,14 @@
 & s 2 3 4 5 ./+to-apply
 & s 7 8 ./+hold-linus
 & q
-$ git checkout -b topic/one master
+$ git switch -c topic/one master
 $ git am -3 -i -s ./+to-apply <4>
 $ compile/test
-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>
-$ git checkout topic/one && git rebase master <6>
-$ git checkout pu && git reset --hard next <7>
+$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
+$ git switch topic/one && git rebase master <6>
+$ git switch -C pu next <7>
 $ git merge topic/one topic/two && git merge hold/linus <8>
-$ git checkout maint
+$ git switch maint
 $ git cherry-pick master~4 <9>
 $ compile/test
 $ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 786e778..82cd573 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -165,12 +165,13 @@
 post-checkout
 ~~~~~~~~~~~~~
 
-This hook is invoked when a linkgit:git-checkout[1] is run after having updated the
+This hook is invoked when a linkgit:git-checkout[1] or
+linkgit:git-switch[1] is run after having updated the
 worktree.  The hook is given three parameters: the ref of the previous HEAD,
 the ref of the new HEAD (which may or may not have changed), and a flag
 indicating whether the checkout was a branch checkout (changing branches,
 flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of `git checkout`.
+This hook cannot affect the outcome of `git switch` or `git checkout`.
 
 It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
 used. The first parameter given to the hook is the null-ref, the second the
@@ -406,7 +407,8 @@
 For example, the hook can simply run `git read-tree -u -m HEAD "$1"`
 in order to emulate `git fetch` that is run in the reverse direction
 with `git push`, as the two-tree form of `git read-tree -u -m` is
-essentially the same as `git checkout` that switches branches while
+essentially the same as `git switch` or `git checkout`
+that switches branches while
 keeping the local changes in the working tree that do not interfere
 with the difference between the branches.
 
diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt
index e0976f6..8bdb7d0 100644
--- a/Documentation/gittutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -370,13 +370,13 @@
 $ git status
 On branch master
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   closing.txt
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   file.txt
 
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 242de31..59ef5ce 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -110,7 +110,7 @@
 On branch master
 Changes to be committed:
 Your branch is up to date with 'origin/master'.
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   file1
 	modified:   file2
@@ -207,7 +207,7 @@
 type
 
 ------------------------------------------------
-$ git checkout experimental
+$ git switch experimental
 ------------------------------------------------
 
 to switch to the experimental branch.  Now edit a file, commit the
@@ -216,7 +216,7 @@
 ------------------------------------------------
 (edit file)
 $ git commit -a
-$ git checkout master
+$ git switch master
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
diff --git a/Documentation/gitweb.txt b/Documentation/gitweb.txt
index c743609..3cc9b03 100644
--- a/Documentation/gitweb.txt
+++ b/Documentation/gitweb.txt
@@ -28,8 +28,7 @@
   revisions one at a time, viewing the history of the repository.
 * Finding commits which commit messages matches given search term.
 
-See http://git.kernel.org/?p=git/git.git;a=tree;f=gitweb[] or
-http://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
+See http://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
 browsed using gitweb itself.
 
 
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
index ca11c7b..abc0dc6 100644
--- a/Documentation/gitworkflows.txt
+++ b/Documentation/gitworkflows.txt
@@ -301,8 +301,7 @@
 .Rewind and rebuild next
 [caption="Recipe: "]
 =====================================
-* `git checkout next`
-* `git reset --hard master`
+* `git switch -C next master`
 * `git merge ai/topic_in_next1`
 * `git merge ai/topic_in_next2`
 * ...
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 71a1fcc..286fc16 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -708,6 +708,16 @@
 	Only useful with `--objects`; print the object IDs that are not
 	in packs.
 
+--object-names::
+	Only useful with `--objects`; print the names of the object IDs
+	that are found. This is the default behavior.
+
+--no-object-names::
+	Only useful with `--objects`; does not print the names of the object
+	IDs that are found. This inverts `--object-names`. This flag allows
+	the output to be more easily parsed by commands such as
+	linkgit:git-cat-file[1].
+
 --filter=<filter-spec>::
 	Only useful with one of the `--objects*`; omits objects (usually
 	blobs) from the list of printed objects.  The '<filter-spec>'
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 82c1e57..97f995e 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -115,7 +115,7 @@
 ------------------------------
 $ git config push.default current
 $ git config remote.pushdefault myfork
-$ git checkout -b mybranch origin/master
+$ git switch -c mybranch origin/master
 
 $ git rev-parse --symbolic-full-name @{upstream}
 refs/remotes/origin/master
diff --git a/Documentation/technical/api-trace2.txt b/Documentation/technical/api-trace2.txt
index 23c3cc7..f7ffe7d 100644
--- a/Documentation/technical/api-trace2.txt
+++ b/Documentation/technical/api-trace2.txt
@@ -35,7 +35,7 @@
 === The Normal Format Target
 
 The normal format target is a tradition printf format and similar
-to GIT_TRACE format.  This format is enabled with the `GIT_TR`
+to GIT_TRACE format.  This format is enabled with the `GIT_TRACE2`
 environment variable or the `trace2.normalTarget` system or global
 config setting.
 
diff --git a/Documentation/technical/commit-graph.txt b/Documentation/technical/commit-graph.txt
index 7805b09..fb53341 100644
--- a/Documentation/technical/commit-graph.txt
+++ b/Documentation/technical/commit-graph.txt
@@ -127,23 +127,6 @@
   helpful for these clones, anyway. The commit-graph will not be read or
   written when shallow commits are present.
 
-Future Work
------------
-
-- After computing and storing generation numbers, we must make graph
-  walks aware of generation numbers to gain the performance benefits they
-  enable. This will mostly be accomplished by swapping a commit-date-ordered
-  priority queue with one ordered by generation number. The following
-  operations are important candidates:
-
-    - 'log --topo-order'
-    - 'tag --merged'
-
-- A server could provide a commit-graph file as part of the network protocol
-  to avoid extra calculations by clients. This feature is only of benefit if
-  the user is willing to trust the file, because verifying the file is correct
-  is as hard as computing it from scratch.
-
 Related Links
 -------------
 [0] https://bugs.chromium.org/p/git/issues/detail?id=8
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index eff7890..8bce75b 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -122,10 +122,10 @@
 while heads are expected to advance as development progresses.
 
 Create a new branch head pointing to one of these versions and check it
-out using linkgit:git-checkout[1]:
+out using linkgit:git-switch[1]:
 
 ------------------------------------------------
-$ git checkout -b new v2.6.13
+$ git switch -c new v2.6.13
 ------------------------------------------------
 
 The working directory then reflects the contents that the project had
@@ -282,10 +282,10 @@
 	this command will fail with a warning.
 `git branch -D <branch>`::
 	delete the branch `<branch>` irrespective of its merged status.
-`git checkout <branch>`::
+`git switch <branch>`::
 	make the current branch `<branch>`, updating the working
 	directory to reflect the version referenced by `<branch>`.
-`git checkout -b <new> <start-point>`::
+`git switch -c <new> <start-point>`::
 	create a new branch `<new>` referencing `<start-point>`, and
 	check it out.
 
@@ -302,22 +302,22 @@
 Examining an old version without creating a new branch
 ------------------------------------------------------
 
-The `git checkout` command normally expects a branch head, but will also
-accept an arbitrary commit; for example, you can check out the commit
-referenced by a tag:
+The `git switch` command normally expects a branch head, but will also
+accept an arbitrary commit when invoked with --detach; for example,
+you can check out the commit referenced by a tag:
 
 ------------------------------------------------
-$ git checkout v2.6.17
+$ git switch --detach v2.6.17
 Note: checking out 'v2.6.17'.
 
 You are in 'detached HEAD' state. You can look around, make experimental
 changes and commit them, and you can discard any commits you make in this
-state without impacting any branches by performing another checkout.
+state without impacting any branches by performing another switch.
 
 If you want to create a new branch to retain commits you create, you may
-do so (now or later) by using -b with the checkout command again. Example:
+do so (now or later) by using -c with the switch command again. Example:
 
-  git checkout -b new_branch_name
+  git switch -c new_branch_name
 
 HEAD is now at 427abfa Linux v2.6.17
 ------------------------------------------------
@@ -373,7 +373,7 @@
 on a branch of your own, just as you would for a tag:
 
 ------------------------------------------------
-$ git checkout -b my-todo-copy origin/todo
+$ git switch -c my-todo-copy origin/todo
 ------------------------------------------------
 
 You can also check out `origin/todo` directly to examine it or
@@ -1408,7 +1408,7 @@
 away, you can always return to the pre-merge state with
 
 -------------------------------------------------
-$ git reset --hard HEAD
+$ git merge --abort
 -------------------------------------------------
 
 Or, if you've already committed the merge that you want to throw away,
@@ -1446,7 +1446,7 @@
 state with
 
 -------------------------------------------------
-$ git reset --hard HEAD
+$ git restore --staged --worktree :/
 -------------------------------------------------
 
 If you make a commit that you later wish you hadn't, there are two
@@ -1523,12 +1523,10 @@
 
 In the process of undoing a previous bad change, you may find it
 useful to check out an older version of a particular file using
-linkgit:git-checkout[1].  We've used `git checkout` before to switch
-branches, but it has quite different behavior if it is given a path
-name: the command
+linkgit:git-restore[1]. The command
 
 -------------------------------------------------
-$ git checkout HEAD^ path/to/file
+$ git restore --source=HEAD^ path/to/file
 -------------------------------------------------
 
 replaces path/to/file by the contents it had in the commit HEAD^, and
@@ -2211,8 +2209,8 @@
 These can be easily kept up to date using linkgit:git-pull[1].
 
 -------------------------------------------------
-$ git checkout test && git pull
-$ git checkout release && git pull
+$ git switch test && git pull
+$ git switch release && git pull
 -------------------------------------------------
 
 Important note!  If you have any local changes in these branches, then
@@ -2264,7 +2262,7 @@
 2) help future bug hunters that use `git bisect` to find problems
 
 -------------------------------------------------
-$ git checkout -b speed-up-spinlocks v2.6.35
+$ git switch -c speed-up-spinlocks v2.6.35
 -------------------------------------------------
 
 Now you apply the patch(es), run some tests, and commit the change(s).  If
@@ -2279,7 +2277,7 @@
 "test" branch in preparation to make it public:
 
 -------------------------------------------------
-$ git checkout test && git merge speed-up-spinlocks
+$ git switch test && git merge speed-up-spinlocks
 -------------------------------------------------
 
 It is unlikely that you would have any conflicts here ... but you might if you
@@ -2291,7 +2289,7 @@
 means that the patches can be moved into the `release` tree in any order.
 
 -------------------------------------------------
-$ git checkout release && git merge speed-up-spinlocks
+$ git switch release && git merge speed-up-spinlocks
 -------------------------------------------------
 
 After a while, you will have a number of branches, and despite the
@@ -2512,7 +2510,7 @@
 `origin`, and create some commits on top of it:
 
 -------------------------------------------------
-$ git checkout -b mywork origin
+$ git switch -c mywork origin
 $ vi file.txt
 $ git commit
 $ vi otherfile.txt
@@ -2552,7 +2550,7 @@
 linkgit:git-rebase[1]:
 
 -------------------------------------------------
-$ git checkout mywork
+$ git switch mywork
 $ git rebase origin
 -------------------------------------------------
 
@@ -3668,13 +3666,13 @@
 new commit:
 
 -------------------------------------------------
-$ git checkout master
+$ git switch master
 -------------------------------------------------
 
 or
 
 -------------------------------------------------
-$ git checkout -b fix-up
+$ git switch -c fix-up
 -------------------------------------------------
 
 then
@@ -3800,8 +3798,8 @@
 The Workflow
 ------------
 
-High-level operations such as linkgit:git-commit[1],
-linkgit:git-checkout[1] and linkgit:git-reset[1] work by moving data
+High-level operations such as linkgit:git-commit[1] and
+linkgit:git-restore[1] work by moving data
 between the working tree, the index, and the object database.  Git
 provides low-level operations which perform each of these steps
 individually.
@@ -4194,7 +4192,7 @@
 A good place to start is with the contents of the initial commit, with:
 
 ----------------------------------------------------
-$ git checkout e83c5163
+$ git switch --detach e83c5163
 ----------------------------------------------------
 
 The initial revision lays the foundation for almost everything Git has
@@ -4437,10 +4435,10 @@
 -----------------
 
 -----------------------------------------------
-$ git branch	     # list all local branches in this repo
-$ git checkout test  # switch working directory to branch "test"
-$ git branch new     # create branch "new" starting at current HEAD
-$ git branch -d new  # delete branch "new"
+$ git branch			# list all local branches in this repo
+$ git switch test	        # switch working directory to branch "test"
+$ git branch new		# create branch "new" starting at current HEAD
+$ git branch -d new		# delete branch "new"
 -----------------------------------------------
 
 Instead of basing a new branch on current HEAD (the default), use:
@@ -4456,7 +4454,7 @@
 Create and switch to a new branch at the same time:
 
 -----------------------------------------------
-$ git checkout -b new v2.6.15
+$ git switch -c new v2.6.15
 -----------------------------------------------
 
 Update and examine branches from the repository you cloned from:
@@ -4467,7 +4465,7 @@
   origin/master
   origin/next
   ...
-$ git checkout -b masterwork origin/master
+$ git switch -c masterwork origin/master
 -----------------------------------------------
 
 Fetch a branch from a different repository, and give it a new
diff --git a/Makefile b/Makefile
index f58bf14..98a0588 100644
--- a/Makefile
+++ b/Makefile
@@ -771,9 +771,11 @@
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-init$X
 BUILT_INS += git-merge-subtree$X
+BUILT_INS += git-restore$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
+BUILT_INS += git-switch$X
 BUILT_INS += git-whatchanged$X
 
 # what 'all' will build and 'install' will install in gitexecdir,
@@ -1235,7 +1237,7 @@
 
 ifdef SANE_TOOL_PATH
 SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH))
-BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix $(SANE_TOOL_PATH_SQ)|'
+BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix "$(SANE_TOOL_PATH_SQ)"|'
 PATH := $(SANE_TOOL_PATH):${PATH}
 else
 BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
@@ -2858,6 +2860,33 @@
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
+ifdef MSVC
+	# We DO NOT install the individual foo.o.pdb files because they
+	# have already been rolled up into the exe's pdb file.
+	# We DO NOT have pdb files for the builtin commands (like git-status.exe)
+	# because it is just a copy/hardlink of git.exe, rather than a unique binary.
+	$(INSTALL) git.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) git-shell.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) git-upload-pack.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) git-credential-store.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-daemon.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-fast-import.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-http-backend.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-http-fetch.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-http-push.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-imap-send.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-remote-http.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-remote-testsvn.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-sh-i18n--envsubst.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+	$(INSTALL) git-show-index.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+ifndef DEBUG
+	$(INSTALL) $(vcpkg_rel_bin)/*.dll '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) $(vcpkg_rel_bin)/*.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
+else
+	$(INSTALL) $(vcpkg_dbg_bin)/*.dll '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) $(vcpkg_dbg_bin)/*.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
+endif
+endif
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mergetools_instdir_SQ)'
 	$(INSTALL) -m 644 mergetools/* '$(DESTDIR_SQ)$(mergetools_instdir_SQ)'
@@ -3070,6 +3099,19 @@
 	$(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS
 	$(RM) GIT-USER-AGENT GIT-PREFIX
 	$(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS
+ifdef MSVC
+	$(RM) $(patsubst %.o,%.o.pdb,$(OBJECTS))
+	$(RM) $(patsubst %.exe,%.pdb,$(OTHER_PROGRAMS))
+	$(RM) $(patsubst %.exe,%.iobj,$(OTHER_PROGRAMS))
+	$(RM) $(patsubst %.exe,%.ipdb,$(OTHER_PROGRAMS))
+	$(RM) $(patsubst %.exe,%.pdb,$(PROGRAMS))
+	$(RM) $(patsubst %.exe,%.iobj,$(PROGRAMS))
+	$(RM) $(patsubst %.exe,%.ipdb,$(PROGRAMS))
+	$(RM) $(patsubst %.exe,%.pdb,$(TEST_PROGRAMS))
+	$(RM) $(patsubst %.exe,%.iobj,$(TEST_PROGRAMS))
+	$(RM) $(patsubst %.exe,%.ipdb,$(TEST_PROGRAMS))
+	$(RM) compat/vcbuild/MSVC-DEFS-GEN
+endif
 
 .PHONY: all install profile-clean cocciclean clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
diff --git a/advice.c b/advice.c
index ce5f374..67de6de 100644
--- a/advice.c
+++ b/advice.c
@@ -3,6 +3,7 @@
 #include "color.h"
 #include "help.h"
 
+int advice_fetch_show_forced_updates = 1;
 int advice_push_update_rejected = 1;
 int advice_push_non_ff_current = 1;
 int advice_push_non_ff_matching = 1;
@@ -12,6 +13,7 @@
 int advice_push_unqualified_ref_name = 1;
 int advice_status_hints = 1;
 int advice_status_u_option = 1;
+int advice_status_ahead_behind_warning = 1;
 int advice_commit_before_merge = 1;
 int advice_reset_quiet_warning = 1;
 int advice_resolve_conflict = 1;
@@ -59,6 +61,7 @@
 	const char *name;
 	int *preference;
 } advice_config[] = {
+	{ "fetchShowForcedUpdates", &advice_fetch_show_forced_updates },
 	{ "pushUpdateRejected", &advice_push_update_rejected },
 	{ "pushNonFFCurrent", &advice_push_non_ff_current },
 	{ "pushNonFFMatching", &advice_push_non_ff_matching },
@@ -68,6 +71,7 @@
 	{ "pushUnqualifiedRefName", &advice_push_unqualified_ref_name },
 	{ "statusHints", &advice_status_hints },
 	{ "statusUoption", &advice_status_u_option },
+	{ "statusAheadBehindWarning", &advice_status_ahead_behind_warning },
 	{ "commitBeforeMerge", &advice_commit_before_merge },
 	{ "resetQuiet", &advice_reset_quiet_warning },
 	{ "resolveConflict", &advice_resolve_conflict },
@@ -193,13 +197,22 @@
 void detach_advice(const char *new_name)
 {
 	const char *fmt =
-	_("Note: checking out '%s'.\n\n"
+	_("Note: switching to '%s'.\n"
+	"\n"
 	"You are in 'detached HEAD' state. You can look around, make experimental\n"
 	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
+	"state without impacting any branches by switching back to a branch.\n"
+	"\n"
 	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b <new-branch-name>\n\n");
+	"do so (now or later) by using -c with the switch command. Example:\n"
+	"\n"
+	"  git switch -c <new-branch-name>\n"
+	"\n"
+	"Or undo this operation with:\n"
+	"\n"
+	"  git switch -\n"
+	"\n"
+	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
 
 	fprintf(stderr, fmt, new_name);
 }
diff --git a/advice.h b/advice.h
index e50f02c..940c4c2 100644
--- a/advice.h
+++ b/advice.h
@@ -3,6 +3,7 @@
 
 #include "git-compat-util.h"
 
+extern int advice_fetch_show_forced_updates;
 extern int advice_push_update_rejected;
 extern int advice_push_non_ff_current;
 extern int advice_push_non_ff_matching;
@@ -12,6 +13,7 @@
 extern int advice_push_unqualified_ref_name;
 extern int advice_status_hints;
 extern int advice_status_u_option;
+extern int advice_status_ahead_behind_warning;
 extern int advice_commit_before_merge;
 extern int advice_reset_quiet_warning;
 extern int advice_resolve_conflict;
diff --git a/blob.c b/blob.c
index 342bdbb..36f9abd 100644
--- a/blob.c
+++ b/blob.c
@@ -7,10 +7,9 @@
 
 struct blob *lookup_blob(struct repository *r, const struct object_id *oid)
 {
-	struct object *obj = lookup_object(r, oid->hash);
+	struct object *obj = lookup_object(r, oid);
 	if (!obj)
-		return create_object(r, oid->hash,
-				     alloc_blob_node(r));
+		return create_object(r, oid, alloc_blob_node(r));
 	return object_as_type(r, obj, OBJ_BLOB, 0);
 }
 
diff --git a/branch.c b/branch.c
index e70838f..5794947 100644
--- a/branch.c
+++ b/branch.c
@@ -346,9 +346,9 @@
 	unlink(git_path_merge_mode(r));
 }
 
-void remove_branch_state(struct repository *r)
+void remove_branch_state(struct repository *r, int verbose)
 {
-	sequencer_post_commit_cleanup(r);
+	sequencer_post_commit_cleanup(r, verbose);
 	unlink(git_path_squash_msg(r));
 	remove_merge_branch_state(r);
 }
diff --git a/branch.h b/branch.h
index 064ee57..df0be61 100644
--- a/branch.h
+++ b/branch.h
@@ -70,7 +70,7 @@
  * Remove information about the state of working on the current
  * branch. (E.g., MERGE_HEAD)
  */
-void remove_branch_state(struct repository *r);
+void remove_branch_state(struct repository *r, int verbose);
 
 /*
  * Configure local branch "local" as downstream to branch "remote"
diff --git a/builtin.h b/builtin.h
index ec7e095..3d449a0 100644
--- a/builtin.h
+++ b/builtin.h
@@ -214,6 +214,7 @@
 int cmd_repack(int argc, const char **argv, const char *prefix);
 int cmd_rerere(int argc, const char **argv, const char *prefix);
 int cmd_reset(int argc, const char **argv, const char *prefix);
+int cmd_restore(int argc, const char **argv, const char *prefix);
 int cmd_rev_list(int argc, const char **argv, const char *prefix);
 int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 int cmd_revert(int argc, const char **argv, const char *prefix);
@@ -227,6 +228,7 @@
 int cmd_stash(int argc, const char **argv, const char *prefix);
 int cmd_stripspace(int argc, const char **argv, const char *prefix);
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+int cmd_switch(int argc, const char **argv, const char *prefix);
 int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 int cmd_tag(int argc, const char **argv, const char *prefix);
 int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/builtin/am.c b/builtin/am.c
index 78389d0..1aea657 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1801,7 +1801,7 @@
 	 */
 	if (!state->rebasing) {
 		am_destroy(state);
-		close_all_packs(the_repository->objects);
+		close_object_store(the_repository->objects);
 		run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
 	}
 }
@@ -1956,7 +1956,7 @@
 	if (merge_tree(remote_tree))
 		return -1;
 
-	remove_branch_state(the_repository);
+	remove_branch_state(the_repository, 0);
 
 	return 0;
 }
diff --git a/builtin/blame.c b/builtin/blame.c
index 21cde57..50e3d4a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -59,8 +59,8 @@
 
 static struct string_list mailmap = STRING_LIST_INIT_NODUP;
 
-#ifndef DEBUG
-#define DEBUG 0
+#ifndef DEBUG_BLAME
+#define DEBUG_BLAME 0
 #endif
 
 static unsigned blame_move_score;
@@ -1062,7 +1062,7 @@
 	if (blame_copy_score)
 		sb.copy_score = blame_copy_score;
 
-	sb.debug = DEBUG;
+	sb.debug = DEBUG_BLAME;
 	sb.on_sanity_fail = &sanity_check_on_fail;
 
 	sb.show_root = show_root;
diff --git a/builtin/branch.c b/builtin/branch.c
index d4359b3..2ef2146 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -47,6 +47,7 @@
 	GIT_COLOR_NORMAL,       /* LOCAL */
 	GIT_COLOR_GREEN,        /* CURRENT */
 	GIT_COLOR_BLUE,         /* UPSTREAM */
+	GIT_COLOR_CYAN,         /* WORKTREE */
 };
 enum color_branch {
 	BRANCH_COLOR_RESET = 0,
@@ -54,7 +55,8 @@
 	BRANCH_COLOR_REMOTE = 2,
 	BRANCH_COLOR_LOCAL = 3,
 	BRANCH_COLOR_CURRENT = 4,
-	BRANCH_COLOR_UPSTREAM = 5
+	BRANCH_COLOR_UPSTREAM = 5,
+	BRANCH_COLOR_WORKTREE = 6
 };
 
 static const char *color_branch_slots[] = {
@@ -64,6 +66,7 @@
 	[BRANCH_COLOR_LOCAL]	= "local",
 	[BRANCH_COLOR_CURRENT]	= "current",
 	[BRANCH_COLOR_UPSTREAM] = "upstream",
+	[BRANCH_COLOR_WORKTREE] = "worktree",
 };
 
 static struct string_list output = STRING_LIST_INIT_DUP;
@@ -342,9 +345,10 @@
 	struct strbuf local = STRBUF_INIT;
 	struct strbuf remote = STRBUF_INIT;
 
-	strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)  %s%%(end)",
-		    branch_get_color(BRANCH_COLOR_CURRENT),
-		    branch_get_color(BRANCH_COLOR_LOCAL));
+	strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else)  %s%%(end)%%(end)",
+			branch_get_color(BRANCH_COLOR_CURRENT),
+			branch_get_color(BRANCH_COLOR_WORKTREE),
+			branch_get_color(BRANCH_COLOR_LOCAL));
 	strbuf_addf(&remote, "  %s",
 		    branch_get_color(BRANCH_COLOR_REMOTE));
 
@@ -363,9 +367,13 @@
 		strbuf_addf(&local, " %s ", obname.buf);
 
 		if (filter->verbose > 1)
+		{
+			strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)",
+				    branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET));
 			strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
 				    "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
 				    branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
+		}
 		else
 			strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
 
@@ -830,7 +838,8 @@
 		strbuf_release(&buf);
 	} else if (argc > 0 && argc <= 2) {
 		if (filter.kind != FILTER_REFS_BRANCHES)
-			die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
+			die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
+				  "Did you mean to use: -a|-r --list <pattern>?"));
 
 		if (track == BRANCH_TRACK_OVERRIDE)
 			die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
diff --git a/builtin/checkout.c b/builtin/checkout.c
index ffa776c..91f8509 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,32 +1,31 @@
 #define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "builtin.h"
-#include "config.h"
+#include "advice.h"
+#include "blob.h"
+#include "branch.h"
+#include "cache-tree.h"
 #include "checkout.h"
+#include "commit.h"
+#include "config.h"
+#include "diff.h"
+#include "dir.h"
+#include "ll-merge.h"
 #include "lockfile.h"
+#include "merge-recursive.h"
+#include "object-store.h"
 #include "parse-options.h"
 #include "refs.h"
-#include "object-store.h"
-#include "commit.h"
+#include "remote.h"
+#include "resolve-undo.h"
+#include "revision.h"
+#include "run-command.h"
+#include "submodule.h"
+#include "submodule-config.h"
 #include "tree.h"
 #include "tree-walk.h"
-#include "cache-tree.h"
 #include "unpack-trees.h"
-#include "dir.h"
-#include "run-command.h"
-#include "merge-recursive.h"
-#include "branch.h"
-#include "diff.h"
-#include "revision.h"
-#include "remote.h"
-#include "blob.h"
+#include "wt-status.h"
 #include "xdiff-interface.h"
-#include "ll-merge.h"
-#include "resolve-undo.h"
-#include "submodule-config.h"
-#include "submodule.h"
-#include "advice.h"
-
-static int checkout_optimize_new_branch;
 
 static const char * const checkout_usage[] = {
 	N_("git checkout [<options>] <branch>"),
@@ -34,12 +33,23 @@
 	NULL,
 };
 
+static const char * const switch_branch_usage[] = {
+	N_("git switch [<options>] [<branch>]"),
+	NULL,
+};
+
+static const char * const restore_usage[] = {
+	N_("git restore [<options>] [--source=<branch>] <file>..."),
+	NULL,
+};
+
 struct checkout_opts {
 	int patch_mode;
 	int quiet;
 	int merge;
 	int force;
 	int force_detach;
+	int implicit_detach;
 	int writeout_stage;
 	int overwrite_ignore;
 	int ignore_skipworktree;
@@ -47,10 +57,19 @@
 	int show_progress;
 	int count_checkout_paths;
 	int overlay_mode;
-	/*
-	 * If new checkout options are added, skip_merge_working_tree
-	 * should be updated accordingly.
-	 */
+	int dwim_new_local_branch;
+	int discard_changes;
+	int accept_ref;
+	int accept_pathspec;
+	int switch_branch_doing_nothing_is_ok;
+	int only_merge_on_switching_branches;
+	int can_switch_when_in_progress;
+	int orphan_from_empty_tree;
+	int empty_pathspec_ok;
+	int checkout_index;
+	int checkout_worktree;
+	const char *ignore_unmerged_opt;
+	int ignore_unmerged;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -58,10 +77,12 @@
 	int new_branch_log;
 	enum branch_track track;
 	struct diff_options diff_options;
+	char *conflict_style;
 
 	int branch_exists;
 	const char *prefix;
 	struct pathspec pathspec;
+	const char *from_treeish;
 	struct tree *source_tree;
 };
 
@@ -313,102 +334,13 @@
 	}
 }
 
-static int checkout_paths(const struct checkout_opts *opts,
-			  const char *revision)
+static int checkout_worktree(const struct checkout_opts *opts)
 {
-	int pos;
 	struct checkout state = CHECKOUT_INIT;
-	static char *ps_matched;
-	struct object_id rev;
-	struct commit *head;
-	int errs = 0;
-	struct lock_file lock_file = LOCK_INIT;
 	int nr_checkouts = 0, nr_unmerged = 0;
+	int errs = 0;
+	int pos;
 
-	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
-
-	if (opts->track != BRANCH_TRACK_UNSPECIFIED)
-		die(_("'%s' cannot be used with updating paths"), "--track");
-
-	if (opts->new_branch_log)
-		die(_("'%s' cannot be used with updating paths"), "-l");
-
-	if (opts->force && opts->patch_mode)
-		die(_("'%s' cannot be used with updating paths"), "-f");
-
-	if (opts->force_detach)
-		die(_("'%s' cannot be used with updating paths"), "--detach");
-
-	if (opts->merge && opts->patch_mode)
-		die(_("'%s' cannot be used with %s"), "--merge", "--patch");
-
-	if (opts->force && opts->merge)
-		die(_("'%s' cannot be used with %s"), "-f", "-m");
-
-	if (opts->new_branch)
-		die(_("Cannot update paths and switch to branch '%s' at the same time."),
-		    opts->new_branch);
-
-	if (opts->patch_mode)
-		return run_add_interactive(revision, "--patch=checkout",
-					   &opts->pathspec);
-
-	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
-	if (read_cache_preload(&opts->pathspec) < 0)
-		return error(_("index file corrupt"));
-
-	if (opts->source_tree)
-		read_tree_some(opts->source_tree, &opts->pathspec);
-
-	ps_matched = xcalloc(opts->pathspec.nr, 1);
-
-	/*
-	 * Make sure all pathspecs participated in locating the paths
-	 * to be checked out.
-	 */
-	for (pos = 0; pos < active_nr; pos++)
-		if (opts->overlay_mode)
-			mark_ce_for_checkout_overlay(active_cache[pos],
-						     ps_matched,
-						     opts);
-		else
-			mark_ce_for_checkout_no_overlay(active_cache[pos],
-							ps_matched,
-							opts);
-
-	if (report_path_error(ps_matched, &opts->pathspec)) {
-		free(ps_matched);
-		return 1;
-	}
-	free(ps_matched);
-
-	/* "checkout -m path" to recreate conflicted state */
-	if (opts->merge)
-		unmerge_marked_index(&the_index);
-
-	/* Any unmerged paths? */
-	for (pos = 0; pos < active_nr; pos++) {
-		const struct cache_entry *ce = active_cache[pos];
-		if (ce->ce_flags & CE_MATCHED) {
-			if (!ce_stage(ce))
-				continue;
-			if (opts->force) {
-				warning(_("path '%s' is unmerged"), ce->name);
-			} else if (opts->writeout_stage) {
-				errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
-			} else if (opts->merge) {
-				errs |= check_stages((1<<2) | (1<<3), ce, pos);
-			} else {
-				errs = 1;
-				error(_("path '%s' is unmerged"), ce->name);
-			}
-			pos = skip_same_name(ce, pos) - 1;
-		}
-	}
-	if (errs)
-		return 1;
-
-	/* Now we are committed to check them out */
 	state.force = 1;
 	state.refresh_cache = 1;
 	state.istate = &the_index;
@@ -457,8 +389,160 @@
 				   nr_checkouts);
 	}
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
-		die(_("unable to write new index file"));
+	return errs;
+}
+
+static int checkout_paths(const struct checkout_opts *opts,
+			  const char *revision)
+{
+	int pos;
+	static char *ps_matched;
+	struct object_id rev;
+	struct commit *head;
+	int errs = 0;
+	struct lock_file lock_file = LOCK_INIT;
+	int checkout_index;
+
+	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
+
+	if (opts->track != BRANCH_TRACK_UNSPECIFIED)
+		die(_("'%s' cannot be used with updating paths"), "--track");
+
+	if (opts->new_branch_log)
+		die(_("'%s' cannot be used with updating paths"), "-l");
+
+	if (opts->ignore_unmerged && opts->patch_mode)
+		die(_("'%s' cannot be used with updating paths"),
+		    opts->ignore_unmerged_opt);
+
+	if (opts->force_detach)
+		die(_("'%s' cannot be used with updating paths"), "--detach");
+
+	if (opts->merge && opts->patch_mode)
+		die(_("'%s' cannot be used with %s"), "--merge", "--patch");
+
+	if (opts->ignore_unmerged && opts->merge)
+		die(_("'%s' cannot be used with %s"),
+		    opts->ignore_unmerged_opt, "-m");
+
+	if (opts->new_branch)
+		die(_("Cannot update paths and switch to branch '%s' at the same time."),
+		    opts->new_branch);
+
+	if (!opts->checkout_worktree && !opts->checkout_index)
+		die(_("neither '%s' or '%s' is specified"),
+		    "--staged", "--worktree");
+
+	if (!opts->checkout_worktree && !opts->from_treeish)
+		die(_("'%s' must be used when '%s' is not specified"),
+		    "--worktree", "--source");
+
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->writeout_stage)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--ours", "--theirs", "--staged");
+
+	if (opts->checkout_index && !opts->checkout_worktree &&
+	    opts->merge)
+		die(_("'%s' or '%s' cannot be used with %s"),
+		    "--merge", "--conflict", "--staged");
+
+	if (opts->patch_mode) {
+		const char *patch_mode;
+
+		if (opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=checkout";
+		else if (opts->checkout_index && !opts->checkout_worktree)
+			patch_mode = "--patch=reset";
+		else if (!opts->checkout_index && opts->checkout_worktree)
+			patch_mode = "--patch=worktree";
+		else
+			BUG("either flag must have been set, worktree=%d, index=%d",
+			    opts->checkout_worktree, opts->checkout_index);
+		return run_add_interactive(revision, patch_mode, &opts->pathspec);
+	}
+
+	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
+	if (read_cache_preload(&opts->pathspec) < 0)
+		return error(_("index file corrupt"));
+
+	if (opts->source_tree)
+		read_tree_some(opts->source_tree, &opts->pathspec);
+
+	ps_matched = xcalloc(opts->pathspec.nr, 1);
+
+	/*
+	 * Make sure all pathspecs participated in locating the paths
+	 * to be checked out.
+	 */
+	for (pos = 0; pos < active_nr; pos++)
+		if (opts->overlay_mode)
+			mark_ce_for_checkout_overlay(active_cache[pos],
+						     ps_matched,
+						     opts);
+		else
+			mark_ce_for_checkout_no_overlay(active_cache[pos],
+							ps_matched,
+							opts);
+
+	if (report_path_error(ps_matched, &opts->pathspec)) {
+		free(ps_matched);
+		return 1;
+	}
+	free(ps_matched);
+
+	/* "checkout -m path" to recreate conflicted state */
+	if (opts->merge)
+		unmerge_marked_index(&the_index);
+
+	/* Any unmerged paths? */
+	for (pos = 0; pos < active_nr; pos++) {
+		const struct cache_entry *ce = active_cache[pos];
+		if (ce->ce_flags & CE_MATCHED) {
+			if (!ce_stage(ce))
+				continue;
+			if (opts->ignore_unmerged) {
+				if (!opts->quiet)
+					warning(_("path '%s' is unmerged"), ce->name);
+			} else if (opts->writeout_stage) {
+				errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
+			} else if (opts->merge) {
+				errs |= check_stages((1<<2) | (1<<3), ce, pos);
+			} else {
+				errs = 1;
+				error(_("path '%s' is unmerged"), ce->name);
+			}
+			pos = skip_same_name(ce, pos) - 1;
+		}
+	}
+	if (errs)
+		return 1;
+
+	/* Now we are committed to check them out */
+	if (opts->checkout_worktree)
+		errs |= checkout_worktree(opts);
+
+	/*
+	 * Allow updating the index when checking out from the index.
+	 * This is to save new stat info.
+	 */
+	if (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)
+		checkout_index = 1;
+	else
+		checkout_index = opts->checkout_index;
+
+	if (checkout_index) {
+		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+			die(_("unable to write new index file"));
+	} else {
+		/*
+		 * NEEDSWORK: if --worktree is not specified, we
+		 * should save stat info of checked out files in the
+		 * index to avoid the next (potentially costly)
+		 * refresh. But it's a bit tricker to do...
+		 */
+		rollback_lock_file(&lock_file);
+	}
 
 	read_ref_full("HEAD", 0, &rev, NULL);
 	head = lookup_commit_reference_gently(the_repository, &rev, 1);
@@ -553,112 +637,6 @@
 	branch->path = strbuf_detach(&buf, NULL);
 }
 
-/*
- * Skip merging the trees, updating the index and working directory if and
- * only if we are creating a new branch via "git checkout -b <new_branch>."
- */
-static int skip_merge_working_tree(const struct checkout_opts *opts,
-	const struct branch_info *old_branch_info,
-	const struct branch_info *new_branch_info)
-{
-	/*
-	 * Do the merge if sparse checkout is on and the user has not opted in
-	 * to the optimized behavior
-	 */
-	if (core_apply_sparse_checkout && !checkout_optimize_new_branch)
-		return 0;
-
-	/*
-	 * We must do the merge if we are actually moving to a new commit.
-	 */
-	if (!old_branch_info->commit || !new_branch_info->commit ||
-		!oideq(&old_branch_info->commit->object.oid,
-		       &new_branch_info->commit->object.oid))
-		return 0;
-
-	/*
-	 * opts->patch_mode cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * opts->quiet only impacts output so doesn't require a merge
-	 */
-
-	/*
-	 * Honor the explicit request for a three-way merge or to throw away
-	 * local changes
-	 */
-	if (opts->merge || opts->force)
-		return 0;
-
-	/*
-	 * --detach is documented as "updating the index and the files in the
-	 * working tree" but this optimization skips those steps so fall through
-	 * to the regular code path.
-	 */
-	if (opts->force_detach)
-		return 0;
-
-	/*
-	 * opts->writeout_stage cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * Honor the explicit ignore requests
-	 */
-	if (!opts->overwrite_ignore || opts->ignore_skipworktree ||
-		opts->ignore_other_worktrees)
-		return 0;
-
-	/*
-	 * opts->show_progress only impacts output so doesn't require a merge
-	 */
-
-	/*
-	 * opts->overlay_mode cannot be used with switching branches so is
-	 * not tested here
-	 */
-
-	/*
-	 * If we aren't creating a new branch any changes or updates will
-	 * happen in the existing branch.  Since that could only be updating
-	 * the index and working directory, we don't want to skip those steps
-	 * or we've defeated any purpose in running the command.
-	 */
-	if (!opts->new_branch)
-		return 0;
-
-	/*
-	 * new_branch_force is defined to "create/reset and checkout a branch"
-	 * so needs to go through the merge to do the reset
-	 */
-	if (opts->new_branch_force)
-		return 0;
-
-	/*
-	 * A new orphaned branch requrires the index and the working tree to be
-	 * adjusted to <start_point>
-	 */
-	if (opts->new_orphan_branch)
-		return 0;
-
-	/*
-	 * Remaining variables are not checkout options but used to track state
-	 */
-
-	 /*
-	  * Do the merge if this is the initial checkout. We cannot use
-	  * is_cache_unborn() here because the index hasn't been loaded yet
-	  * so cache_nr and timestamp.sec are always zero.
-	  */
-	if (!file_exists(get_index_file()))
-		return 0;
-
-	return 1;
-}
-
 static int merge_working_tree(const struct checkout_opts *opts,
 			      struct branch_info *old_branch_info,
 			      struct branch_info *new_branch_info,
@@ -666,15 +644,21 @@
 {
 	int ret;
 	struct lock_file lock_file = LOCK_INIT;
+	struct tree *new_tree;
 
 	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 	if (read_cache_preload(NULL) < 0)
 		return error(_("index file corrupt"));
 
 	resolve_undo_clear();
-	if (opts->force) {
-		ret = reset_tree(get_commit_tree(new_branch_info->commit),
-				 opts, 1, writeout_error);
+	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+		if (new_branch_info->commit)
+			BUG("'switch --orphan' should never accept a commit as starting point");
+		new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
+	} else
+		new_tree = get_commit_tree(new_branch_info->commit);
+	if (opts->discard_changes) {
+		ret = reset_tree(new_tree, opts, 1, writeout_error);
 		if (ret)
 			return ret;
 	} else {
@@ -712,7 +696,8 @@
 					   &old_branch_info->commit->object.oid :
 					   the_hash_algo->empty_tree);
 		init_tree_desc(&trees[0], tree->buffer, tree->size);
-		tree = parse_tree_indirect(&new_branch_info->commit->object.oid);
+		parse_tree(new_tree);
+		tree = new_tree;
 		init_tree_desc(&trees[1], tree->buffer, tree->size);
 
 		ret = unpack_trees(2, trees, &topts);
@@ -777,7 +762,7 @@
 			o.verbosity = 0;
 			work = write_tree_from_memory(&o);
 
-			ret = reset_tree(get_commit_tree(new_branch_info->commit),
+			ret = reset_tree(new_tree,
 					 opts, 1,
 					 writeout_error);
 			if (ret)
@@ -786,13 +771,13 @@
 			o.branch1 = new_branch_info->name;
 			o.branch2 = "local";
 			ret = merge_trees(&o,
-					  get_commit_tree(new_branch_info->commit),
+					  new_tree,
 					  work,
 					  old_tree,
 					  &result);
 			if (ret < 0)
 				exit(128);
-			ret = reset_tree(get_commit_tree(new_branch_info->commit),
+			ret = reset_tree(new_tree,
 					 opts, 0,
 					 writeout_error);
 			strbuf_release(&o.obuf);
@@ -810,7 +795,7 @@
 	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
-	if (!opts->force && !opts->quiet)
+	if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
 		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
 
 	return 0;
@@ -915,7 +900,7 @@
 				delete_reflog(old_branch_info->path);
 		}
 	}
-	remove_branch_state(the_repository);
+	remove_branch_state(the_repository, !opts->quiet);
 	strbuf_release(&msg);
 	if (!opts->quiet &&
 	    (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
@@ -1011,7 +996,10 @@
 	add_pending_object(&revs, object, oid_to_hex(&object->oid));
 
 	for_each_ref(add_pending_uninteresting_ref, &revs);
-	add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING);
+	if (new_commit)
+		add_pending_oid(&revs, "HEAD",
+				&new_commit->object.oid,
+				UNINTERESTING);
 
 	if (prepare_revision_walk(&revs))
 		die(_("internal error in revision walk"));
@@ -1032,6 +1020,7 @@
 	void *path_to_free;
 	struct object_id rev;
 	int flag, writeout_error = 0;
+	int do_merge = 1;
 
 	trace2_cmd_mode("branch");
 
@@ -1045,22 +1034,26 @@
 	if (old_branch_info.path)
 		skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
 
+	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+		if (new_branch_info->name)
+			BUG("'switch --orphan' should never accept a commit as starting point");
+		new_branch_info->commit = NULL;
+		new_branch_info->name = "(empty)";
+		do_merge = 1;
+	}
+
 	if (!new_branch_info->name) {
 		new_branch_info->name = "HEAD";
 		new_branch_info->commit = old_branch_info.commit;
 		if (!new_branch_info->commit)
 			die(_("You are on a branch yet to be born"));
 		parse_commit_or_die(new_branch_info->commit);
+
+		if (opts->only_merge_on_switching_branches)
+			do_merge = 0;
 	}
 
-	/* optimize the "checkout -b <new_branch> path */
-	if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) {
-		if (!checkout_optimize_new_branch && !opts->quiet) {
-			if (read_cache_preload(NULL) < 0)
-				return error(_("index file corrupt"));
-			show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
-		}
-	} else {
+	if (do_merge) {
 		ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
 		if (ret) {
 			free(path_to_free);
@@ -1080,11 +1073,6 @@
 
 static int git_checkout_config(const char *var, const char *value, void *cb)
 {
-	if (!strcmp(var, "checkout.optimizenewbranch")) {
-		checkout_optimize_new_branch = git_config_bool(var, value);
-		return 0;
-	}
-
 	if (!strcmp(var, "diff.ignoresubmodules")) {
 		struct checkout_opts *opts = cb;
 		handle_ignore_submodules_arg(&opts->diff_options, value);
@@ -1097,6 +1085,34 @@
 	return git_xmerge_config(var, value, NULL);
 }
 
+static void setup_new_branch_info_and_source_tree(
+	struct branch_info *new_branch_info,
+	struct checkout_opts *opts,
+	struct object_id *rev,
+	const char *arg)
+{
+	struct tree **source_tree = &opts->source_tree;
+	struct object_id branch_rev;
+
+	new_branch_info->name = arg;
+	setup_branch_path(new_branch_info);
+
+	if (!check_refname_format(new_branch_info->path, 0) &&
+	    !read_ref(new_branch_info->path, &branch_rev))
+		oidcpy(rev, &branch_rev);
+	else
+		new_branch_info->path = NULL; /* not an existing branch */
+
+	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
+	if (!new_branch_info->commit) {
+		/* not a commit */
+		*source_tree = parse_tree_indirect(rev);
+	} else {
+		parse_commit_or_die(new_branch_info->commit);
+		*source_tree = get_commit_tree(new_branch_info->commit);
+	}
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
 				int dwim_new_local_branch_ok,
 				struct branch_info *new_branch_info,
@@ -1104,10 +1120,8 @@
 				struct object_id *rev,
 				int *dwim_remotes_matched)
 {
-	struct tree **source_tree = &opts->source_tree;
 	const char **new_branch = &opts->new_branch;
 	int argcount = 0;
-	struct object_id branch_rev;
 	const char *arg;
 	int dash_dash_pos;
 	int has_dash_dash = 0;
@@ -1157,10 +1171,16 @@
 	if (!argc)
 		return 0;
 
+	if (!opts->accept_pathspec) {
+		if (argc > 1)
+			die(_("only one reference expected"));
+		has_dash_dash = 1; /* helps disambiguate */
+	}
+
 	arg = argv[0];
 	dash_dash_pos = -1;
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(argv[i], "--")) {
+		if (opts->accept_pathspec && !strcmp(argv[i], "--")) {
 			dash_dash_pos = i;
 			break;
 		}
@@ -1194,11 +1214,12 @@
 			recover_with_dwim = 0;
 
 		/*
-		 * Accept "git checkout foo" and "git checkout foo --"
-		 * as candidates for dwim.
+		 * Accept "git checkout foo", "git checkout foo --"
+		 * and "git switch foo" as candidates for dwim.
 		 */
 		if (!(argc == 1 && !has_dash_dash) &&
-		    !(argc == 2 && has_dash_dash))
+		    !(argc == 2 && has_dash_dash) &&
+		    opts->accept_pathspec)
 			recover_with_dwim = 0;
 
 		if (recover_with_dwim) {
@@ -1229,26 +1250,11 @@
 	argv++;
 	argc--;
 
-	new_branch_info->name = arg;
-	setup_branch_path(new_branch_info);
+	setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
 
-	if (!check_refname_format(new_branch_info->path, 0) &&
-	    !read_ref(new_branch_info->path, &branch_rev))
-		oidcpy(rev, &branch_rev);
-	else
-		new_branch_info->path = NULL; /* not an existing branch */
-
-	new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
-	if (!new_branch_info->commit) {
-		/* not a commit */
-		*source_tree = parse_tree_indirect(rev);
-	} else {
-		parse_commit_or_die(new_branch_info->commit);
-		*source_tree = get_commit_tree(new_branch_info->commit);
-	}
-
-	if (!*source_tree)                   /* case (1): want a tree */
+	if (!opts->source_tree)                   /* case (1): want a tree */
 		die(_("reference is not a tree: %s"), arg);
+
 	if (!has_dash_dash) {	/* case (3).(d) -> (1) */
 		/*
 		 * Do not complain the most common case
@@ -1258,7 +1264,7 @@
 		 */
 		if (argc)
 			verify_non_filename(opts->prefix, arg);
-	} else {
+	} else if (opts->accept_pathspec) {
 		argcount++;
 		argv++;
 		argc--;
@@ -1285,6 +1291,60 @@
 	return status;
 }
 
+static void die_expecting_a_branch(const struct branch_info *branch_info)
+{
+	struct object_id oid;
+	char *to_free;
+
+	if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
+		const char *ref = to_free;
+
+		if (skip_prefix(ref, "refs/tags/", &ref))
+			die(_("a branch is expected, got tag '%s'"), ref);
+		if (skip_prefix(ref, "refs/remotes/", &ref))
+			die(_("a branch is expected, got remote branch '%s'"), ref);
+		die(_("a branch is expected, got '%s'"), ref);
+	}
+	if (branch_info->commit)
+		die(_("a branch is expected, got commit '%s'"), branch_info->name);
+	/*
+	 * This case should never happen because we already die() on
+	 * non-commit, but just in case.
+	 */
+	die(_("a branch is expected, got '%s'"), branch_info->name);
+}
+
+static void die_if_some_operation_in_progress(void)
+{
+	struct wt_status_state state;
+
+	memset(&state, 0, sizeof(state));
+	wt_status_get_state(the_repository, &state, 0);
+
+	if (state.merge_in_progress)
+		die(_("cannot switch branch while merging\n"
+		      "Consider \"git merge --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.am_in_progress)
+		die(_("cannot switch branch in the middle of an am session\n"
+		      "Consider \"git am --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.rebase_interactive_in_progress || state.rebase_in_progress)
+		die(_("cannot switch branch while rebasing\n"
+		      "Consider \"git rebase --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.cherry_pick_in_progress)
+		die(_("cannot switch branch while cherry-picking\n"
+		      "Consider \"git cherry-pick --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.revert_in_progress)
+		die(_("cannot switch branch while reverting\n"
+		      "Consider \"git revert --quit\" "
+		      "or \"git worktree add\"."));
+	if (state.bisect_in_progress)
+		warning(_("you are switching branch while bisecting"));
+}
+
 static int checkout_branch(struct checkout_opts *opts,
 			   struct branch_info *new_branch_info)
 {
@@ -1295,9 +1355,9 @@
 		die(_("'%s' cannot be used with switching branches"),
 		    "--patch");
 
-	if (!opts->overlay_mode)
+	if (opts->overlay_mode != -1)
 		die(_("'%s' cannot be used with switching branches"),
-		    "--no-overlay");
+		    "--[no]-overlay");
 
 	if (opts->writeout_stage)
 		die(_("'%s' cannot be used with switching branches"),
@@ -1306,6 +1366,9 @@
 	if (opts->force && opts->merge)
 		die(_("'%s' cannot be used with '%s'"), "-f", "-m");
 
+	if (opts->discard_changes && opts->merge)
+		die(_("'%s' cannot be used with '%s'"), "--discard-changes", "--merge");
+
 	if (opts->force_detach && opts->new_branch)
 		die(_("'%s' cannot be used with '%s'"),
 		    "--detach", "-b/-B/--orphan");
@@ -1313,6 +1376,8 @@
 	if (opts->new_orphan_branch) {
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--orphan", "-t");
+		if (opts->orphan_from_empty_tree && new_branch_info->name)
+			die(_("'%s' cannot take <start-point>"), "--orphan");
 	} else if (opts->force_detach) {
 		if (opts->track != BRANCH_TRACK_UNSPECIFIED)
 			die(_("'%s' cannot be used with '%s'"), "--detach", "-t");
@@ -1323,6 +1388,23 @@
 		die(_("Cannot switch branch to a non-commit '%s'"),
 		    new_branch_info->name);
 
+	if (!opts->switch_branch_doing_nothing_is_ok &&
+	    !new_branch_info->name &&
+	    !opts->new_branch &&
+	    !opts->force_detach)
+		die(_("missing branch or commit argument"));
+
+	if (!opts->implicit_detach &&
+	    !opts->force_detach &&
+	    !opts->new_branch &&
+	    !opts->new_branch_force &&
+	    new_branch_info->name &&
+	    !new_branch_info->path)
+		die_expecting_a_branch(new_branch_info);
+
+	if (!opts->can_switch_when_in_progress)
+		die_if_some_operation_in_progress();
+
 	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
 	    !opts->ignore_other_worktrees) {
 		int flag;
@@ -1344,99 +1426,148 @@
 	return switch_branches(opts, new_branch_info);
 }
 
-int cmd_checkout(int argc, const char **argv, const char *prefix)
+static struct option *add_common_options(struct checkout_opts *opts,
+					 struct option *prevopts)
 {
-	struct checkout_opts opts;
-	struct branch_info new_branch_info;
-	char *conflict_style = NULL;
-	int dwim_new_local_branch, no_dwim_new_local_branch = 0;
-	int dwim_remotes_matched = 0;
 	struct option options[] = {
-		OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
-		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
-			   N_("create and checkout a new branch")),
-		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
-			   N_("create/reset and checkout a branch")),
-		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-		OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
-		OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
-			BRANCH_TRACK_EXPLICIT),
-		OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-		OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
-			      N_("checkout our version for unmerged files"),
-			      2, PARSE_OPT_NONEG),
-		OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
-			      N_("checkout their version for unmerged files"),
-			      3, PARSE_OPT_NONEG),
-		OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-		OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
-			   N_("update ignored files (default)"),
-			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
-			   N_("conflict style (merge or diff3)")),
-		OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
-		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
-			 N_("do not limit pathspecs to sparse entries only")),
-		OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
-			 N_("do not second guess 'git checkout <no-such-branch>'")),
-		OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
-			 N_("do not check if another worktree is holding the given ref")),
+		OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
 		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
 			    "checkout", "control recursive updating of submodules",
 			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
-		OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
-		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
-		OPT_END(),
+		OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
+		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
+		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
+			   N_("conflict style (merge or diff3)")),
+		OPT_END()
 	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
 
-	memset(&opts, 0, sizeof(opts));
+static struct option *add_common_switch_branch_options(
+	struct checkout_opts *opts, struct option *prevopts)
+{
+	struct option options[] = {
+		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
+		OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
+			BRANCH_TRACK_EXPLICIT),
+		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
+			   N_("update ignored files (default)"),
+			   PARSE_OPT_NOCOMPLETE),
+		OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees,
+			 N_("do not check if another worktree is holding the given ref")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static struct option *add_checkout_path_options(struct checkout_opts *opts,
+						struct option *prevopts)
+{
+	struct option options[] = {
+		OPT_SET_INT_F('2', "ours", &opts->writeout_stage,
+			      N_("checkout our version for unmerged files"),
+			      2, PARSE_OPT_NONEG),
+		OPT_SET_INT_F('3', "theirs", &opts->writeout_stage,
+			      N_("checkout their version for unmerged files"),
+			      3, PARSE_OPT_NONEG),
+		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
+			 N_("do not limit pathspecs to sparse entries only")),
+		OPT_END()
+	};
+	struct option *newopts = parse_options_concat(prevopts, options);
+	free(prevopts);
+	return newopts;
+}
+
+static int checkout_main(int argc, const char **argv, const char *prefix,
+			 struct checkout_opts *opts, struct option *options,
+			 const char * const usagestr[])
+{
+	struct branch_info new_branch_info;
+	int dwim_remotes_matched = 0;
+	int parseopt_flags = 0;
+
 	memset(&new_branch_info, 0, sizeof(new_branch_info));
-	opts.overwrite_ignore = 1;
-	opts.prefix = prefix;
-	opts.show_progress = -1;
-	opts.overlay_mode = -1;
+	opts->overwrite_ignore = 1;
+	opts->prefix = prefix;
+	opts->show_progress = -1;
 
-	git_config(git_checkout_config, &opts);
+	git_config(git_checkout_config, opts);
 
-	opts.track = BRANCH_TRACK_UNSPECIFIED;
+	opts->track = BRANCH_TRACK_UNSPECIFIED;
 
-	argc = parse_options(argc, argv, prefix, options, checkout_usage,
-			     PARSE_OPT_KEEP_DASHDASH);
+	if (!opts->accept_pathspec && !opts->accept_ref)
+		BUG("make up your mind, you need to take _something_");
+	if (opts->accept_pathspec && opts->accept_ref)
+		parseopt_flags = PARSE_OPT_KEEP_DASHDASH;
 
-	dwim_new_local_branch = !no_dwim_new_local_branch;
-	if (opts.show_progress < 0) {
-		if (opts.quiet)
-			opts.show_progress = 0;
+	argc = parse_options(argc, argv, prefix, options,
+			     usagestr, parseopt_flags);
+
+	if (opts->show_progress < 0) {
+		if (opts->quiet)
+			opts->show_progress = 0;
 		else
-			opts.show_progress = isatty(2);
+			opts->show_progress = isatty(2);
 	}
 
-	if (conflict_style) {
-		opts.merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+	if (opts->conflict_style) {
+		opts->merge = 1; /* implied */
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
+	}
+	if (opts->force) {
+		opts->discard_changes = 1;
+		opts->ignore_unmerged_opt = "--force";
+		opts->ignore_unmerged = 1;
 	}
 
-	if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
+	if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
 		die(_("-b, -B and --orphan are mutually exclusive"));
 
-	if (opts.overlay_mode == 1 && opts.patch_mode)
+	if (opts->overlay_mode == 1 && opts->patch_mode)
 		die(_("-p and --overlay are mutually exclusive"));
 
+	if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = 0;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = 0;
+	} else {
+		if (opts->checkout_index < 0)
+			opts->checkout_index = -opts->checkout_index - 1;
+		if (opts->checkout_worktree < 0)
+			opts->checkout_worktree = -opts->checkout_worktree - 1;
+	}
+	if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
+		BUG("these flags should be non-negative by now");
+	/*
+	 * convenient shortcut: "git restore --staged" equals
+	 * "git restore --staged --source HEAD"
+	 */
+	if (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)
+		opts->from_treeish = "HEAD";
+
 	/*
 	 * From here on, new_branch will contain the branch to be checked out,
 	 * and new_branch_force and new_orphan_branch will tell us which one of
 	 * -b/-B/--orphan is being used.
 	 */
-	if (opts.new_branch_force)
-		opts.new_branch = opts.new_branch_force;
+	if (opts->new_branch_force)
+		opts->new_branch = opts->new_branch_force;
 
-	if (opts.new_orphan_branch)
-		opts.new_branch = opts.new_orphan_branch;
+	if (opts->new_orphan_branch)
+		opts->new_branch = opts->new_orphan_branch;
 
 	/* --track without -b/-B/--orphan should DWIM */
-	if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {
+	if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
 		const char *argv0 = argv[0];
 		if (!argc || !strcmp(argv0, "--"))
 			die(_("--track needs a branch name"));
@@ -1445,7 +1576,7 @@
 		argv0 = strchr(argv0, '/');
 		if (!argv0 || !argv0[1])
 			die(_("missing branch name; try -b"));
-		opts.new_branch = argv0 + 1;
+		opts->new_branch = argv0 + 1;
 	}
 
 	/*
@@ -1461,59 +1592,75 @@
 	 * including "last branch" syntax and DWIM-ery for names of
 	 * remote branches, erroring out for invalid or ambiguous cases.
 	 */
-	if (argc) {
+	if (argc && opts->accept_ref) {
 		struct object_id rev;
 		int dwim_ok =
-			!opts.patch_mode &&
-			dwim_new_local_branch &&
-			opts.track == BRANCH_TRACK_UNSPECIFIED &&
-			!opts.new_branch;
+			!opts->patch_mode &&
+			opts->dwim_new_local_branch &&
+			opts->track == BRANCH_TRACK_UNSPECIFIED &&
+			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
-					     &new_branch_info, &opts, &rev,
+					     &new_branch_info, opts, &rev,
 					     &dwim_remotes_matched);
 		argv += n;
 		argc -= n;
+	} else if (!opts->accept_ref && opts->from_treeish) {
+		struct object_id rev;
+
+		if (get_oid_mb(opts->from_treeish, &rev))
+			die(_("could not resolve %s"), opts->from_treeish);
+
+		setup_new_branch_info_and_source_tree(&new_branch_info,
+						      opts, &rev,
+						      opts->from_treeish);
+
+		if (!opts->source_tree)
+			die(_("reference is not a tree: %s"), opts->from_treeish);
 	}
 
+	if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
+	    !opts->patch_mode)	/* patch mode is special */
+		die(_("you must specify path(s) to restore"));
+
 	if (argc) {
-		parse_pathspec(&opts.pathspec, 0,
-			       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+		parse_pathspec(&opts->pathspec, 0,
+			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
 			       prefix, argv);
 
-		if (!opts.pathspec.nr)
+		if (!opts->pathspec.nr)
 			die(_("invalid path specification"));
 
 		/*
 		 * Try to give more helpful suggestion.
 		 * new_branch && argc > 1 will be caught later.
 		 */
-		if (opts.new_branch && argc == 1)
+		if (opts->new_branch && argc == 1)
 			die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
-				argv[0], opts.new_branch);
+				argv[0], opts->new_branch);
 
-		if (opts.force_detach)
+		if (opts->force_detach)
 			die(_("git checkout: --detach does not take a path argument '%s'"),
 			    argv[0]);
 
-		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+		if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
 			die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 			      "checking out of the index."));
 	}
 
-	if (opts.new_branch) {
+	if (opts->new_branch) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (opts.new_branch_force)
-			opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+		if (opts->new_branch_force)
+			opts->branch_exists = validate_branchname(opts->new_branch, &buf);
 		else
-			opts.branch_exists =
-				validate_new_branchname(opts.new_branch, &buf, 0);
+			opts->branch_exists =
+				validate_new_branchname(opts->new_branch, &buf, 0);
 		strbuf_release(&buf);
 	}
 
 	UNLEAK(opts);
-	if (opts.patch_mode || opts.pathspec.nr) {
-		int ret = checkout_paths(&opts, new_branch_info.name);
+	if (opts->patch_mode || opts->pathspec.nr) {
+		int ret = checkout_paths(opts, new_branch_info.name);
 		if (ret && dwim_remotes_matched > 1 &&
 		    advice_checkout_ambiguous_remote_branch_name)
 			advise(_("'%s' matched more than one remote tracking branch.\n"
@@ -1532,6 +1679,123 @@
 			       dwim_remotes_matched);
 		return ret;
 	} else {
-		return checkout_branch(&opts, &new_branch_info);
+		return checkout_branch(opts, &new_branch_info);
 	}
 }
+
+int cmd_checkout(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options;
+	struct option checkout_options[] = {
+		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+			   N_("create and checkout a new branch")),
+		OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and checkout a branch")),
+		OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git checkout <no-such-branch>' (default)")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
+		OPT_END()
+	};
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.dwim_new_local_branch = 1;
+	opts.switch_branch_doing_nothing_is_ok = 1;
+	opts.only_merge_on_switching_branches = 0;
+	opts.accept_ref = 1;
+	opts.accept_pathspec = 1;
+	opts.implicit_detach = 1;
+	opts.can_switch_when_in_progress = 1;
+	opts.orphan_from_empty_tree = 0;
+	opts.empty_pathspec_ok = 1;
+	opts.overlay_mode = -1;
+	opts.checkout_index = -2;    /* default on */
+	opts.checkout_worktree = -2; /* default on */
+
+	options = parse_options_dup(checkout_options);
+	options = add_common_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, checkout_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_switch(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options = NULL;
+	struct option switch_options[] = {
+		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
+			   N_("create and switch to a new branch")),
+		OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"),
+			   N_("create/reset and switch to a branch")),
+		OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
+			 N_("second guess 'git switch <no-such-branch>'")),
+		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
+			 N_("throw away local modifications")),
+		OPT_END()
+	};
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.dwim_new_local_branch = 1;
+	opts.accept_ref = 1;
+	opts.accept_pathspec = 0;
+	opts.switch_branch_doing_nothing_is_ok = 0;
+	opts.only_merge_on_switching_branches = 1;
+	opts.implicit_detach = 0;
+	opts.can_switch_when_in_progress = 0;
+	opts.orphan_from_empty_tree = 1;
+	opts.overlay_mode = -1;
+
+	options = parse_options_dup(switch_options);
+	options = add_common_options(&opts, options);
+	options = add_common_switch_branch_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, switch_branch_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
+
+int cmd_restore(int argc, const char **argv, const char *prefix)
+{
+	struct checkout_opts opts;
+	struct option *options;
+	struct option restore_options[] = {
+		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
+			   N_("where the checkout from")),
+		OPT_BOOL('S', "staged", &opts.checkout_index,
+			   N_("restore the index")),
+		OPT_BOOL('W', "worktree", &opts.checkout_worktree,
+			   N_("restore the working tree (default)")),
+		OPT_BOOL(0, "ignore-unmerged", &opts.ignore_unmerged,
+			 N_("ignore unmerged entries")),
+		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
+		OPT_END()
+	};
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.accept_ref = 0;
+	opts.accept_pathspec = 1;
+	opts.empty_pathspec_ok = 0;
+	opts.overlay_mode = 0;
+	opts.checkout_index = -1;    /* default off */
+	opts.checkout_worktree = -2; /* default on */
+	opts.ignore_unmerged_opt = "--ignore-unmerged";
+
+	options = parse_options_dup(restore_options);
+	options = add_common_options(&opts, options);
+	options = add_checkout_path_options(&opts, options);
+
+	ret = checkout_main(argc, argv, prefix, &opts,
+			    options, restore_usage);
+	FREE_AND_NULL(options);
+	return ret;
+}
diff --git a/builtin/clone.c b/builtin/clone.c
index 5b9ebe9..a4fe728 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -494,7 +494,7 @@
 static const char junk_leave_repo_msg[] =
 N_("Clone succeeded, but checkout failed.\n"
    "You can inspect what was checked out with 'git status'\n"
-   "and retry the checkout with 'git checkout -f HEAD'\n");
+   "and retry with 'git restore --source=HEAD :/'\n");
 
 static void remove_junk(void)
 {
@@ -1252,7 +1252,7 @@
 	transport_disconnect(transport);
 
 	if (option_dissociate) {
-		close_all_packs(the_repository->objects);
+		close_object_store(the_repository->objects);
 		dissociate_from_references();
 	}
 
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 537fdfd..d8efa5b 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -141,6 +141,8 @@
 	struct string_list *pack_indexes = NULL;
 	struct string_list *commit_hex = NULL;
 	struct string_list lines;
+	int result = 0;
+	unsigned int flags = COMMIT_GRAPH_PROGRESS;
 
 	static struct option builtin_commit_graph_write_options[] = {
 		OPT_STRING(0, "object-dir", &opts.obj_dir,
@@ -165,13 +167,13 @@
 		die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs"));
 	if (!opts.obj_dir)
 		opts.obj_dir = get_object_directory();
+	if (opts.append)
+		flags |= COMMIT_GRAPH_APPEND;
 
 	read_replace_refs = 0;
 
-	if (opts.reachable) {
-		write_commit_graph_reachable(opts.obj_dir, opts.append, 1);
-		return 0;
-	}
+	if (opts.reachable)
+		return write_commit_graph_reachable(opts.obj_dir, flags);
 
 	string_list_init(&lines, 0);
 	if (opts.stdin_packs || opts.stdin_commits) {
@@ -188,14 +190,14 @@
 		UNLEAK(buf);
 	}
 
-	write_commit_graph(opts.obj_dir,
-			   pack_indexes,
-			   commit_hex,
-			   opts.append,
-			   1);
+	if (write_commit_graph(opts.obj_dir,
+			       pack_indexes,
+			       commit_hex,
+			       flags))
+		result = 1;
 
 	UNLEAK(lines);
-	return 0;
+	return result;
 }
 
 int cmd_commit_graph(int argc, const char **argv, const char *prefix)
diff --git a/builtin/commit.c b/builtin/commit.c
index 1c9e8e2..3b561c2 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1078,9 +1078,11 @@
 static struct status_deferred_config {
 	enum wt_status_format status_format;
 	int show_branch;
+	enum ahead_behind_flags ahead_behind;
 } status_deferred_config = {
 	STATUS_FORMAT_UNSPECIFIED,
-	-1 /* unspecified */
+	-1, /* unspecified */
+	AHEAD_BEHIND_UNSPECIFIED,
 };
 
 static void finalize_deferred_config(struct wt_status *s)
@@ -1107,6 +1109,17 @@
 	if (s->show_branch < 0)
 		s->show_branch = 0;
 
+	/*
+	 * If the user did not give a "--[no]-ahead-behind" command
+	 * line argument *AND* we will print in a human-readable format
+	 * (short, long etc.) then we inherit from the status.aheadbehind
+	 * config setting.  In all other cases (and porcelain V[12] formats
+	 * in particular), we inherit _FULL for backwards compatibility.
+	 */
+	if (use_deferred_config &&
+	    s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
+		s->ahead_behind_flags = status_deferred_config.ahead_behind;
+
 	if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
 		s->ahead_behind_flags = AHEAD_BEHIND_FULL;
 }
@@ -1246,6 +1259,10 @@
 		status_deferred_config.show_branch = git_config_bool(k, v);
 		return 0;
 	}
+	if (!strcmp(k, "status.aheadbehind")) {
+		status_deferred_config.ahead_behind = git_config_bool(k, v);
+		return 0;
+	}
 	if (!strcmp(k, "status.showstash")) {
 		s->show_stash = git_config_bool(k, v);
 		return 0;
@@ -1658,7 +1675,7 @@
 		die("%s", err.buf);
 	}
 
-	sequencer_post_commit_cleanup(the_repository);
+	sequencer_post_commit_cleanup(the_repository, 0);
 	unlink(git_path_merge_head(the_repository));
 	unlink(git_path_merge_msg(the_repository));
 	unlink(git_path_merge_mode(the_repository));
@@ -1667,10 +1684,11 @@
 	if (commit_index_files())
 		die(_("repository has been updated, but unable to write\n"
 		      "new_index file. Check that disk is not full and quota is\n"
-		      "not exceeded, and then \"git reset HEAD\" to recover."));
+		      "not exceeded, and then \"git restore --staged :/\" to recover."));
 
-	if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
-		write_commit_graph_reachable(get_object_directory(), 0, 0);
+	if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
+	    write_commit_graph_reachable(get_object_directory(), 0))
+		return 1;
 
 	repo_rerere(the_repository, 0);
 	run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
diff --git a/builtin/describe.c b/builtin/describe.c
index 1409ced..2001542 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -76,7 +76,7 @@
 
 static inline struct commit_name *find_commit_name(const struct object_id *peeled)
 {
-	return hashmap_get_from_hash(&names, sha1hash(peeled->hash), peeled->hash);
+	return hashmap_get_from_hash(&names, oidhash(peeled), peeled);
 }
 
 static int replace_name(struct commit_name *e,
@@ -123,7 +123,7 @@
 		if (!e) {
 			e = xmalloc(sizeof(struct commit_name));
 			oidcpy(&e->peeled, peeled);
-			hashmap_entry_init(e, sha1hash(peeled->hash));
+			hashmap_entry_init(e, oidhash(peeled));
 			hashmap_add(&names, e);
 			e->path = NULL;
 		}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index c22cef3..f541f55 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -275,7 +275,7 @@
 	if (is_null_oid(oid))
 		return;
 
-	object = lookup_object(the_repository, oid->hash);
+	object = lookup_object(the_repository, oid);
 	if (object && object->flags & SHOWN)
 		return;
 
@@ -453,7 +453,7 @@
 						  &spec->oid));
 			else {
 				struct object *object = lookup_object(the_repository,
-								      spec->oid.hash);
+								      &spec->oid);
 				printf("M %06o :%d ", spec->mode,
 				       get_object_mark(object));
 			}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 4ba63d5..53ce99d 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -24,6 +24,8 @@
 #include "list-objects-filter-options.h"
 #include "commit-reach.h"
 
+#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
+
 static const char * const builtin_fetch_usage[] = {
 	N_("git fetch [<options>] [<repository> [<refspec>...]]"),
 	N_("git fetch [<options>] <group>"),
@@ -39,6 +41,8 @@
 };
 
 static int fetch_prune_config = -1; /* unspecified */
+static int fetch_show_forced_updates = 1;
+static uint64_t forced_updates_ms = 0;
 static int prune = -1; /* unspecified */
 #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
 
@@ -48,6 +52,7 @@
 
 static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
 static int progress = -1;
+static int enable_auto_gc = 1;
 static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
 static int max_children = 1;
 static enum transport_family family;
@@ -79,6 +84,11 @@
 		return 0;
 	}
 
+	if (!strcmp(k, "fetch.showforcedupdates")) {
+		fetch_show_forced_updates = git_config_bool(k, v);
+		return 0;
+	}
+
 	if (!strcmp(k, "submodule.recurse")) {
 		int r = git_config_bool(k, v) ?
 			RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
@@ -169,6 +179,10 @@
 	OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
 			N_("report that we have only objects reachable from this object")),
 	OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+	OPT_BOOL(0, "auto-gc", &enable_auto_gc,
+		 N_("run 'gc --auto' after fetching")),
+	OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
+		 N_("check for forced-updates on all updated branches")),
 	OPT_END()
 };
 
@@ -239,6 +253,7 @@
 struct refname_hash_entry {
 	struct hashmap_entry ent; /* must be the first member */
 	struct object_id oid;
+	int ignore;
 	char refname[FLEX_ARRAY];
 };
 
@@ -287,6 +302,11 @@
 	return !!hashmap_get_from_hash(map, strhash(refname), refname);
 }
 
+static void clear_item(struct refname_hash_entry *item)
+{
+	item->ignore = 1;
+}
+
 static void find_non_local_tags(const struct ref *refs,
 				struct ref **head,
 				struct ref ***tail)
@@ -319,7 +339,7 @@
 			    !will_fetch(head, ref->old_oid.hash) &&
 			    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
 			    !will_fetch(head, item->oid.hash))
-				oidclr(&item->oid);
+				clear_item(item);
 			item = NULL;
 			continue;
 		}
@@ -333,7 +353,7 @@
 		if (item &&
 		    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
 		    !will_fetch(head, item->oid.hash))
-			oidclr(&item->oid);
+			clear_item(item);
 
 		item = NULL;
 
@@ -354,7 +374,7 @@
 	if (item &&
 	    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
 	    !will_fetch(head, item->oid.hash))
-		oidclr(&item->oid);
+		clear_item(item);
 
 	/*
 	 * For all the tags in the remote_refs_list,
@@ -362,19 +382,21 @@
 	 */
 	for_each_string_list_item(remote_ref_item, &remote_refs_list) {
 		const char *refname = remote_ref_item->string;
+		struct ref *rm;
 
 		item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
 		if (!item)
 			BUG("unseen remote ref?");
 
 		/* Unless we have already decided to ignore this item... */
-		if (!is_null_oid(&item->oid)) {
-			struct ref *rm = alloc_ref(item->refname);
-			rm->peer_ref = alloc_ref(item->refname);
-			oidcpy(&rm->old_oid, &item->oid);
-			**tail = rm;
-			*tail = &rm->next;
-		}
+		if (item->ignore)
+			continue;
+
+		rm = alloc_ref(item->refname);
+		rm->peer_ref = alloc_ref(item->refname);
+		oidcpy(&rm->old_oid, &item->oid);
+		**tail = rm;
+		*tail = &rm->next;
 	}
 	hashmap_free(&remote_refs, 1);
 	string_list_clear(&remote_refs_list, 0);
@@ -699,6 +721,7 @@
 	enum object_type type;
 	struct branch *current_branch = branch_get(NULL);
 	const char *pretty_ref = prettify_refname(ref->name);
+	int fast_forward = 0;
 
 	type = oid_object_info(the_repository, &ref->new_oid, NULL);
 	if (type < 0)
@@ -773,9 +796,18 @@
 		return r;
 	}
 
-	if (in_merge_bases(current, updated)) {
+	if (fetch_show_forced_updates) {
+		uint64_t t_before = getnanotime();
+		fast_forward = in_merge_bases(current, updated);
+		forced_updates_ms += (getnanotime() - t_before) / 1000000;
+	} else {
+		fast_forward = 1;
+	}
+
+	if (fast_forward) {
 		struct strbuf quickref = STRBUF_INIT;
 		int r;
+
 		strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
 		strbuf_addstr(&quickref, "..");
 		strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
@@ -971,6 +1003,17 @@
 		      " 'git remote prune %s' to remove any old, conflicting "
 		      "branches"), remote_name);
 
+	if (advice_fetch_show_forced_updates) {
+		if (!fetch_show_forced_updates) {
+			warning(_("Fetch normally indicates which branches had a forced update, but that check has been disabled."));
+			warning(_("To re-enable, use '--show-forced-updates' flag or run 'git config fetch.showForcedUpdates true'."));
+		} else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
+			warning(_("It took %.2f seconds to check forced updates. You can use '--no-show-forced-updates'\n"),
+				forced_updates_ms / 1000.0);
+			warning(_("or run 'git config fetch.showForcedUpdates false' to avoid this check.\n"));
+		}
+	}
+
  abort:
 	strbuf_release(&note);
 	free(url);
@@ -1424,7 +1467,7 @@
 			return errcode;
 	}
 
-	argv_array_pushl(&argv, "fetch", "--append", NULL);
+	argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
 	add_options_to_argv(&argv);
 
 	for (i = 0; i < list->nr; i++) {
@@ -1672,13 +1715,15 @@
 
 	string_list_clear(&list, 0);
 
-	close_all_packs(the_repository->objects);
+	close_object_store(the_repository->objects);
 
-	argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
-	if (verbosity < 0)
-		argv_array_push(&argv_gc_auto, "--quiet");
-	run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
-	argv_array_clear(&argv_gc_auto);
+	if (enable_auto_gc) {
+		argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
+		if (verbosity < 0)
+			argv_array_push(&argv_gc_auto, "--quiet");
+		run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
+		argv_array_clear(&argv_gc_auto);
+	}
 
 	return result;
 }
diff --git a/builtin/fsck.c b/builtin/fsck.c
index d26fb0a..18403a9 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -238,7 +238,7 @@
 static void mark_unreachable_referents(const struct object_id *oid)
 {
 	struct fsck_options options = FSCK_OPTIONS_DEFAULT;
-	struct object *obj = lookup_object(the_repository, oid->hash);
+	struct object *obj = lookup_object(the_repository, oid);
 
 	if (!obj || !(obj->flags & HAS_OBJ))
 		return; /* not part of our original set */
@@ -497,7 +497,7 @@
 	struct object *obj;
 
 	if (!is_null_oid(oid)) {
-		obj = lookup_object(the_repository, oid->hash);
+		obj = lookup_object(the_repository, oid);
 		if (obj && (obj->flags & HAS_OBJ)) {
 			if (timestamp && name_objects)
 				add_decoration(fsck_walk_options.object_names,
@@ -756,7 +756,7 @@
 
 static void mark_object_for_connectivity(const struct object_id *oid)
 {
-	struct object *obj = lookup_unknown_object(oid->hash);
+	struct object *obj = lookup_unknown_object(oid);
 	obj->flags |= HAS_OBJ;
 }
 
@@ -879,7 +879,7 @@
 		struct object_id oid;
 		if (!get_oid(arg, &oid)) {
 			struct object *obj = lookup_object(the_repository,
-							   oid.hash);
+							   &oid);
 
 			if (!obj || !(obj->flags & HAS_OBJ)) {
 				if (is_promisor_object(&oid))
diff --git a/builtin/gc.c b/builtin/gc.c
index 8943bcc..be8e0bf 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -653,7 +653,7 @@
 	gc_before_repack();
 
 	if (!repository_format_precious_objects) {
-		close_all_packs(the_repository->objects);
+		close_object_store(the_repository->objects);
 		if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
 			die(FAILED_RUN, repack.argv[0]);
 
@@ -681,13 +681,14 @@
 	report_garbage = report_pack_garbage;
 	reprepare_packed_git(the_repository);
 	if (pack_garbage.nr > 0) {
-		close_all_packs(the_repository->objects);
+		close_object_store(the_repository->objects);
 		clean_pack_garbage();
 	}
 
-	if (gc_write_commit_graph)
-		write_commit_graph_reachable(get_object_directory(), 0,
-					     !quiet && !daemonized);
+	if (gc_write_commit_graph &&
+	    write_commit_graph_reachable(get_object_directory(),
+					 !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0))
+		return 1;
 
 	if (auto_gc && too_many_loose_objects())
 		warning(_("There are too many unreachable loose objects; "
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 8ae40de..f101d09 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -10,6 +10,7 @@
 #include "parse-options.h"
 #include "string-list.h"
 #include "trailer.h"
+#include "config.h"
 
 static const char * const git_interpret_trailers_usage[] = {
 	N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
@@ -112,6 +113,8 @@
 		OPT_END()
 	};
 
+	git_config(git_default_config, NULL);
+
 	argc = parse_options(argc, argv, prefix, options,
 			     git_interpret_trailers_usage, 0);
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 7f83c9a..670e8fb 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -373,7 +373,7 @@
 	first = pos;
 	last = istate->cache_nr;
 	while (last > first) {
-		int next = (last + first) >> 1;
+		int next = first + ((last - first) >> 1);
 		const struct cache_entry *ce = istate->cache[next];
 		if (!strncmp(ce->name, prefix, prefixlen)) {
 			first = next+1;
diff --git a/builtin/merge.c b/builtin/merge.c
index 6e99aea..aad5a95 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -453,7 +453,7 @@
 			 * We ignore errors in 'gc --auto', since the
 			 * user should see them.
 			 */
-			close_all_packs(the_repository->objects);
+			close_object_store(the_repository->objects);
 			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
 		}
 	}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 16df434..c785fe1 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -378,8 +378,7 @@
 			*(p+1) = 0;
 			if (!get_oid(p - (hexsz - 1), &oid)) {
 				struct object *o =
-					lookup_object(the_repository,
-						      oid.hash);
+					lookup_object(the_repository, &oid);
 				if (o)
 					name = get_rev_name(o, &buf);
 			}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index b2be886..000dc4b 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -606,12 +606,12 @@
 		       void *cb_data)
 {
 	struct object_id peeled;
-	struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL);
+	struct object_entry *entry = packlist_find(&to_pack, oid, NULL);
 
 	if (entry)
 		entry->tagged = 1;
 	if (!peel_ref(path, &peeled)) {
-		entry = packlist_find(&to_pack, peeled.hash, NULL);
+		entry = packlist_find(&to_pack, &peeled, NULL);
 		if (entry)
 			entry->tagged = 1;
 	}
@@ -996,7 +996,7 @@
 {
 	struct object_entry *entry;
 
-	entry = packlist_find(&to_pack, oid->hash, index_pos);
+	entry = packlist_find(&to_pack, oid, index_pos);
 	if (!entry)
 		return 0;
 
@@ -1494,11 +1494,13 @@
 	if (!base_sha1)
 		return 0;
 
+	oidread(&base_oid, base_sha1);
+
 	/*
 	 * First see if we're already sending the base (or it's explicitly in
 	 * our "excluded" list).
 	 */
-	base = packlist_find(&to_pack, base_sha1, NULL);
+	base = packlist_find(&to_pack, &base_oid, NULL);
 	if (base) {
 		if (!in_same_island(&delta->idx.oid, &base->idx.oid))
 			return 0;
@@ -1511,7 +1513,6 @@
 	 * even if it was buried too deep in history to make it into the
 	 * packing list.
 	 */
-	oidread(&base_oid, base_sha1);
 	if (thin && bitmap_has_oid_in_uninteresting(bitmap_git, &base_oid)) {
 		if (use_delta_islands) {
 			if (!in_same_island(&delta->idx.oid, &base_oid))
@@ -2571,7 +2572,7 @@
 	 * it was included via bitmaps, we would not have parsed it
 	 * previously).
 	 */
-	if (packlist_find(&to_pack, oid->hash, NULL))
+	if (packlist_find(&to_pack, oid, NULL))
 		return;
 
 	tag = lookup_tag(the_repository, oid);
@@ -2595,7 +2596,7 @@
 
 	if (starts_with(path, "refs/tags/") && /* is a tag? */
 	    !peel_ref(path, &peeled)    && /* peelable? */
-	    packlist_find(&to_pack, peeled.hash, NULL))      /* object packed? */
+	    packlist_find(&to_pack, &peeled, NULL))      /* object packed? */
 		add_tag_chain(oid);
 	return 0;
 }
@@ -2795,7 +2796,7 @@
 		for (p = strchr(name, '/'); p; p = strchr(p + 1, '/'))
 			depth++;
 
-		ent = packlist_find(&to_pack, obj->oid.hash, NULL);
+		ent = packlist_find(&to_pack, &obj->oid, NULL);
 		if (ent && depth > oe_tree_depth(&to_pack, ent))
 			oe_set_tree_depth(&to_pack, ent, depth);
 	}
@@ -2922,7 +2923,7 @@
 
 		for (i = 0; i < p->num_objects; i++) {
 			nth_packed_object_oid(&oid, p, i);
-			o = lookup_unknown_object(oid.hash);
+			o = lookup_unknown_object(&oid);
 			if (!(o->flags & OBJECT_ADDED))
 				mark_in_pack_object(o, p, &in_pack);
 			o->flags |= OBJECT_ADDED;
@@ -3026,7 +3027,7 @@
 
 		for (i = 0; i < p->num_objects; i++) {
 			nth_packed_object_oid(&oid, p, i);
-			if (!packlist_find(&to_pack, oid.hash, NULL) &&
+			if (!packlist_find(&to_pack, &oid, NULL) &&
 			    !has_sha1_pack_kept_or_nonlocal(&oid) &&
 			    !loosened_object_can_be_discarded(&oid, p->mtime))
 				if (force_object_loose(&oid, p->mtime))
@@ -3134,7 +3135,7 @@
 		return;
 
 	if (use_delta_islands)
-		load_delta_islands(the_repository);
+		load_delta_islands(the_repository, progress);
 
 	if (prepare_revision_walk(&revs))
 		die(_("revision walk setup failed"));
diff --git a/builtin/prune.c b/builtin/prune.c
index 97613ec..2b76872 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -53,7 +53,7 @@
 
 	perform_reachability_traversal(revs);
 
-	obj = lookup_object(the_repository, oid->hash);
+	obj = lookup_object(the_repository, oid);
 	return obj && (obj->flags & SEEN);
 }
 
diff --git a/builtin/pull.c b/builtin/pull.c
index 9dd32a1..f1eaf6e 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -128,6 +128,7 @@
 static char *opt_refmap;
 static char *opt_ipv4;
 static char *opt_ipv6;
+static int opt_show_forced_updates = -1;
 
 static struct option pull_options[] = {
 	/* Shared options */
@@ -240,6 +241,8 @@
 	OPT_PASSTHRU('6',  "ipv6", &opt_ipv6, NULL,
 		N_("use IPv6 addresses only"),
 		PARSE_OPT_NOARG),
+	OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
+		 N_("check for forced-updates on all updated branches")),
 
 	OPT_END()
 };
@@ -549,6 +552,10 @@
 		argv_array_push(&args, opt_ipv4);
 	if (opt_ipv6)
 		argv_array_push(&args, opt_ipv6);
+	if (opt_show_forced_updates > 0)
+		argv_array_push(&args, "--show-forced-updates");
+	else if (opt_show_forced_updates == 0)
+		argv_array_push(&args, "--no-show-forced-updates");
 
 	if (repo) {
 		argv_array_push(&args, repo);
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b8116db..89fc4b8 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -738,20 +738,30 @@
 {
 	struct strbuf dir = STRBUF_INIT;
 	const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+	int ret = 0;
 
 	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 	apply_autostash(opts);
-	close_all_packs(the_repository->objects);
+	close_object_store(the_repository->objects);
 	/*
 	 * We ignore errors in 'gc --auto', since the
 	 * user should see them.
 	 */
 	run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
-	strbuf_addstr(&dir, opts->state_dir);
-	remove_dir_recursively(&dir, 0);
-	strbuf_release(&dir);
+	if (opts->type == REBASE_INTERACTIVE) {
+		struct replay_opts replay = REPLAY_OPTS_INIT;
 
-	return 0;
+		replay.action = REPLAY_INTERACTIVE_REBASE;
+		ret = sequencer_remove_state(&replay);
+	} else {
+		strbuf_addstr(&dir, opts->state_dir);
+		if (remove_dir_recursively(&dir, 0))
+			ret = error(_("could not remove '%s'"),
+				    opts->state_dir);
+		strbuf_release(&dir);
+	}
+
+	return ret;
 }
 
 static struct commit *peel_committish(const char *name)
@@ -1379,6 +1389,7 @@
 	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
 	struct object_id squash_onto;
 	char *squash_onto_name = NULL;
+	int reschedule_failed_exec = -1;
 	struct option builtin_rebase_options[] = {
 		OPT_STRING(0, "onto", &options.onto_name,
 			   N_("revision"),
@@ -1471,7 +1482,7 @@
 		OPT_BOOL(0, "root", &options.root,
 			 N_("rebase all reachable commits up to the root(s)")),
 		OPT_BOOL(0, "reschedule-failed-exec",
-			 &options.reschedule_failed_exec,
+			 &reschedule_failed_exec,
 			 N_("automatically re-schedule any `exec` that fails")),
 		OPT_END(),
 	};
@@ -1600,7 +1611,7 @@
 		if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
 			       NULL, NULL) < 0)
 			die(_("could not discard worktree changes"));
-		remove_branch_state(the_repository);
+		remove_branch_state(the_repository, 0);
 		if (read_basic_state(&options))
 			exit(1);
 		goto run_rebase;
@@ -1620,16 +1631,24 @@
 			       NULL, NULL) < 0)
 			die(_("could not move back to %s"),
 			    oid_to_hex(&options.orig_head));
-		remove_branch_state(the_repository);
-		ret = finish_rebase(&options);
+		remove_branch_state(the_repository, 0);
+		ret = !!finish_rebase(&options);
 		goto cleanup;
 	}
 	case ACTION_QUIT: {
-		strbuf_reset(&buf);
-		strbuf_addstr(&buf, options.state_dir);
-		ret = !!remove_dir_recursively(&buf, 0);
-		if (ret)
-			die(_("could not remove '%s'"), options.state_dir);
+		if (options.type == REBASE_INTERACTIVE) {
+			struct replay_opts replay = REPLAY_OPTS_INIT;
+
+			replay.action = REPLAY_INTERACTIVE_REBASE;
+			ret = !!sequencer_remove_state(&replay);
+		} else {
+			strbuf_reset(&buf);
+			strbuf_addstr(&buf, options.state_dir);
+			ret = !!remove_dir_recursively(&buf, 0);
+			if (ret)
+				error(_("could not remove '%s'"),
+				       options.state_dir);
+		}
 		goto cleanup;
 	}
 	case ACTION_EDIT_TODO:
@@ -1778,8 +1797,11 @@
 		break;
 	}
 
-	if (options.reschedule_failed_exec && !is_interactive(&options))
-		die(_("%s requires an interactive rebase"), "--reschedule-failed-exec");
+	if (reschedule_failed_exec > 0 && !is_interactive(&options))
+		die(_("--reschedule-failed-exec requires "
+		      "--exec or --interactive"));
+	if (reschedule_failed_exec >= 0)
+		options.reschedule_failed_exec = reschedule_failed_exec;
 
 	if (options.git_am_opts.argc) {
 		/* all am options except -q are compatible only with --am */
@@ -2141,6 +2163,7 @@
 	ret = !!run_specific_rebase(&options, action);
 
 cleanup:
+	strbuf_release(&buf);
 	strbuf_release(&revisions);
 	free(options.head_name);
 	free(options.gpg_sign_opt);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 77b7122..610eadf 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -2042,7 +2042,7 @@
 			proc.git_cmd = 1;
 			proc.argv = argv_gc_auto;
 
-			close_all_packs(the_repository->objects);
+			close_object_store(the_repository->objects);
 			if (!start_command(&proc)) {
 				if (use_sideband)
 					copy_to_sideband(proc.err, -1, NULL);
diff --git a/builtin/repack.c b/builtin/repack.c
index caca113..f834b55 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -422,7 +422,7 @@
 	if (!names.nr && !po_args.quiet)
 		printf_ln(_("Nothing new to pack."));
 
-	close_all_packs(the_repository->objects);
+	close_object_store(the_repository->objects);
 
 	/*
 	 * Ok we have prepared all new packfiles.
diff --git a/builtin/reset.c b/builtin/reset.c
index 26ef9a7..c2bb35a 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -421,7 +421,7 @@
 			print_new_head_line(lookup_commit_reference(the_repository, &oid));
 	}
 	if (!pathspec.nr)
-		remove_branch_state(the_repository);
+		remove_branch_state(the_repository, 0);
 
 	return update_ref_status;
 }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 660172b..301ccb9 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -49,6 +49,7 @@
 "    --objects | --objects-edge\n"
 "    --unpacked\n"
 "    --header | --pretty\n"
+"    --[no-]object-names\n"
 "    --abbrev=<n> | --no-abbrev\n"
 "    --abbrev-commit\n"
 "    --left-right\n"
@@ -75,6 +76,9 @@
 };
 static enum missing_action arg_missing_action;
 
+/* display only the oid of each object encountered */
+static int arg_show_object_names = 1;
+
 #define DEFAULT_OIDSET_SIZE     (16*1024)
 
 static void finish_commit(struct commit *commit);
@@ -255,7 +259,10 @@
 	display_progress(progress, ++progress_counter);
 	if (info->flags & REV_LIST_QUIET)
 		return;
-	show_object_with_name(stdout, obj, name);
+	if (arg_show_object_names)
+		show_object_with_name(stdout, obj, name);
+	else
+		printf("%s\n", oid_to_hex(&obj->oid));
 }
 
 static void show_edge(struct commit *commit)
@@ -484,6 +491,16 @@
 		if (skip_prefix(arg, "--missing=", &arg))
 			continue; /* already handled above */
 
+		if (!strcmp(arg, ("--no-object-names"))) {
+			arg_show_object_names = 0;
+			continue;
+		}
+
+		if (!strcmp(arg, ("--object-names"))) {
+			arg_show_object_names = 1;
+			continue;
+		}
+
 		usage(rev_list_usage);
 
 	}
diff --git a/builtin/revert.c b/builtin/revert.c
index d4dcedb..4e71b2f 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -203,7 +203,7 @@
 	if (cmd == 'q') {
 		int ret = sequencer_remove_state(opts);
 		if (!ret)
-			remove_branch_state(the_repository);
+			remove_branch_state(the_repository, 0);
 		return ret;
 	}
 	if (cmd == 'c')
diff --git a/builtin/stash.c b/builtin/stash.c
index 2a8e6d0..fde6397 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -713,11 +713,11 @@
 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 argv_array revision_args = ARGV_ARRAY_INIT;
 	struct option options[] = {
 		OPT_END()
 	};
@@ -726,11 +726,12 @@
 	git_config(git_diff_ui_config, NULL);
 	init_revisions(&rev, prefix);
 
+	argv_array_push(&revision_args, argv[0]);
 	for (i = 1; i < argc; i++) {
 		if (argv[i][0] != '-')
 			argv_array_push(&stash_args, argv[i]);
 		else
-			opts++;
+			argv_array_push(&revision_args, argv[i]);
 	}
 
 	ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
@@ -742,7 +743,7 @@
 	 * The config settings are applied only if there are not passed
 	 * any options.
 	 */
-	if (!opts) {
+	if (revision_args.argc == 1) {
 		git_config(git_stash_config, NULL);
 		if (show_stat)
 			rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
@@ -756,7 +757,7 @@
 		}
 	}
 
-	argc = setup_revisions(argc, argv, &rev, NULL);
+	argc = setup_revisions(revision_args.argc, revision_args.argv, &rev, NULL);
 	if (argc > 1) {
 		free_stash_info(&info);
 		usage_with_options(git_stash_show_usage, options);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 13da32d..909e77e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -540,6 +540,7 @@
 		if (info->quiet)
 			argv_array_push(&cpr.args, "--quiet");
 
+		argv_array_push(&cpr.args, "--");
 		argv_array_pushv(&cpr.args, info->argv);
 
 		if (run_command(&cpr))
diff --git a/builtin/tag.c b/builtin/tag.c
index ef37dcc..e0a4c25 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -33,6 +33,7 @@
 
 static unsigned int colopts;
 static int force_sign_annotate;
+static int config_sign_tag = -1; /* unspecified */
 
 static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
 		     struct ref_format *format)
@@ -144,6 +145,11 @@
 	int status;
 	struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
 
+	if (!strcmp(var, "tag.gpgsign")) {
+		config_sign_tag = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (!strcmp(var, "tag.sort")) {
 		if (!value)
 			return config_error_nonbool(var);
@@ -442,15 +448,10 @@
 	memset(&opt, 0, sizeof(opt));
 	memset(&filter, 0, sizeof(filter));
 	filter.lines = -1;
+	opt.sign = -1;
 
 	argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
 
-	if (keyid) {
-		opt.sign = 1;
-		set_signing_key(keyid);
-	}
-	create_tag_object = (opt.sign || annotate || msg.given || msgfile);
-
 	if (!cmdmode) {
 		if (argc == 0)
 			cmdmode = 'l';
@@ -463,6 +464,15 @@
 	if (cmdmode == 'l')
 		setup_auto_pager("tag", 1);
 
+	if (opt.sign == -1)
+		opt.sign = cmdmode ? 0 : config_sign_tag > 0;
+
+	if (keyid) {
+		opt.sign = 1;
+		set_signing_key(keyid);
+	}
+	create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+
 	if ((create_tag_object || force) && (cmdmode != 0))
 		usage_with_options(git_tag_usage, options);
 
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 8047880..a87a4bf 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -332,7 +332,7 @@
 {
 	struct object *obj;
 	struct obj_buffer *obj_buffer;
-	obj = lookup_object(the_repository, base->hash);
+	obj = lookup_object(the_repository, base);
 	if (!obj)
 		return 0;
 	obj_buffer = lookup_object_buffer(obj);
diff --git a/cache-tree.c b/cache-tree.c
index b13bfaf..706ffcf 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -6,8 +6,8 @@
 #include "object-store.h"
 #include "replace-object.h"
 
-#ifndef DEBUG
-#define DEBUG 0
+#ifndef DEBUG_CACHE_TREE
+#define DEBUG_CACHE_TREE 0
 #endif
 
 struct cache_tree *cache_tree(void)
@@ -111,7 +111,7 @@
 	int namelen;
 	struct cache_tree_sub *down;
 
-#if DEBUG
+#if DEBUG_CACHE_TREE
 	fprintf(stderr, "cache-tree invalidate <%s>\n", path);
 #endif
 
@@ -398,7 +398,7 @@
 		strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
 		strbuf_add(&buffer, oid->hash, the_hash_algo->rawsz);
 
-#if DEBUG
+#if DEBUG_CACHE_TREE
 		fprintf(stderr, "cache-tree update-one %o %.*s\n",
 			mode, entlen, path + baselen);
 #endif
@@ -421,7 +421,7 @@
 
 	strbuf_release(&buffer);
 	it->entry_count = to_invalidate ? -1 : i - *skip_count;
-#if DEBUG
+#if DEBUG_CACHE_TREE
 	fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
 		it->entry_count, it->subtree_nr,
 		oid_to_hex(&it->oid));
@@ -462,7 +462,7 @@
 	strbuf_add(buffer, path, pathlen);
 	strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr);
 
-#if DEBUG
+#if DEBUG_CACHE_TREE
 	if (0 <= it->entry_count)
 		fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n",
 			pathlen, path, it->entry_count, it->subtree_nr,
@@ -536,7 +536,7 @@
 		size -= rawsz;
 	}
 
-#if DEBUG
+#if DEBUG_CACHE_TREE
 	if (0 <= it->entry_count)
 		fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n",
 			*buffer, it->entry_count, subtree_nr,
diff --git a/cache.h b/cache.h
index bf20337..3167585 100644
--- a/cache.h
+++ b/cache.h
@@ -43,30 +43,6 @@
 int git_deflate(git_zstream *, int flush);
 unsigned long git_deflate_bound(git_zstream *, unsigned long);
 
-/* 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_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];
-};
-
-#define the_hash_algo the_repository->hash_algo
-
 #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
 #define DTYPE(de)	((de)->d_type)
 #else
@@ -1759,6 +1735,7 @@
 int pager_in_use(void);
 extern int pager_use_color;
 int term_columns(void);
+void term_clear_line(void);
 int decimal_width(uintmax_t);
 int check_pager_config(const char *cmd);
 void prepare_pager_args(struct child_process *, const char *pager);
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 7f6acdd..8cc7250 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -34,7 +34,7 @@
 	popd
 	;;
 osx-clang|osx-gcc)
-	brew update >/dev/null
+	export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1
 	# Uncomment this if you want to run perf tests:
 	# brew install gnu-time
 	test -z "$BREW_INSTALL_PACKAGES" ||
diff --git a/ci/lib.sh b/ci/lib.sh
index 288a5b3..0c7171a 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -163,8 +163,10 @@
 	export GIT_TEST_HTTPD=YesPlease
 
 	# The Linux build installs the defined dependency versions below.
-	# The OS X build installs the latest available versions. Keep that
-	# in mind when you encounter a broken OS X build!
+	# The OS X build installs much more recent versions, whichever
+	# were recorded in the Homebrew database upon creating the OS X
+	# image.
+	# Keep that in mind when you encounter a broken OS X build!
 	export LINUX_P4_VERSION="16.2"
 	export LINUX_GIT_LFS_VERSION="1.5.2"
 
diff --git a/command-list.txt b/command-list.txt
index 3a9af10..a9ac72b 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -59,7 +59,7 @@
 git-check-attr                          purehelpers
 git-check-ignore                        purehelpers
 git-check-mailmap                       purehelpers
-git-checkout                            mainporcelain           history
+git-checkout                            mainporcelain
 git-checkout-index                      plumbingmanipulators
 git-check-ref-format                    purehelpers
 git-cherry                              plumbinginterrogators          complete
@@ -81,7 +81,7 @@
 git-cvsserver                           foreignscminterface
 git-daemon                              synchingrepositories
 git-describe                            mainporcelain
-git-diff                                mainporcelain           history
+git-diff                                mainporcelain           info
 git-diff-files                          plumbinginterrogators
 git-diff-index                          plumbinginterrogators
 git-diff-tree                           plumbinginterrogators
@@ -150,7 +150,8 @@
 git-replace                             ancillarymanipulators           complete
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
-git-reset                               mainporcelain           worktree
+git-reset                               mainporcelain           history
+git-restore                             mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
 git-rev-parse                           plumbinginterrogators
@@ -171,6 +172,7 @@
 git-stripspace                          purehelpers
 git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
+git-switch                              mainporcelain           history
 git-symbolic-ref                        plumbingmanipulators
 git-tag                                 mainporcelain           history
 git-unpack-file                         plumbinginterrogators
diff --git a/commit-graph.c b/commit-graph.c
index 7c5e548..8cc1d1d 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -361,10 +361,10 @@
 	return !!first_generation;
 }
 
-void close_commit_graph(struct repository *r)
+void close_commit_graph(struct raw_object_store *o)
 {
-	free_commit_graph(r->objects->commit_graph);
-	r->objects->commit_graph = NULL;
+	free_commit_graph(o->commit_graph);
+	o->commit_graph = NULL;
 }
 
 static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@ -525,14 +525,38 @@
 	return get_commit_tree_in_graph_one(r, r->objects->commit_graph, c);
 }
 
+struct packed_commit_list {
+	struct commit **list;
+	int nr;
+	int alloc;
+};
+
+struct packed_oid_list {
+	struct object_id *list;
+	int nr;
+	int alloc;
+};
+
+struct write_commit_graph_context {
+	struct repository *r;
+	const char *obj_dir;
+	char *graph_name;
+	struct packed_oid_list oids;
+	struct packed_commit_list commits;
+	int num_extra_edges;
+	unsigned long approx_nr_objects;
+	struct progress *progress;
+	int progress_done;
+	uint64_t progress_cnt;
+	unsigned append:1,
+		 report_progress:1;
+};
+
 static void write_graph_chunk_fanout(struct hashfile *f,
-				     struct commit **commits,
-				     int nr_commits,
-				     struct progress *progress,
-				     uint64_t *progress_cnt)
+				     struct write_commit_graph_context *ctx)
 {
 	int i, count = 0;
-	struct commit **list = commits;
+	struct commit **list = ctx->commits.list;
 
 	/*
 	 * Write the first-level table (the list is sorted,
@@ -540,10 +564,10 @@
 	 * having to do eight extra binary search iterations).
 	 */
 	for (i = 0; i < 256; i++) {
-		while (count < nr_commits) {
+		while (count < ctx->commits.nr) {
 			if ((*list)->object.oid.hash[0] != i)
 				break;
-			display_progress(progress, ++*progress_cnt);
+			display_progress(ctx->progress, ++ctx->progress_cnt);
 			count++;
 			list++;
 		}
@@ -553,14 +577,12 @@
 }
 
 static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
-				   struct commit **commits, int nr_commits,
-				   struct progress *progress,
-				   uint64_t *progress_cnt)
+				   struct write_commit_graph_context *ctx)
 {
-	struct commit **list = commits;
+	struct commit **list = ctx->commits.list;
 	int count;
-	for (count = 0; count < nr_commits; count++, list++) {
-		display_progress(progress, ++*progress_cnt);
+	for (count = 0; count < ctx->commits.nr; count++, list++) {
+		display_progress(ctx->progress, ++ctx->progress_cnt);
 		hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
 	}
 }
@@ -572,19 +594,17 @@
 }
 
 static void write_graph_chunk_data(struct hashfile *f, int hash_len,
-				   struct commit **commits, int nr_commits,
-				   struct progress *progress,
-				   uint64_t *progress_cnt)
+				   struct write_commit_graph_context *ctx)
 {
-	struct commit **list = commits;
-	struct commit **last = commits + nr_commits;
+	struct commit **list = ctx->commits.list;
+	struct commit **last = ctx->commits.list + ctx->commits.nr;
 	uint32_t num_extra_edges = 0;
 
 	while (list < last) {
 		struct commit_list *parent;
 		int edge_value;
 		uint32_t packedDate[2];
-		display_progress(progress, ++*progress_cnt);
+		display_progress(ctx->progress, ++ctx->progress_cnt);
 
 		parse_commit_no_graph(*list);
 		hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
@@ -595,8 +615,8 @@
 			edge_value = GRAPH_PARENT_NONE;
 		else {
 			edge_value = sha1_pos(parent->item->object.oid.hash,
-					      commits,
-					      nr_commits,
+					      ctx->commits.list,
+					      ctx->commits.nr,
 					      commit_to_sha1);
 
 			if (edge_value < 0)
@@ -616,8 +636,8 @@
 			edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
 		else {
 			edge_value = sha1_pos(parent->item->object.oid.hash,
-					      commits,
-					      nr_commits,
+					      ctx->commits.list,
+					      ctx->commits.nr,
 					      commit_to_sha1);
 			if (edge_value < 0)
 				BUG("missing parent %s for commit %s",
@@ -649,19 +669,16 @@
 }
 
 static void write_graph_chunk_extra_edges(struct hashfile *f,
-					  struct commit **commits,
-					  int nr_commits,
-					  struct progress *progress,
-					  uint64_t *progress_cnt)
+					  struct write_commit_graph_context *ctx)
 {
-	struct commit **list = commits;
-	struct commit **last = commits + nr_commits;
+	struct commit **list = ctx->commits.list;
+	struct commit **last = ctx->commits.list + ctx->commits.nr;
 	struct commit_list *parent;
 
 	while (list < last) {
 		int num_parents = 0;
 
-		display_progress(progress, ++*progress_cnt);
+		display_progress(ctx->progress, ++ctx->progress_cnt);
 
 		for (parent = (*list)->parents; num_parents < 3 && parent;
 		     parent = parent->next)
@@ -675,8 +692,8 @@
 		/* Since num_parents > 2, this initializer is safe. */
 		for (parent = (*list)->parents->next; parent; parent = parent->next) {
 			int edge_value = sha1_pos(parent->item->object.oid.hash,
-						  commits,
-						  nr_commits,
+						  ctx->commits.list,
+						  ctx->commits.nr,
 						  commit_to_sha1);
 
 			if (edge_value < 0)
@@ -700,125 +717,111 @@
 	return oidcmp(a, b);
 }
 
-struct packed_commit_list {
-	struct commit **list;
-	int nr;
-	int alloc;
-};
-
-struct packed_oid_list {
-	struct object_id *list;
-	int nr;
-	int alloc;
-	struct progress *progress;
-	int progress_done;
-};
-
 static int add_packed_commits(const struct object_id *oid,
 			      struct packed_git *pack,
 			      uint32_t pos,
 			      void *data)
 {
-	struct packed_oid_list *list = (struct packed_oid_list*)data;
+	struct write_commit_graph_context *ctx = (struct write_commit_graph_context*)data;
 	enum object_type type;
 	off_t offset = nth_packed_object_offset(pack, pos);
 	struct object_info oi = OBJECT_INFO_INIT;
 
-	if (list->progress)
-		display_progress(list->progress, ++list->progress_done);
+	if (ctx->progress)
+		display_progress(ctx->progress, ++ctx->progress_done);
 
 	oi.typep = &type;
-	if (packed_object_info(the_repository, pack, offset, &oi) < 0)
+	if (packed_object_info(ctx->r, pack, offset, &oi) < 0)
 		die(_("unable to get type of object %s"), oid_to_hex(oid));
 
 	if (type != OBJ_COMMIT)
 		return 0;
 
-	ALLOC_GROW(list->list, list->nr + 1, list->alloc);
-	oidcpy(&(list->list[list->nr]), oid);
-	list->nr++;
+	ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+	oidcpy(&(ctx->oids.list[ctx->oids.nr]), oid);
+	ctx->oids.nr++;
 
 	return 0;
 }
 
-static void add_missing_parents(struct packed_oid_list *oids, struct commit *commit)
+static void add_missing_parents(struct write_commit_graph_context *ctx, struct commit *commit)
 {
 	struct commit_list *parent;
 	for (parent = commit->parents; parent; parent = parent->next) {
 		if (!(parent->item->object.flags & UNINTERESTING)) {
-			ALLOC_GROW(oids->list, oids->nr + 1, oids->alloc);
-			oidcpy(&oids->list[oids->nr], &(parent->item->object.oid));
-			oids->nr++;
+			ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+			oidcpy(&ctx->oids.list[ctx->oids.nr], &(parent->item->object.oid));
+			ctx->oids.nr++;
 			parent->item->object.flags |= UNINTERESTING;
 		}
 	}
 }
 
-static void close_reachable(struct packed_oid_list *oids, int report_progress)
+static void close_reachable(struct write_commit_graph_context *ctx)
 {
 	int i;
 	struct commit *commit;
-	struct progress *progress = NULL;
 
-	if (report_progress)
-		progress = start_delayed_progress(
-			_("Loading known commits in commit graph"), oids->nr);
-	for (i = 0; i < oids->nr; i++) {
-		display_progress(progress, i + 1);
-		commit = lookup_commit(the_repository, &oids->list[i]);
+	if (ctx->report_progress)
+		ctx->progress = start_delayed_progress(
+					_("Loading known commits in commit graph"),
+					ctx->oids.nr);
+	for (i = 0; i < ctx->oids.nr; i++) {
+		display_progress(ctx->progress, i + 1);
+		commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
 		if (commit)
 			commit->object.flags |= UNINTERESTING;
 	}
-	stop_progress(&progress);
+	stop_progress(&ctx->progress);
 
 	/*
-	 * As this loop runs, oids->nr may grow, but not more
+	 * As this loop runs, ctx->oids.nr may grow, but not more
 	 * than the number of missing commits in the reachable
 	 * closure.
 	 */
-	if (report_progress)
-		progress = start_delayed_progress(
-			_("Expanding reachable commits in commit graph"), oids->nr);
-	for (i = 0; i < oids->nr; i++) {
-		display_progress(progress, i + 1);
-		commit = lookup_commit(the_repository, &oids->list[i]);
+	if (ctx->report_progress)
+		ctx->progress = start_delayed_progress(
+					_("Expanding reachable commits in commit graph"),
+					ctx->oids.nr);
+	for (i = 0; i < ctx->oids.nr; i++) {
+		display_progress(ctx->progress, i + 1);
+		commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
 
 		if (commit && !parse_commit_no_graph(commit))
-			add_missing_parents(oids, commit);
+			add_missing_parents(ctx, commit);
 	}
-	stop_progress(&progress);
+	stop_progress(&ctx->progress);
 
-	if (report_progress)
-		progress = start_delayed_progress(
-			_("Clearing commit marks in commit graph"), oids->nr);
-	for (i = 0; i < oids->nr; i++) {
-		display_progress(progress, i + 1);
-		commit = lookup_commit(the_repository, &oids->list[i]);
+	if (ctx->report_progress)
+		ctx->progress = start_delayed_progress(
+					_("Clearing commit marks in commit graph"),
+					ctx->oids.nr);
+	for (i = 0; i < ctx->oids.nr; i++) {
+		display_progress(ctx->progress, i + 1);
+		commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
 
 		if (commit)
 			commit->object.flags &= ~UNINTERESTING;
 	}
-	stop_progress(&progress);
+	stop_progress(&ctx->progress);
 }
 
-static void compute_generation_numbers(struct packed_commit_list* commits,
-				       int report_progress)
+static void compute_generation_numbers(struct write_commit_graph_context *ctx)
 {
 	int i;
 	struct commit_list *list = NULL;
-	struct progress *progress = NULL;
 
-	if (report_progress)
-		progress = start_progress(
-			_("Computing commit graph generation numbers"),
-			commits->nr);
-	for (i = 0; i < commits->nr; i++) {
-		display_progress(progress, i + 1);
-		if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
-		    commits->list[i]->generation != GENERATION_NUMBER_ZERO)
+	if (ctx->report_progress)
+		ctx->progress = start_progress(
+					_("Computing commit graph generation numbers"),
+					ctx->commits.nr);
+	for (i = 0; i < ctx->commits.nr; i++) {
+		display_progress(ctx->progress, i + 1);
+		if (ctx->commits.list[i]->generation != GENERATION_NUMBER_INFINITY &&
+		    ctx->commits.list[i]->generation != GENERATION_NUMBER_ZERO)
 			continue;
 
-		commit_list_insert(commits->list[i], &list);
+		commit_list_insert(ctx->commits.list[i], &list);
 		while (list) {
 			struct commit *current = list->item;
 			struct commit_list *parent;
@@ -845,7 +848,7 @@
 			}
 		}
 	}
-	stop_progress(&progress);
+	stop_progress(&ctx->progress);
 }
 
 static int add_ref_to_list(const char *refname,
@@ -858,207 +861,187 @@
 	return 0;
 }
 
-void write_commit_graph_reachable(const char *obj_dir, int append,
-				  int report_progress)
+int write_commit_graph_reachable(const char *obj_dir, unsigned int flags)
 {
 	struct string_list list = STRING_LIST_INIT_DUP;
+	int result;
 
 	for_each_ref(add_ref_to_list, &list);
-	write_commit_graph(obj_dir, NULL, &list, append, report_progress);
+	result = write_commit_graph(obj_dir, NULL, &list,
+				    flags);
 
 	string_list_clear(&list, 0);
+	return result;
 }
 
-void write_commit_graph(const char *obj_dir,
-			struct string_list *pack_indexes,
-			struct string_list *commit_hex,
-			int append, int report_progress)
+static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
+				struct string_list *pack_indexes)
 {
-	struct packed_oid_list oids;
-	struct packed_commit_list commits;
-	struct hashfile *f;
-	uint32_t i, count_distinct = 0;
-	char *graph_name;
-	struct lock_file lk = LOCK_INIT;
-	uint32_t chunk_ids[5];
-	uint64_t chunk_offsets[5];
-	int num_chunks;
-	int num_extra_edges;
-	struct commit_list *parent;
-	struct progress *progress = NULL;
-	const unsigned hashsz = the_hash_algo->rawsz;
-	uint64_t progress_cnt = 0;
+	uint32_t i;
 	struct strbuf progress_title = STRBUF_INIT;
-	unsigned long approx_nr_objects;
+	struct strbuf packname = STRBUF_INIT;
+	int dirlen;
 
-	if (!commit_graph_compatible(the_repository))
-		return;
-
-	oids.nr = 0;
-	approx_nr_objects = approximate_object_count();
-	oids.alloc = approx_nr_objects / 32;
-	oids.progress = NULL;
-	oids.progress_done = 0;
-
-	if (append) {
-		prepare_commit_graph_one(the_repository, obj_dir);
-		if (the_repository->objects->commit_graph)
-			oids.alloc += the_repository->objects->commit_graph->num_commits;
+	strbuf_addf(&packname, "%s/pack/", ctx->obj_dir);
+	dirlen = packname.len;
+	if (ctx->report_progress) {
+		strbuf_addf(&progress_title,
+			    Q_("Finding commits for commit graph in %d pack",
+			       "Finding commits for commit graph in %d packs",
+			       pack_indexes->nr),
+			    pack_indexes->nr);
+		ctx->progress = start_delayed_progress(progress_title.buf, 0);
+		ctx->progress_done = 0;
 	}
-
-	if (oids.alloc < 1024)
-		oids.alloc = 1024;
-	ALLOC_ARRAY(oids.list, oids.alloc);
-
-	if (append && the_repository->objects->commit_graph) {
-		struct commit_graph *commit_graph =
-			the_repository->objects->commit_graph;
-		for (i = 0; i < commit_graph->num_commits; i++) {
-			const unsigned char *hash = commit_graph->chunk_oid_lookup +
-				commit_graph->hash_len * i;
-			hashcpy(oids.list[oids.nr++].hash, hash);
+	for (i = 0; i < pack_indexes->nr; i++) {
+		struct packed_git *p;
+		strbuf_setlen(&packname, dirlen);
+		strbuf_addstr(&packname, pack_indexes->items[i].string);
+		p = add_packed_git(packname.buf, packname.len, 1);
+		if (!p) {
+			error(_("error adding pack %s"), packname.buf);
+			return -1;
 		}
-	}
-
-	if (pack_indexes) {
-		struct strbuf packname = STRBUF_INIT;
-		int dirlen;
-		strbuf_addf(&packname, "%s/pack/", obj_dir);
-		dirlen = packname.len;
-		if (report_progress) {
-			strbuf_addf(&progress_title,
-				    Q_("Finding commits for commit graph in %d pack",
-				       "Finding commits for commit graph in %d packs",
-				       pack_indexes->nr),
-				    pack_indexes->nr);
-			oids.progress = start_delayed_progress(progress_title.buf, 0);
-			oids.progress_done = 0;
+		if (open_pack_index(p)) {
+			error(_("error opening index for %s"), packname.buf);
+			return -1;
 		}
-		for (i = 0; i < pack_indexes->nr; i++) {
-			struct packed_git *p;
-			strbuf_setlen(&packname, dirlen);
-			strbuf_addstr(&packname, pack_indexes->items[i].string);
-			p = add_packed_git(packname.buf, packname.len, 1);
-			if (!p)
-				die(_("error adding pack %s"), packname.buf);
-			if (open_pack_index(p))
-				die(_("error opening index for %s"), packname.buf);
-			for_each_object_in_pack(p, add_packed_commits, &oids,
-						FOR_EACH_OBJECT_PACK_ORDER);
-			close_pack(p);
-			free(p);
-		}
-		stop_progress(&oids.progress);
-		strbuf_reset(&progress_title);
-		strbuf_release(&packname);
+		for_each_object_in_pack(p, add_packed_commits, ctx,
+					FOR_EACH_OBJECT_PACK_ORDER);
+		close_pack(p);
+		free(p);
 	}
 
-	if (commit_hex) {
-		if (report_progress) {
-			strbuf_addf(&progress_title,
-				    Q_("Finding commits for commit graph from %d ref",
-				       "Finding commits for commit graph from %d refs",
-				       commit_hex->nr),
-				    commit_hex->nr);
-			progress = start_delayed_progress(progress_title.buf,
-							  commit_hex->nr);
-		}
-		for (i = 0; i < commit_hex->nr; i++) {
-			const char *end;
-			struct object_id oid;
-			struct commit *result;
+	stop_progress(&ctx->progress);
+	strbuf_reset(&progress_title);
+	strbuf_release(&packname);
 
-			display_progress(progress, i + 1);
-			if (commit_hex->items[i].string &&
-			    parse_oid_hex(commit_hex->items[i].string, &oid, &end))
-				continue;
+	return 0;
+}
 
-			result = lookup_commit_reference_gently(the_repository, &oid, 1);
+static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
+				      struct string_list *commit_hex)
+{
+	uint32_t i;
+	struct strbuf progress_title = STRBUF_INIT;
 
-			if (result) {
-				ALLOC_GROW(oids.list, oids.nr + 1, oids.alloc);
-				oidcpy(&oids.list[oids.nr], &(result->object.oid));
-				oids.nr++;
-			}
-		}
-		stop_progress(&progress);
-		strbuf_reset(&progress_title);
+	if (ctx->report_progress) {
+		strbuf_addf(&progress_title,
+			    Q_("Finding commits for commit graph from %d ref",
+			       "Finding commits for commit graph from %d refs",
+			       commit_hex->nr),
+			    commit_hex->nr);
+		ctx->progress = start_delayed_progress(
+					progress_title.buf,
+					commit_hex->nr);
 	}
+	for (i = 0; i < commit_hex->nr; i++) {
+		const char *end;
+		struct object_id oid;
+		struct commit *result;
 
-	if (!pack_indexes && !commit_hex) {
-		if (report_progress)
-			oids.progress = start_delayed_progress(
-				_("Finding commits for commit graph among packed objects"),
-				approx_nr_objects);
-		for_each_packed_object(add_packed_commits, &oids,
-				       FOR_EACH_OBJECT_PACK_ORDER);
-		if (oids.progress_done < approx_nr_objects)
-			display_progress(oids.progress, approx_nr_objects);
-		stop_progress(&oids.progress);
-	}
-
-	close_reachable(&oids, report_progress);
-
-	if (report_progress)
-		progress = start_delayed_progress(
-			_("Counting distinct commits in commit graph"),
-			oids.nr);
-	display_progress(progress, 0); /* TODO: Measure QSORT() progress */
-	QSORT(oids.list, oids.nr, commit_compare);
-	count_distinct = 1;
-	for (i = 1; i < oids.nr; i++) {
-		display_progress(progress, i + 1);
-		if (!oideq(&oids.list[i - 1], &oids.list[i]))
-			count_distinct++;
-	}
-	stop_progress(&progress);
-
-	if (count_distinct >= GRAPH_EDGE_LAST_MASK)
-		die(_("the commit graph format cannot write %d commits"), count_distinct);
-
-	commits.nr = 0;
-	commits.alloc = count_distinct;
-	ALLOC_ARRAY(commits.list, commits.alloc);
-
-	num_extra_edges = 0;
-	if (report_progress)
-		progress = start_delayed_progress(
-			_("Finding extra edges in commit graph"),
-			oids.nr);
-	for (i = 0; i < oids.nr; i++) {
-		int num_parents = 0;
-		display_progress(progress, i + 1);
-		if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i]))
+		display_progress(ctx->progress, i + 1);
+		if (commit_hex->items[i].string &&
+		    parse_oid_hex(commit_hex->items[i].string, &oid, &end))
 			continue;
 
-		commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]);
-		parse_commit_no_graph(commits.list[commits.nr]);
+		result = lookup_commit_reference_gently(ctx->r, &oid, 1);
 
-		for (parent = commits.list[commits.nr]->parents;
+		if (result) {
+			ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+			oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid));
+			ctx->oids.nr++;
+		}
+	}
+	stop_progress(&ctx->progress);
+	strbuf_release(&progress_title);
+}
+
+static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
+{
+	if (ctx->report_progress)
+		ctx->progress = start_delayed_progress(
+			_("Finding commits for commit graph among packed objects"),
+			ctx->approx_nr_objects);
+	for_each_packed_object(add_packed_commits, ctx,
+			       FOR_EACH_OBJECT_PACK_ORDER);
+	if (ctx->progress_done < ctx->approx_nr_objects)
+		display_progress(ctx->progress, ctx->approx_nr_objects);
+	stop_progress(&ctx->progress);
+}
+
+static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
+{
+	uint32_t i, count_distinct = 1;
+
+	if (ctx->report_progress)
+		ctx->progress = start_delayed_progress(
+			_("Counting distinct commits in commit graph"),
+			ctx->oids.nr);
+	display_progress(ctx->progress, 0); /* TODO: Measure QSORT() progress */
+	QSORT(ctx->oids.list, ctx->oids.nr, commit_compare);
+
+	for (i = 1; i < ctx->oids.nr; i++) {
+		display_progress(ctx->progress, i + 1);
+		if (!oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
+			count_distinct++;
+	}
+	stop_progress(&ctx->progress);
+
+	return count_distinct;
+}
+
+static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
+{
+	uint32_t i;
+	struct commit_list *parent;
+
+	ctx->num_extra_edges = 0;
+	if (ctx->report_progress)
+		ctx->progress = start_delayed_progress(
+			_("Finding extra edges in commit graph"),
+			ctx->oids.nr);
+	for (i = 0; i < ctx->oids.nr; i++) {
+		int num_parents = 0;
+		display_progress(ctx->progress, i + 1);
+		if (i > 0 && oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
+			continue;
+
+		ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
+		parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
+
+		for (parent = ctx->commits.list[ctx->commits.nr]->parents;
 		     parent; parent = parent->next)
 			num_parents++;
 
 		if (num_parents > 2)
-			num_extra_edges += num_parents - 1;
+			ctx->num_extra_edges += num_parents - 1;
 
-		commits.nr++;
+		ctx->commits.nr++;
 	}
-	num_chunks = num_extra_edges ? 4 : 3;
-	stop_progress(&progress);
+	stop_progress(&ctx->progress);
+}
 
-	if (commits.nr >= GRAPH_EDGE_LAST_MASK)
-		die(_("too many commits to write graph"));
+static int write_commit_graph_file(struct write_commit_graph_context *ctx)
+{
+	uint32_t i;
+	struct hashfile *f;
+	struct lock_file lk = LOCK_INIT;
+	uint32_t chunk_ids[5];
+	uint64_t chunk_offsets[5];
+	const unsigned hashsz = the_hash_algo->rawsz;
+	struct strbuf progress_title = STRBUF_INIT;
+	int num_chunks = ctx->num_extra_edges ? 4 : 3;
 
-	compute_generation_numbers(&commits, report_progress);
-
-	graph_name = get_commit_graph_filename(obj_dir);
-	if (safe_create_leading_directories(graph_name)) {
-		UNLEAK(graph_name);
-		die_errno(_("unable to create leading directories of %s"),
-			  graph_name);
+	ctx->graph_name = get_commit_graph_filename(ctx->obj_dir);
+	if (safe_create_leading_directories(ctx->graph_name)) {
+		UNLEAK(ctx->graph_name);
+		error(_("unable to create leading directories of %s"),
+			ctx->graph_name);
+		return -1;
 	}
 
-	hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
+	hold_lock_file_for_update(&lk, ctx->graph_name, LOCK_DIE_ON_ERROR);
 	f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
 
 	hashwrite_be32(f, GRAPH_SIGNATURE);
@@ -1071,7 +1054,7 @@
 	chunk_ids[0] = GRAPH_CHUNKID_OIDFANOUT;
 	chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
 	chunk_ids[2] = GRAPH_CHUNKID_DATA;
-	if (num_extra_edges)
+	if (ctx->num_extra_edges)
 		chunk_ids[3] = GRAPH_CHUNKID_EXTRAEDGES;
 	else
 		chunk_ids[3] = 0;
@@ -1079,9 +1062,9 @@
 
 	chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
 	chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
-	chunk_offsets[2] = chunk_offsets[1] + hashsz * commits.nr;
-	chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * commits.nr;
-	chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
+	chunk_offsets[2] = chunk_offsets[1] + hashsz * ctx->commits.nr;
+	chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * ctx->commits.nr;
+	chunk_offsets[4] = chunk_offsets[3] + 4 * ctx->num_extra_edges;
 
 	for (i = 0; i <= num_chunks; i++) {
 		uint32_t chunk_write[3];
@@ -1092,31 +1075,113 @@
 		hashwrite(f, chunk_write, 12);
 	}
 
-	if (report_progress) {
+	if (ctx->report_progress) {
 		strbuf_addf(&progress_title,
 			    Q_("Writing out commit graph in %d pass",
 			       "Writing out commit graph in %d passes",
 			       num_chunks),
 			    num_chunks);
-		progress = start_delayed_progress(
+		ctx->progress = start_delayed_progress(
 			progress_title.buf,
-			num_chunks * commits.nr);
+			num_chunks * ctx->commits.nr);
 	}
-	write_graph_chunk_fanout(f, commits.list, commits.nr, progress, &progress_cnt);
-	write_graph_chunk_oids(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
-	write_graph_chunk_data(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
-	if (num_extra_edges)
-		write_graph_chunk_extra_edges(f, commits.list, commits.nr, progress, &progress_cnt);
-	stop_progress(&progress);
+	write_graph_chunk_fanout(f, ctx);
+	write_graph_chunk_oids(f, hashsz, ctx);
+	write_graph_chunk_data(f, hashsz, ctx);
+	if (ctx->num_extra_edges)
+		write_graph_chunk_extra_edges(f, ctx);
+	stop_progress(&ctx->progress);
 	strbuf_release(&progress_title);
 
-	close_commit_graph(the_repository);
+	close_commit_graph(ctx->r->objects);
 	finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
 	commit_lock_file(&lk);
 
-	free(graph_name);
-	free(commits.list);
-	free(oids.list);
+	return 0;
+}
+
+int write_commit_graph(const char *obj_dir,
+		       struct string_list *pack_indexes,
+		       struct string_list *commit_hex,
+		       unsigned int flags)
+{
+	struct write_commit_graph_context *ctx;
+	uint32_t i, count_distinct = 0;
+	int res = 0;
+
+	if (!commit_graph_compatible(the_repository))
+		return 0;
+
+	ctx = xcalloc(1, sizeof(struct write_commit_graph_context));
+	ctx->r = the_repository;
+	ctx->obj_dir = obj_dir;
+	ctx->append = flags & COMMIT_GRAPH_APPEND ? 1 : 0;
+	ctx->report_progress = flags & COMMIT_GRAPH_PROGRESS ? 1 : 0;
+
+	ctx->approx_nr_objects = approximate_object_count();
+	ctx->oids.alloc = ctx->approx_nr_objects / 32;
+
+	if (ctx->append) {
+		prepare_commit_graph_one(ctx->r, ctx->obj_dir);
+		if (ctx->r->objects->commit_graph)
+			ctx->oids.alloc += ctx->r->objects->commit_graph->num_commits;
+	}
+
+	if (ctx->oids.alloc < 1024)
+		ctx->oids.alloc = 1024;
+	ALLOC_ARRAY(ctx->oids.list, ctx->oids.alloc);
+
+	if (ctx->append && ctx->r->objects->commit_graph) {
+		struct commit_graph *g = ctx->r->objects->commit_graph;
+		for (i = 0; i < g->num_commits; i++) {
+			const unsigned char *hash = g->chunk_oid_lookup + g->hash_len * i;
+			hashcpy(ctx->oids.list[ctx->oids.nr++].hash, hash);
+		}
+	}
+
+	if (pack_indexes) {
+		if ((res = fill_oids_from_packs(ctx, pack_indexes)))
+			goto cleanup;
+	}
+
+	if (commit_hex)
+		fill_oids_from_commit_hex(ctx, commit_hex);
+
+	if (!pack_indexes && !commit_hex)
+		fill_oids_from_all_packs(ctx);
+
+	close_reachable(ctx);
+
+	count_distinct = count_distinct_commits(ctx);
+
+	if (count_distinct >= GRAPH_EDGE_LAST_MASK) {
+		error(_("the commit graph format cannot write %d commits"), count_distinct);
+		res = -1;
+		goto cleanup;
+	}
+
+	ctx->commits.alloc = count_distinct;
+	ALLOC_ARRAY(ctx->commits.list, ctx->commits.alloc);
+
+	copy_oids_to_commits(ctx);
+
+	if (ctx->commits.nr >= GRAPH_EDGE_LAST_MASK) {
+		error(_("too many commits to write graph"));
+		res = -1;
+		goto cleanup;
+	}
+
+	compute_generation_numbers(ctx);
+
+	res = write_commit_graph_file(ctx);
+
+cleanup:
+	free(ctx->graph_name);
+	free(ctx->commits.list);
+	free(ctx->oids.list);
+	free(ctx);
+
+	return res;
 }
 
 #define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
@@ -1214,7 +1279,7 @@
 		hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
 
 		graph_commit = lookup_commit(r, &cur_oid);
-		odb_commit = (struct commit *)create_object(r, cur_oid.hash, alloc_commit_node(r));
+		odb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r));
 		if (parse_commit_internal(odb_commit, 0, 0)) {
 			graph_report(_("failed to parse commit %s from object database for commit-graph"),
 				     oid_to_hex(&cur_oid));
diff --git a/commit-graph.h b/commit-graph.h
index 7dfb8c8..390c7f6 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -65,16 +65,24 @@
  */
 int generation_numbers_enabled(struct repository *r);
 
-void write_commit_graph_reachable(const char *obj_dir, int append,
-				  int report_progress);
-void write_commit_graph(const char *obj_dir,
-			struct string_list *pack_indexes,
-			struct string_list *commit_hex,
-			int append, int report_progress);
+#define COMMIT_GRAPH_APPEND     (1 << 0)
+#define COMMIT_GRAPH_PROGRESS   (1 << 1)
+
+/*
+ * The write_commit_graph* methods return zero on success
+ * and a negative value on failure. Note that if the repository
+ * is not compatible with the commit-graph feature, then the
+ * methods will return 0 without writing a commit-graph.
+ */
+int write_commit_graph_reachable(const char *obj_dir, unsigned int flags);
+int write_commit_graph(const char *obj_dir,
+		       struct string_list *pack_indexes,
+		       struct string_list *commit_hex,
+		       unsigned int flags);
 
 int verify_commit_graph(struct repository *r, struct commit_graph *g);
 
-void close_commit_graph(struct repository *);
+void close_commit_graph(struct raw_object_store *);
 void free_commit_graph(struct commit_graph *);
 
 #endif
diff --git a/commit.c b/commit.c
index 8fa1883..a98de16 100644
--- a/commit.c
+++ b/commit.c
@@ -57,10 +57,9 @@
 
 struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
 {
-	struct object *obj = lookup_object(r, oid->hash);
+	struct object *obj = lookup_object(r, oid);
 	if (!obj)
-		return create_object(r, oid->hash,
-				     alloc_commit_node(r));
+		return create_object(r, oid, alloc_commit_node(r));
 	return object_as_type(r, obj, OBJ_COMMIT, 0);
 }
 
@@ -449,7 +448,7 @@
 	item->date = parse_commit_date(bufptr, tail);
 
 	if (check_graph)
-		load_commit_graph_info(the_repository, item);
+		load_commit_graph_info(r, item);
 
 	return 0;
 }
diff --git a/compat/mingw.c b/compat/mingw.c
index a0eb695..4891789 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1553,7 +1553,10 @@
 	if (prog) {
 		int exec_id;
 		int argc = 0;
-		const char **argv2;
+#ifndef _MSC_VER
+		const
+#endif
+		char **argv2;
 		while (argv[argc]) argc++;
 		ALLOC_ARRAY(argv2, argc + 1);
 		argv2[0] = (char *)cmd;	/* full path to the script file */
@@ -2122,8 +2125,33 @@
 			sigint_fn(SIGINT);
 		return 0;
 
+#if defined(_MSC_VER)
+	case SIGILL:
+	case SIGFPE:
+	case SIGSEGV:
+	case SIGTERM:
+	case SIGBREAK:
+	case SIGABRT:
+	case SIGABRT_COMPAT:
+		/*
+		 * The <signal.h> header in the MS C Runtime defines 8 signals
+		 * as being supported on the platform. Anything else causes an
+		 * "Invalid signal or error" (which in DEBUG builds causes the
+		 * Abort/Retry/Ignore dialog). We by-pass the CRT for things we
+		 * already know will fail.
+		 */
+		return raise(sig);
+	default:
+		errno = EINVAL;
+		return -1;
+
+#else
+
 	default:
 		return raise(sig);
+
+#endif
+
 	}
 }
 
@@ -2307,18 +2335,13 @@
 		setenv("TERM", "cygwin", 1);
 }
 
+#if !defined(_MSC_VER)
 /*
  * Disable MSVCRT command line wildcard expansion (__getmainargs called from
  * mingw startup code, see init.c in mingw runtime).
  */
 int _CRT_glob = 0;
-
-typedef struct {
-	int newmode;
-} _startupinfo;
-
-extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
-		_startupinfo *si);
+#endif
 
 static NORETURN void die_startup(void)
 {
@@ -2396,21 +2419,40 @@
 				  GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
 }
 
-void mingw_startup(void)
+#ifdef _MSC_VER
+#ifdef _DEBUG
+#include <crtdbg.h>
+#endif
+#endif
+
+/*
+ * 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 main().
+ */
+int wmain(int argc, const wchar_t **wargv)
 {
-	int i, maxlen, argc;
-	char *buffer;
-	wchar_t **wenv, **wargv;
-	_startupinfo si;
+	int i, maxlen, exit_status;
+	char *buffer, **save;
+	const char **argv;
 
 	trace2_initialize_clock();
 
-	maybe_redirect_std_handles();
+#ifdef _MSC_VER
+#ifdef _DEBUG
+	_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
+#endif
 
-	/* get wide char arguments and environment */
-	si.newmode = 0;
-	if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
-		die_startup();
+#ifdef USE_MSVC_CRTDBG
+	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+#endif
+#endif
+
+	maybe_redirect_std_handles();
 
 	/* determine size of argv and environ conversion buffer */
 	maxlen = wcslen(wargv[0]);
@@ -2421,9 +2463,16 @@
 	maxlen = 3 * maxlen + 1;
 	buffer = malloc_startup(maxlen);
 
-	/* convert command line arguments and environment to UTF-8 */
+	/*
+	 * Create a UTF-8 version of w_argv. Also create a "save" copy
+	 * to remember all the string pointers because parse_options()
+	 * will remove claimed items from the argv that we pass down.
+	 */
+	ALLOC_ARRAY(argv, argc + 1);
+	ALLOC_ARRAY(save, argc + 1);
 	for (i = 0; i < argc; i++)
-		__argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
+		argv[i] = save[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
+	argv[i] = save[i] = NULL;
 	free(buffer);
 
 	/* fix Windows specific environment settings */
@@ -2442,6 +2491,16 @@
 
 	/* initialize Unicode console */
 	winansi_init();
+
+	/* invoke the real main() using our utf8 version of argv. */
+	exit_status = main(argc, argv);
+
+	for (i = 0; i < argc; i++)
+		free(save[i]);
+	free(save);
+	free(argv);
+
+	return exit_status;
 }
 
 int uname(struct utsname *buf)
diff --git a/compat/mingw.h b/compat/mingw.h
index 593bdbf..a03e40e 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -352,11 +352,13 @@
 #ifndef __MINGW64_VERSION_MAJOR
 #define off_t off64_t
 #define lseek _lseeki64
+#ifndef _MSC_VER
 struct timespec {
 	time_t tv_sec;
 	long tv_nsec;
 };
 #endif
+#endif
 
 struct mingw_stat {
     _dev_t st_dev;
@@ -562,18 +564,18 @@
 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.
+ *
+ * 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.
  */
-
-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)
+int wmain(int argc, const wchar_t **w_argv);
+int main(int argc, const char **argv);
 
 /*
  * Used by Pthread API implementation for Windows
diff --git a/compat/msvc.h b/compat/msvc.h
index 29a8ce8..1d7a8c6 100644
--- a/compat/msvc.h
+++ b/compat/msvc.h
@@ -6,6 +6,10 @@
 #include <malloc.h>
 #include <io.h>
 
+#pragma warning(disable: 4018) /* signed/unsigned comparison */
+#pragma warning(disable: 4244) /* type conversion, possible loss of data */
+#pragma warning(disable: 4090) /* 'function' : different 'const' qualifiers (ALLOC_GROW etc.)*/
+
 /* porting function */
 #define inline __inline
 #define __inline__ __inline
@@ -18,6 +22,12 @@
 
 #undef ERROR
 
+#define ftello _ftelli64
+
+typedef int sigset_t;
+/* open for reading, writing, or both (not in fcntl.h) */
+#define O_ACCMODE     (_O_RDONLY | _O_WRONLY | _O_RDWR)
+
 #include "compat/mingw.h"
 
 #endif
diff --git a/compat/obstack.h b/compat/obstack.h
index ced94d0..ae36ed6 100644
--- a/compat/obstack.h
+++ b/compat/obstack.h
@@ -496,7 +496,7 @@
 ( (h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk,		\
   ((((h)->temp.tempint > 0						\
     && (h)->temp.tempint < (h)->chunk_limit - (char *) (h)->chunk))	\
-   ? (int) ((h)->next_free = (h)->object_base				\
+   ? (ptrdiff_t) ((h)->next_free = (h)->object_base				\
 	    = (h)->temp.tempint + (char *) (h)->chunk)			\
    : (((obstack_free) ((h), (h)->temp.tempint + (char *) (h)->chunk), 0), 0)))
 
diff --git a/compat/poll/poll.c b/compat/poll/poll.c
index 8f24b80..0e95dd4 100644
--- a/compat/poll/poll.c
+++ b/compat/poll/poll.c
@@ -149,7 +149,7 @@
     case FILE_TYPE_PIPE:
       if (!once_only)
 	{
-	  NtQueryInformationFile = (PNtQueryInformationFile)
+	  NtQueryInformationFile = (PNtQueryInformationFile)(void (*)(void))
 	    GetProcAddress (GetModuleHandleW (L"ntdll.dll"),
 			    "NtQueryInformationFile");
 	  once_only = TRUE;
diff --git a/compat/vcbuild/.gitignore b/compat/vcbuild/.gitignore
new file mode 100644
index 0000000..8f8b794
--- /dev/null
+++ b/compat/vcbuild/.gitignore
@@ -0,0 +1,3 @@
+/vcpkg/
+/MSVC-DEFS-GEN
+/VCPKG-DEFS
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
index 60fd873..b633e7d 100644
--- a/compat/vcbuild/README
+++ b/compat/vcbuild/README
@@ -1,3 +1,42 @@
+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 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:
+
+   $ cd <repo_root>
+   $ ./compat/vcbuild/vcpkg_install.bat
+
+   The vcpkg tools and all of the third-party sources will be installed
+   in this folder:
+      <repo_root>/compat/vcbuild/vcpkg/
+
+   A file will be created with a set of Makefile macros pointing to a
+   unified "include", "lib", and "bin" directory (release and debug) for
+   all of the required packages.  This file will be included by the main
+   Makefile:
+      <repo_root>/compat/vcbuild/MSVC-DEFS-GEN
+
+2. OPTIONALLY copy the third-party *.dll and *.pdb files into the repo
+   root to make it easier to run and debug git.exe without having to
+   manipulate your PATH.  This is especially true for debug sessions in
+   Visual Studio.
+
+   Use ONE of the following forms which should match how you want to
+   compile git.exe.
+
+   $ ./compat/vcbuild/vcpkg_copy_packages.bat debug
+   $ ./compat/vcbuild/vcpkg_copy_packages.bat release
+
+3. Build git using MSVC from an SDK bash window using one of the
+   following commands:
+
+   $ make MSVC=1
+   $ make MSVC=1 DEBUG=1
+
+================================================================
+
 The Steps of Build Git with VS2008
 
 1. You need the build environment, which contains the Git dependencies
diff --git a/compat/vcbuild/find_vs_env.bat b/compat/vcbuild/find_vs_env.bat
new file mode 100644
index 0000000..40194dd
--- /dev/null
+++ b/compat/vcbuild/find_vs_env.bat
@@ -0,0 +1,168 @@
+@ECHO OFF
+REM ================================================================
+REM You can use either GCC (the default) or MSVC to build git
+REM using the GIT-SDK command line tools.
+REM        $ make
+REM        $ make MSVC=1
+REM
+REM GIT-SDK BASH windows inherit environment variables with all of
+REM the bin/lib/include paths for GCC.  It DOES NOT inherit values
+REM for the corresponding MSVC tools.
+REM
+REM During normal (non-git) Windows development, you launch one
+REM of the provided "developer command prompts" to set environment
+REM variables for the MSVC tools.
+REM
+REM Therefore, to allow MSVC command line builds of git from BASH
+REM and MAKE, we must blend these two different worlds.  This script
+REM attempts to do that.
+REM ================================================================
+REM This BAT file starts in a plain (non-developer) command prompt,
+REM searches for the "best" commmand prompt setup script, installs
+REM it into the current CMD process, and exports the various MSVC
+REM environment variables for use by MAKE.
+REM
+REM The output of this script should be written to a make "include
+REM file" and referenced by the top-level Makefile.
+REM
+REM See "config.mak.uname" (look for compat/vcbuild/MSVC-DEFS-GEN).
+REM ================================================================
+REM The provided command prompts are custom to each VS release and
+REM filled with lots of internal knowledge (such as Registry settings);
+REM even their names vary by release, so it is not appropriate for us
+REM to look inside them.  Rather, just run them in a subordinate
+REM process and extract the settings we need.
+REM ================================================================
+REM
+REM Current (VS2017 and beyond)
+REM -------------------
+REM Visual Studio 2017 introduced a new installation layout and
+REM support for side-by-side installation of multiple versions of
+REM VS2017.  Furthermore, these can all coexist with installations
+REM of previous versions of VS (which have a completely different
+REM layout on disk).
+REM
+REM VS2017 Update 2 introduced a "vswhere.exe" command:
+REM https://github.com/Microsoft/vswhere
+REM https://blogs.msdn.microsoft.com/heaths/2017/02/25/vswhere-available/
+REM https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
+REM
+REM VS2015
+REM ------
+REM Visual Studio 2015 uses the traditional VcVarsAll.
+REM
+REM Earlier Versions
+REM ----------------
+REM Currently unsupported.
+REM
+REM ================================================================
+REM Note: Throughout this script we use "dir <path> && <cmd>" rather
+REM than "if exist <path>" because of script problems with pathnames
+REM containing spaces.
+REM ================================================================
+
+REM Sanitize PATH to prevent git-sdk paths from confusing "wmic.exe"
+REM (called internally in some of the system BAT files).
+SET PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;
+
+REM ================================================================
+
+:current
+   SET vs_where=C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe
+   dir "%vs_where%" >nul 2>nul && GOTO have_vs_where
+   GOTO not_2017
+
+:have_vs_where
+   REM Try to use VsWhere to get the location of VsDevCmd.
+
+   REM Keep VsDevCmd from cd'ing away.
+   SET VSCMD_START_DIR=.
+
+   REM Get the root of the VS product installation.
+   FOR /F "usebackq tokens=*" %%i IN (`"%vs_where%" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath`) DO @SET vs_ip=%%i
+
+   SET vs_devcmd=%vs_ip%\Common7\Tools\VsDevCmd.bat
+   dir "%vs_devcmd%" >nul 2>nul && GOTO have_vs_devcmd
+   GOTO not_2017
+
+:have_vs_devcmd
+   REM Use VsDevCmd to setup the environment of this process.
+   REM Setup CL for building 64-bit apps using 64-bit tools.
+   @call "%vs_devcmd%" -no_logo -arch=x64 -host_arch=x64
+
+   SET tgt=%VSCMD_ARG_TGT_ARCH%
+
+   SET mn=%VCToolsInstallDir%
+   SET msvc_includes=-I"%mn%INCLUDE"
+   SET msvc_libs=-L"%mn%lib\%tgt%"
+   SET msvc_bin_dir=%mn%bin\Host%VSCMD_ARG_HOST_ARCH%\%tgt%
+
+   SET sdk_dir=%WindowsSdkDir%
+   SET sdk_ver=%WindowsSDKVersion%
+   SET si=%sdk_dir%Include\%sdk_ver%
+   SET sdk_includes=-I"%si%ucrt" -I"%si%um" -I"%si%shared"
+   SET sl=%sdk_dir%lib\%sdk_ver%
+   SET sdk_libs=-L"%sl%ucrt\%tgt%" -L"%sl%um\%tgt%"
+
+   SET vs_ver=%VisualStudioVersion%
+
+   GOTO print_vars
+
+REM ================================================================
+
+:not_2017
+   REM See if VS2015 is installed.
+
+   SET vs_2015_bat=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
+   dir "%vs_2015_bat%" >nul 2>nul && GOTO have_vs_2015
+   GOTO not_2015
+
+:have_vs_2015
+   REM Use VcVarsAll like the "x64 Native" command prompt.
+   REM Setup CL for building 64-bit apps using 64-bit tools.
+   @call "%vs_2015_bat%" amd64
+
+   REM Note that in VS2015 they use "x64" in some contexts and "amd64" in others.
+   SET mn=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\
+   SET msvc_includes=-I"%mn%INCLUDE"
+   SET msvc_libs=-L"%mn%lib\amd64"
+   SET msvc_bin_dir=%mn%bin\amd64
+
+   SET sdk_dir=%WindowsSdkDir%
+   SET sdk_ver=%WindowsSDKVersion%
+   SET si=%sdk_dir%Include\%sdk_ver%
+   SET sdk_includes=-I"%si%ucrt" -I"%si%um" -I"%si%shared" -I"%si%winrt"
+   SET sl=%sdk_dir%lib\%sdk_ver%
+   SET sdk_libs=-L"%sl%ucrt\x64" -L"%sl%um\x64"
+
+   SET vs_ver=%VisualStudioVersion%
+
+   GOTO print_vars
+
+REM ================================================================
+
+:not_2015
+   echo "ERROR: unsupported VS version (older than VS2015)" >&2
+   EXIT /B 1
+
+REM ================================================================
+
+:print_vars
+   REM Dump the essential vars to stdout to allow the main
+   REM Makefile to include it.  See config.mak.uname.
+   REM Include DOS-style and BASH-style path for bin dir.
+
+   echo msvc_bin_dir=%msvc_bin_dir%
+   SET X1=%msvc_bin_dir:C:=/C%
+   SET X2=%X1:\=/%
+   echo msvc_bin_dir_msys=%X2%
+
+   echo msvc_includes=%msvc_includes%
+   echo msvc_libs=%msvc_libs%
+
+   echo sdk_includes=%sdk_includes%
+   echo sdk_libs=%sdk_libs%
+
+   echo vs_ver=%vs_ver%
+
+   EXIT /B 0
diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl
index a87d0da..c7b021b 100755
--- a/compat/vcbuild/scripts/clink.pl
+++ b/compat/vcbuild/scripts/clink.pl
@@ -12,32 +12,62 @@
 use strict;
 my @args = ();
 my @cflags = ();
+my @lflags = ();
 my $is_linking = 0;
+my $is_debug = 0;
 while (@ARGV) {
 	my $arg = shift @ARGV;
-	if ("$arg" =~ /^-[DIMGO]/) {
+	if ("$arg" eq "-DDEBUG") {
+	    # Some vcpkg-based libraries have different names for release
+	    # and debug versions.  This hack assumes that -DDEBUG comes
+	    # before any "-l*" flags.
+	    $is_debug = 1;
+	}
+	if ("$arg" =~ /^-[DIMGOZ]/) {
 		push(@cflags, $arg);
 	} elsif ("$arg" eq "-o") {
 		my $file_out = shift @ARGV;
 		if ("$file_out" =~ /exe$/) {
 			$is_linking = 1;
+			# Create foo.exe and foo.pdb
 			push(@args, "-OUT:$file_out");
 		} else {
+			# Create foo.o and foo.o.pdb
 			push(@args, "-Fo$file_out");
+			push(@args, "-Fd$file_out.pdb");
 		}
 	} elsif ("$arg" eq "-lz") {
+	    if ($is_debug) {
+		push(@args, "zlibd.lib");
+	    } else{
 		push(@args, "zlib.lib");
+	    }
 	} elsif ("$arg" eq "-liconv") {
-		push(@args, "iconv.lib");
+		push(@args, "libiconv.lib");
 	} elsif ("$arg" eq "-lcrypto") {
 		push(@args, "libeay32.lib");
 	} elsif ("$arg" eq "-lssl") {
 		push(@args, "ssleay32.lib");
 	} elsif ("$arg" eq "-lcurl") {
-		push(@args, "libcurl.lib");
+		my $lib = "";
+		# Newer vcpkg definitions call this libcurl_imp.lib; Do we
+		# need to use that instead?
+		foreach my $flag (@lflags) {
+			if ($flag =~ /^-LIBPATH:(.*)/) {
+				foreach my $l ("libcurl_imp.lib", "libcurl.lib") {
+					if (-f "$1/$l") {
+						$lib = $l;
+						last;
+					}
+				}
+			}
+		}
+		push(@args, $lib);
+	} elsif ("$arg" eq "-lexpat") {
+		push(@args, "expat.lib");
 	} elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") {
 		$arg =~ s/^-L/-LIBPATH:/;
-		push(@args, $arg);
+		push(@lflags, $arg);
 	} elsif ("$arg" =~ /^-R/) {
 		# eat
 	} else {
@@ -45,10 +75,11 @@
 	}
 }
 if ($is_linking) {
+	push(@args, @lflags);
 	unshift(@args, "link.exe");
 } else {
 	unshift(@args, "cl.exe");
 	push(@args, @cflags);
 }
-#printf("**** @args\n");
+printf(STDERR "**** @args\n\n\n") if (!defined($ENV{'QUIET_GEN'}));
 exit (system(@args) != 0);
diff --git a/compat/vcbuild/vcpkg_copy_dlls.bat b/compat/vcbuild/vcpkg_copy_dlls.bat
new file mode 100644
index 0000000..13661c1
--- /dev/null
+++ b/compat/vcbuild/vcpkg_copy_dlls.bat
@@ -0,0 +1,39 @@
+@ECHO OFF
+REM ================================================================
+REM This script is an optional step. It copies the *.dll and *.pdb
+REM files (created by vcpkg_install.bat) into the top-level directory
+REM of the repo so that you can type "./git.exe" and find them without
+REM having to fixup your PATH.
+REM
+REM NOTE: Because the names of some DLL files change between DEBUG and
+REM NOTE: RELEASE builds when built using "vcpkg.exe", you will need
+REM NOTE: to copy up the corresponding version.
+REM ================================================================
+
+	SETLOCAL EnableDelayedExpansion
+
+	@FOR /F "delims=" %%D IN ("%~dp0") DO @SET cwd=%%~fD
+	cd %cwd%
+
+	SET arch=x64-windows
+	SET inst=%cwd%vcpkg\installed\%arch%
+
+	IF [%1]==[release] (
+		echo Copying RELEASE mode DLLs to repo root...
+	) ELSE IF [%1]==[debug] (
+		SET inst=%inst%\debug
+		echo Copying DEBUG mode DLLs to repo root...
+	) ELSE (
+		echo ERROR: Invalid argument.
+		echo Usage: %~0 release
+		echo Usage: %~0 debug
+		EXIT /B 1
+	)
+
+	xcopy /e/s/v/y %inst%\bin\*.dll ..\..\
+	xcopy /e/s/v/y %inst%\bin\*.pdb ..\..\
+
+	xcopy /e/s/v/y %inst%\bin\*.dll ..\..\t\helper\
+	xcopy /e/s/v/y %inst%\bin\*.pdb ..\..\t\helper\
+
+	EXIT /B 0
diff --git a/compat/vcbuild/vcpkg_install.bat b/compat/vcbuild/vcpkg_install.bat
new file mode 100644
index 0000000..ebd0bad
--- /dev/null
+++ b/compat/vcbuild/vcpkg_install.bat
@@ -0,0 +1,80 @@
+@ECHO OFF
+REM ================================================================
+REM This script installs the "vcpkg" source package manager and uses
+REM it to build the third-party libraries that git requires when it
+REM is built using MSVC.
+REM
+REM [1] Install VCPKG.
+REM     [a] Create <root>/compat/vcbuild/vcpkg/
+REM     [b] Download "vcpkg".
+REM     [c] Compile using the currently installed version of VS.
+REM     [d] Create <root>/compat/vcbuild/vcpkg/vcpkg.exe
+REM
+REM [2] Install third-party libraries.
+REM     [a] Download each (which may also install CMAKE).
+REM     [b] Compile in RELEASE mode and install in:
+REM         vcpkg/installed/<arch>/{bin,lib}
+REM     [c] Compile in DEBUG mode and install in:
+REM         vcpkg/installed/<arch>/debug/{bin,lib}
+REM     [d] Install headers in:
+REM         vcpkg/installed/<arch>/include
+REM
+REM [3] Create a set of MAKE definitions for the top-level
+REM     Makefile to allow "make MSVC=1" to find the above
+REM     third-party libraries.
+REM     [a] Write vcpkg/VCPGK-DEFS
+REM
+REM https://blogs.msdn.microsoft.com/vcblog/2016/09/19/vcpkg-a-tool-to-acquire-and-build-c-open-source-libraries-on-windows/
+REM https://github.com/Microsoft/vcpkg
+REM https://vcpkg.readthedocs.io/en/latest/
+REM ================================================================
+
+	SETLOCAL EnableDelayedExpansion
+
+	@FOR /F "delims=" %%D IN ("%~dp0") DO @SET cwd=%%~fD
+	cd %cwd%
+
+	dir vcpkg\vcpkg.exe >nul 2>nul && GOTO :install_libraries
+
+	echo Fetching vcpkg in %cwd%vcpkg
+	git.exe clone https://github.com/Microsoft/vcpkg vcpkg
+	IF ERRORLEVEL 1 ( EXIT /B 1 )
+
+	cd vcpkg
+	echo Building vcpkg
+	powershell -exec bypass scripts\bootstrap.ps1
+	IF ERRORLEVEL 1 ( EXIT /B 1 )
+
+	echo Successfully installed %cwd%vcpkg\vcpkg.exe
+
+:install_libraries
+	SET arch=x64-windows
+
+	echo Installing third-party libraries...
+	FOR %%i IN (zlib expat libiconv openssl libssh2 curl) DO (
+	    cd %cwd%vcpkg
+	    IF NOT EXIST "packages\%%i_%arch%" CALL :sub__install_one %%i
+	    IF ERRORLEVEL 1 ( EXIT /B 1 )
+	)
+
+:install_defines
+	cd %cwd%
+	SET inst=%cwd%vcpkg\installed\%arch%
+
+	echo vcpkg_inc=-I"%inst%\include">VCPKG-DEFS
+	echo vcpkg_rel_lib=-L"%inst%\lib">>VCPKG-DEFS
+	echo vcpkg_rel_bin="%inst%\bin">>VCPKG-DEFS
+	echo vcpkg_dbg_lib=-L"%inst%\debug\lib">>VCPKG-DEFS
+	echo vcpkg_dbg_bin="%inst%\debug\bin">>VCPKG-DEFS
+
+	EXIT /B 0
+
+
+:sub__install_one
+	echo     Installing package %1...
+
+	.\vcpkg.exe install %1:%arch%
+	IF ERRORLEVEL 1 ( EXIT /B 1 )
+
+	echo     Finished %1
+	goto :EOF
diff --git a/compat/win32/git.manifest b/compat/win32/git.manifest
new file mode 100644
index 0000000..771e3cc
--- /dev/null
+++ b/compat/win32/git.manifest
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+	<assemblyIdentity type="win32" name="Git" version="0.0.0.1" />
+	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+		<security>
+			<requestedPrivileges>
+				<requestedExecutionLevel level="asInvoker" uiAccess="false" />
+			</requestedPrivileges>
+		</security>
+	</trustInfo>
+	<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+		<application>
+			<!-- Windows Vista -->
+			<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+			<!-- Windows 7 -->
+			<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+			<!-- Windows 8 -->
+			<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+			<!-- Windows 8.1 -->
+			<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+			<!-- Windows 10 -->
+			<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+		</application>
+	</compatibility>
+</assembly>
diff --git a/compat/winansi.c b/compat/winansi.c
index cd947e0..cacd82c 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -7,6 +7,7 @@
 #include <wingdi.h>
 #include <winreg.h>
 #include "win32.h"
+#include "win32/lazyload.h"
 
 static int fd_is_interactive[3] = { 0, 0, 0 };
 #define FD_CONSOLE 0x1
@@ -41,26 +42,21 @@
 #endif
 #endif
 
-typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
-		PCONSOLE_FONT_INFOEX);
-
 static void warn_if_raster_font(void)
 {
 	DWORD fontFamily = 0;
-	PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx;
+	DECLARE_PROC_ADDR(kernel32.dll, BOOL, GetCurrentConsoleFontEx,
+			HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
 
 	/* don't bother if output was ascii only */
 	if (!non_ascii_used)
 		return;
 
 	/* GetCurrentConsoleFontEx is available since Vista */
-	pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress(
-			GetModuleHandle("kernel32.dll"),
-			"GetCurrentConsoleFontEx");
-	if (pGetCurrentConsoleFontEx) {
+	if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) {
 		CONSOLE_FONT_INFOEX cfi;
 		cfi.cbSize = sizeof(cfi);
-		if (pGetCurrentConsoleFontEx(console, 0, &cfi))
+		if (GetCurrentConsoleFontEx(console, 0, &cfi))
 			fontFamily = cfi.FontFamily;
 	} else {
 		/* pre-Vista: check default console font in registry */
@@ -544,7 +540,20 @@
 #ifdef DETECT_MSYS_TTY
 
 #include <winternl.h>
+
+#if defined(_MSC_VER)
+
+typedef struct _OBJECT_NAME_INFORMATION
+{
+	UNICODE_STRING Name;
+	WCHAR NameBuffer[0];
+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
+
+#define ObjectNameInformation 1
+
+#else
 #include <ntstatus.h>
+#endif
 
 static void detect_msys_tty(int fd)
 {
diff --git a/config.c b/config.c
index 296a6d9..faa57e4 100644
--- a/config.c
+++ b/config.c
@@ -19,6 +19,7 @@
 #include "utf8.h"
 #include "dir.h"
 #include "color.h"
+#include "refs.h"
 
 struct config_source {
 	struct config_source *prev;
@@ -170,6 +171,12 @@
 	return ret;
 }
 
+static void add_trailing_starstar_for_dir(struct strbuf *pat)
+{
+	if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
+		strbuf_addstr(pat, "**");
+}
+
 static int prepare_include_condition_pattern(struct strbuf *pat)
 {
 	struct strbuf path = STRBUF_INIT;
@@ -199,8 +206,7 @@
 	} else if (!is_absolute_path(pat->buf))
 		strbuf_insert(pat, 0, "**/", 3);
 
-	if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
-		strbuf_addstr(pat, "**");
+	add_trailing_starstar_for_dir(pat);
 
 	strbuf_release(&path);
 	return prefix;
@@ -264,6 +270,25 @@
 	return ret;
 }
 
+static int include_by_branch(const char *cond, size_t cond_len)
+{
+	int flags;
+	int ret;
+	struct strbuf pattern = STRBUF_INIT;
+	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+	const char *shortname;
+
+	if (!refname || !(flags & REF_ISSYMREF)	||
+			!skip_prefix(refname, "refs/heads/", &shortname))
+		return 0;
+
+	strbuf_add(&pattern, cond, cond_len);
+	add_trailing_starstar_for_dir(&pattern);
+	ret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);
+	strbuf_release(&pattern);
+	return ret;
+}
+
 static int include_condition_is_true(const struct config_options *opts,
 				     const char *cond, size_t cond_len)
 {
@@ -272,6 +297,8 @@
 		return include_by_gitdir(opts, cond, cond_len, 0);
 	else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
 		return include_by_gitdir(opts, cond, cond_len, 1);
+	else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
+		return include_by_branch(cond, cond_len);
 
 	/* unknown conditionals are always false */
 	return 0;
@@ -834,22 +861,16 @@
 	return error_return;
 }
 
-static int parse_unit_factor(const char *end, uintmax_t *val)
+static uintmax_t get_unit_factor(const char *end)
 {
 	if (!*end)
 		return 1;
-	else if (!strcasecmp(end, "k")) {
-		*val *= 1024;
-		return 1;
-	}
-	else if (!strcasecmp(end, "m")) {
-		*val *= 1024 * 1024;
-		return 1;
-	}
-	else if (!strcasecmp(end, "g")) {
-		*val *= 1024 * 1024 * 1024;
-		return 1;
-	}
+	else if (!strcasecmp(end, "k"))
+		return 1024;
+	else if (!strcasecmp(end, "m"))
+		return 1024 * 1024;
+	else if (!strcasecmp(end, "g"))
+		return 1024 * 1024 * 1024;
 	return 0;
 }
 
@@ -859,19 +880,20 @@
 		char *end;
 		intmax_t val;
 		uintmax_t uval;
-		uintmax_t factor = 1;
+		uintmax_t factor;
 
 		errno = 0;
 		val = strtoimax(value, &end, 0);
 		if (errno == ERANGE)
 			return 0;
-		if (!parse_unit_factor(end, &factor)) {
+		factor = get_unit_factor(end);
+		if (!factor) {
 			errno = EINVAL;
 			return 0;
 		}
-		uval = labs(val);
-		uval *= factor;
-		if (uval > max || labs(val) > uval) {
+		uval = val < 0 ? -val : val;
+		if (unsigned_mult_overflows(factor, uval) ||
+		    factor * uval > max) {
 			errno = ERANGE;
 			return 0;
 		}
@@ -888,21 +910,23 @@
 	if (value && *value) {
 		char *end;
 		uintmax_t val;
-		uintmax_t oldval;
+		uintmax_t factor;
 
 		errno = 0;
 		val = strtoumax(value, &end, 0);
 		if (errno == ERANGE)
 			return 0;
-		oldval = val;
-		if (!parse_unit_factor(end, &val)) {
+		factor = get_unit_factor(end);
+		if (!factor) {
 			errno = EINVAL;
 			return 0;
 		}
-		if (val > max || oldval > val) {
+		if (unsigned_mult_overflows(factor, val) ||
+		    factor * val > max) {
 			errno = ERANGE;
 			return 0;
 		}
+		val *= factor;
 		*ret = val;
 		return 1;
 	}
diff --git a/config.mak.uname b/config.mak.uname
index b71688e..48a6723 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -1,5 +1,9 @@
 # Platform specific Makefile tweaks based on uname detection
 
+# Define NO_SAFESEH if you need MSVC/Visual Studio to ignore the lack of
+# Microsoft's Safe Exception Handling in libraries (such as zlib).
+# Typically required for VS2013+/32-bit compilation on Vista+ versions.
+
 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
 uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
 uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
@@ -11,6 +15,19 @@
 	# avoid the MingW and Cygwin configuration sections
 	uname_S := Windows
 	uname_O := Windows
+
+	# Generate and include makefile variables that point to the
+	# currently installed set of MSVC command line tools.
+compat/vcbuild/MSVC-DEFS-GEN: compat/vcbuild/find_vs_env.bat
+	@"$<" | tr '\\' / >"$@"
+include compat/vcbuild/MSVC-DEFS-GEN
+
+	# See if vcpkg and the vcpkg-build versions of the third-party
+	# libraries that we use are installed.  We include the result
+	# to get $(vcpkg_*) variables defined for the Makefile.
+compat/vcbuild/VCPKG-DEFS: compat/vcbuild/vcpkg_install.bat
+	@"$<"
+include compat/vcbuild/VCPKG-DEFS
 endif
 
 # We choose to avoid "if .. else if .. else .. endif endif"
@@ -356,6 +373,19 @@
 ifeq ($(uname_S),Windows)
 	GIT_VERSION := $(GIT_VERSION).MSVC
 	pathsep = ;
+	# Assume that this is built in Git for Windows' SDK
+	ifeq (MINGW32,$(MSYSTEM))
+		prefix = /mingw32
+	else
+		prefix = /mingw64
+	endif
+	# Prepend MSVC 64-bit tool-chain to PATH.
+	#
+	# A regular Git Bash *does not* have cl.exe in its $PATH. As there is a
+	# link.exe next to, and required by, cl.exe, we have to prepend this
+	# onto the existing $PATH.
+	#
+	SANE_TOOL_PATH ?= $(msvc_bin_dir_msys)
 	HAVE_ALLOCA_H = YesPlease
 	NO_PREAD = YesPlease
 	NEEDS_CRYPTO_WITH_SSL = YesPlease
@@ -368,11 +398,14 @@
 	NO_STRCASESTR = YesPlease
 	NO_STRLCPY = YesPlease
 	NO_MEMMEM = YesPlease
-	# NEEDS_LIBICONV = YesPlease
-	NO_ICONV = YesPlease
+	NEEDS_LIBICONV = YesPlease
 	NO_STRTOUMAX = YesPlease
 	NO_MKDTEMP = YesPlease
-	SNPRINTF_RETURNS_BOGUS = YesPlease
+	NO_INTTYPES_H = YesPlease
+	# VS2015 with UCRT claims that snprintf and friends are C99 compliant,
+	# so we don't need this:
+	#
+	#   SNPRINTF_RETURNS_BOGUS = YesPlease
 	NO_SVN_TESTS = YesPlease
 	RUNTIME_PREFIX = YesPlease
 	HAVE_WPGMPTR = YesWeDo
@@ -385,7 +418,6 @@
 	NO_REGEX = YesPlease
 	NO_GETTEXT = YesPlease
 	NO_PYTHON = YesPlease
-	BLK_SHA1 = YesPlease
 	ETAGS_TARGET = ETAGS
 	NO_POSIX_GOODIES = UnfortunatelyYes
 	NATIVE_CRLF = YesPlease
@@ -394,26 +426,52 @@
 	CC = compat/vcbuild/scripts/clink.pl
 	AR = compat/vcbuild/scripts/lib.pl
 	CFLAGS =
-	BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
+	BASIC_CFLAGS = -nologo -I. -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
 	COMPAT_OBJS = compat/msvc.o compat/winansi.o \
 		compat/win32/path-utils.o \
 		compat/win32/pthread.o compat/win32/syslog.o \
 		compat/win32/trace2_win32_process_info.o \
 		compat/win32/dirent.o
-	COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
-	BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE
-	EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
+	COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DDETECT_MSYS_TTY -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
+	BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -ENTRY:wmainCRTStartup -SUBSYSTEM:CONSOLE
+	# invalidcontinue.obj allows Git's source code to close the same file
+	# handle twice, or to access the osfhandle of an already-closed stdout
+	# See https://msdn.microsoft.com/en-us/library/ms235330.aspx
+	EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj kernel32.lib ntdll.lib
 	PTHREAD_LIBS =
 	lib =
-	BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
+	BASIC_CFLAGS += $(vcpkg_inc) $(sdk_includes) $(msvc_includes)
 ifndef DEBUG
-	BASIC_CFLAGS += -GL -Os -MD
-	BASIC_LDFLAGS += -LTCG
+	BASIC_CFLAGS += $(vcpkg_rel_lib)
+else
+	BASIC_CFLAGS += $(vcpkg_dbg_lib)
+endif
+	BASIC_CFLAGS += $(sdk_libs) $(msvc_libs)
+
+ifneq ($(USE_MSVC_CRTDBG),)
+	# Optionally enable memory leak reporting.
+	BASIC_CFLAGS += -DUSE_MSVC_CRTDBG
+endif
+	BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
+	# Always give "-Zi" to the compiler and "-debug" to linker (even in
+	# release mode) to force a PDB to be generated (like RelWithDebInfo).
+	BASIC_CFLAGS += -Zi
+	BASIC_LDFLAGS += -debug -Zf
+
+ifdef NO_SAFESEH
+	LDFLAGS += -SAFESEH:NO
+endif
+
+ifndef DEBUG
+	BASIC_CFLAGS += -GL -Gy -O2 -Oy- -MD -DNDEBUG
+	BASIC_LDFLAGS += -release -LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO /DEBUGTYPE:CV,FIXUP
 	AR += -LTCG
 else
-	BASIC_CFLAGS += -Zi -MDd
+	BASIC_CFLAGS += -MDd -DDEBUG -D_DEBUG
 endif
 	X = .exe
+
+compat/msvc.o: compat/msvc.c compat/mingw.c GIT-CFLAGS
 endif
 ifeq ($(uname_S),Interix)
 	NO_INITGROUPS = YesPlease
@@ -548,6 +606,7 @@
 	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 \
@@ -571,7 +630,7 @@
 	INTERNAL_QSORT = YesPlease
 	HAVE_LIBCHARSET_H = YesPlease
 	NO_GETTEXT = YesPlease
-	COMPAT_CLFAGS += -D__USE_MINGW_ACCESS
+	COMPAT_CFLAGS += -D__USE_MINGW_ACCESS
 else
 	ifneq ($(shell expr "$(uname_R)" : '1\.'),2)
 		# MSys2
@@ -596,7 +655,8 @@
 			BASIC_LDFLAGS += -Wl,--large-address-aware
 		endif
 		CC = gcc
-		COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY
+		COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
+			-fstack-protector-strong
 		EXTLIBS += -lntdll
 		INSTALL = /bin/install
 		NO_R_TO_GCC_LINKER = YesPlease
diff --git a/configure.ac b/configure.ac
index be3b55f..a43b476 100644
--- a/configure.ac
+++ b/configure.ac
@@ -475,8 +475,18 @@
       if test "$git_cv_ld_rpath" = "yes"; then
          CC_LD_DYNPATH=-rpath
       else
-         CC_LD_DYNPATH=
-         AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
+         AC_CACHE_CHECK([if linker supports -Wl,+b,], git_cv_ld_wl_b, [
+            SAVE_LDFLAGS="${LDFLAGS}"
+            LDFLAGS="${SAVE_LDFLAGS} -Wl,+b,/"
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [git_cv_ld_wl_b=yes], [git_cv_ld_wl_b=no])
+            LDFLAGS="${SAVE_LDFLAGS}"
+         ])
+         if test "$git_cv_ld_wl_b" = "yes"; then
+            CC_LD_DYNPATH=-Wl,+b,
+          else
+             CC_LD_DYNPATH=
+             AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
+          fi
       fi
    fi
 fi
diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci
index 0158682..46b8d2e 100644
--- a/contrib/coccinelle/array.cocci
+++ b/contrib/coccinelle/array.cocci
@@ -1,29 +1,60 @@
 @@
-type T;
-T *dst;
-T *src;
-expression n;
+expression dst, src, n, E;
 @@
-- memcpy(dst, src, (n) * sizeof(*dst));
-+ COPY_ARRAY(dst, src, n);
+  memcpy(dst, src, n * sizeof(
+- E[...]
++ *(E)
+  ))
 
 @@
 type T;
-T *dst;
-T *src;
-expression n;
+T *ptr;
+T[] arr;
+expression E, n;
 @@
-- memcpy(dst, src, (n) * sizeof(*src));
-+ COPY_ARRAY(dst, src, n);
+(
+  memcpy(ptr, E,
+- n * sizeof(*(ptr))
++ n * sizeof(T)
+  )
+|
+  memcpy(arr, E,
+- n * sizeof(*(arr))
++ n * sizeof(T)
+  )
+|
+  memcpy(E, ptr,
+- n * sizeof(*(ptr))
++ n * sizeof(T)
+  )
+|
+  memcpy(E, arr,
+- n * sizeof(*(arr))
++ n * sizeof(T)
+  )
+)
 
 @@
 type T;
-T *dst;
-T *src;
+T *dst_ptr;
+T *src_ptr;
+T[] dst_arr;
+T[] src_arr;
 expression n;
 @@
-- memcpy(dst, src, (n) * sizeof(T));
-+ COPY_ARRAY(dst, src, n);
+(
+- memcpy(dst_ptr, src_ptr, (n) * sizeof(T))
++ COPY_ARRAY(dst_ptr, src_ptr, n)
+|
+- memcpy(dst_ptr, src_arr, (n) * sizeof(T))
++ COPY_ARRAY(dst_ptr, src_arr, n)
+|
+- memcpy(dst_arr, src_ptr, (n) * sizeof(T))
++ COPY_ARRAY(dst_arr, src_ptr, n)
+|
+- memcpy(dst_arr, src_arr, (n) * sizeof(T))
++ COPY_ARRAY(dst_arr, src_arr, n)
+)
 
 @@
 type T;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 9f71bcd..e087c4b 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -37,7 +37,8 @@
 #   GIT_COMPLETION_CHECKOUT_NO_GUESS
 #
 #     When set to "1", do not include "DWIM" suggestions in git-checkout
-#     completion (e.g., completing "foo" when "origin/foo" exists).
+#     and git-switch completion (e.g., completing "foo" when "origin/foo"
+#     exists).
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -400,7 +401,8 @@
 	if [ -z "$options" ]; then
 		# leading and trailing spaces are significant to make
 		# option removal work correctly.
-		options=" $incl $(__git ${cmd/_/ } --git-completion-helper) "
+		options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " || return
+
 		for i in $excl; do
 			options="${options/ $i / }"
 		done
@@ -2159,6 +2161,44 @@
 	__git_complete_index_file "$complete_opt"
 }
 
+_git_switch ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp_builtin switch
+		;;
+	*)
+		# check if --track, --no-track, or --no-guess was specified
+		# if so, disable DWIM mode
+		local track_opt="--track" only_local_ref=n
+		if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
+		   [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
+			track_opt=''
+		fi
+		# explicit --guess enables DWIM mode regardless of
+		# $GIT_COMPLETION_CHECKOUT_NO_GUESS
+		if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
+			track_opt='--track'
+		fi
+		if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
+			only_local_ref=y
+		else
+			# --guess --detach is invalid combination, no
+			# dwim will be done when --detach is specified
+			track_opt=
+		fi
+		if [ $only_local_ref = y -a -z "$track_opt" ]; then
+			__gitcomp_direct "$(__git_heads "" "$cur" " ")"
+		else
+			__git_complete_refs $track_opt
+		fi
+		;;
+	esac
+}
+
 __git_config_get_set_variables ()
 {
 	local prevword word config_file= c=$cword
@@ -2457,6 +2497,21 @@
 	__git_complete_refs
 }
 
+_git_restore ()
+{
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--source=*)
+		__git_complete_refs --cur="${cur##--source=}"
+		;;
+	--*)
+		__gitcomp_builtin restore
+		;;
+	esac
+}
+
 __git_revert_inprogress_options="--continue --quit --abort"
 
 _git_revert ()
diff --git a/decorate.c b/decorate.c
index de31331..a605b1b 100644
--- a/decorate.c
+++ b/decorate.c
@@ -8,7 +8,7 @@
 
 static unsigned int hash_obj(const struct object *obj, unsigned int n)
 {
-	return sha1hash(obj->oid.hash) % n;
+	return oidhash(&obj->oid) % n;
 }
 
 static void *insert_decoration(struct decoration *n, const struct object *base, void *decoration)
diff --git a/delta-islands.c b/delta-islands.c
index 2186bd0..09dbd3c 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -22,7 +22,7 @@
 
 KHASH_INIT(str, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
 
-static khash_sha1 *island_marks;
+static kh_oid_map_t *island_marks;
 static unsigned island_counter;
 static unsigned island_counter_core;
 
@@ -105,7 +105,7 @@
 	 * If we don't have a bitmap for the target, we can delta it
 	 * against anything -- it's not an important object
 	 */
-	trg_pos = kh_get_sha1(island_marks, trg_oid->hash);
+	trg_pos = kh_get_oid_map(island_marks, *trg_oid);
 	if (trg_pos >= kh_end(island_marks))
 		return 1;
 
@@ -113,7 +113,7 @@
 	 * if the source (our delta base) doesn't have a bitmap,
 	 * we don't want to base any deltas on it!
 	 */
-	src_pos = kh_get_sha1(island_marks, src_oid->hash);
+	src_pos = kh_get_oid_map(island_marks, *src_oid);
 	if (src_pos >= kh_end(island_marks))
 		return 0;
 
@@ -129,11 +129,11 @@
 	if (!island_marks)
 		return 0;
 
-	a_pos = kh_get_sha1(island_marks, a->hash);
+	a_pos = kh_get_oid_map(island_marks, *a);
 	if (a_pos < kh_end(island_marks))
 		a_bitmap = kh_value(island_marks, a_pos);
 
-	b_pos = kh_get_sha1(island_marks, b->hash);
+	b_pos = kh_get_oid_map(island_marks, *b);
 	if (b_pos < kh_end(island_marks))
 		b_bitmap = kh_value(island_marks, b_pos);
 
@@ -154,7 +154,7 @@
 	khiter_t pos;
 	int hash_ret;
 
-	pos = kh_put_sha1(island_marks, obj->oid.hash, &hash_ret);
+	pos = kh_put_oid_map(island_marks, obj->oid, &hash_ret);
 	if (hash_ret)
 		kh_value(island_marks, pos) = island_bitmap_new(NULL);
 
@@ -167,7 +167,7 @@
 	khiter_t pos;
 	int hash_ret;
 
-	pos = kh_put_sha1(island_marks, obj->oid.hash, &hash_ret);
+	pos = kh_put_oid_map(island_marks, obj->oid, &hash_ret);
 	if (hash_ret) {
 		/*
 		 * We don't have one yet; make a copy-on-write of the
@@ -279,7 +279,7 @@
 		struct name_entry entry;
 		khiter_t pos;
 
-		pos = kh_get_sha1(island_marks, ent->idx.oid.hash);
+		pos = kh_get_oid_map(island_marks, ent->idx.oid);
 		if (pos >= kh_end(island_marks))
 			continue;
 
@@ -296,7 +296,7 @@
 			if (S_ISGITLINK(entry.mode))
 				continue;
 
-			obj = lookup_object(r, entry.oid.hash);
+			obj = lookup_object(r, &entry.oid);
 			if (!obj)
 				continue;
 
@@ -454,21 +454,22 @@
 	free(list);
 }
 
-void load_delta_islands(struct repository *r)
+void load_delta_islands(struct repository *r, int progress)
 {
-	island_marks = kh_init_sha1();
+	island_marks = kh_init_oid_map();
 	remote_islands = kh_init_str();
 
 	git_config(island_config_callback, NULL);
 	for_each_ref(find_island_for_ref, NULL);
 	deduplicate_islands(r);
 
-	fprintf(stderr, _("Marked %d islands, done.\n"), island_counter);
+	if (progress)
+		fprintf(stderr, _("Marked %d islands, done.\n"), island_counter);
 }
 
 void propagate_island_marks(struct commit *commit)
 {
-	khiter_t pos = kh_get_sha1(island_marks, commit->object.oid.hash);
+	khiter_t pos = kh_get_oid_map(island_marks, commit->object.oid);
 
 	if (pos < kh_end(island_marks)) {
 		struct commit_list *p;
@@ -490,7 +491,7 @@
 
 	for (i = 0; i < to_pack->nr_objects; ++i) {
 		struct object_entry *entry = &to_pack->objects[i];
-		khiter_t pos = kh_get_sha1(island_marks, entry->idx.oid.hash);
+		khiter_t pos = kh_get_oid_map(island_marks, entry->idx.oid);
 
 		oe_set_layer(to_pack, entry, 1);
 
diff --git a/delta-islands.h b/delta-islands.h
index 3ac8045..eb0f952 100644
--- a/delta-islands.h
+++ b/delta-islands.h
@@ -11,7 +11,7 @@
 void resolve_tree_islands(struct repository *r,
 			  int progress,
 			  struct packing_data *to_pack);
-void load_delta_islands(struct repository *r);
+void load_delta_islands(struct repository *r, int progress);
 void propagate_island_marks(struct commit *commit);
 int compute_pack_layers(struct packing_data *to_pack);
 
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 07bd34b..9624864 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -23,7 +23,7 @@
 	first = 0;
 	last = rename_dst_nr;
 	while (last > first) {
-		int next = (last + first) >> 1;
+		int next = first + ((last - first) >> 1);
 		struct diff_rename_dst *dst = &(rename_dst[next]);
 		int cmp = strcmp(two->path, dst->two->path);
 		if (!cmp)
@@ -83,7 +83,7 @@
 	first = 0;
 	last = rename_src_nr;
 	while (last > first) {
-		int next = (last + first) >> 1;
+		int next = first + ((last - first) >> 1);
 		struct diff_rename_src *src = &(rename_src[next]);
 		int cmp = strcmp(one->path, src->p->one->path);
 		if (!cmp)
@@ -266,7 +266,7 @@
 		hash_object_file(filespec->data, filespec->size, "blob",
 				 &filespec->oid);
 	}
-	return sha1hash(filespec->oid.hash);
+	return oidhash(&filespec->oid);
 }
 
 static int find_identical_files(struct hashmap *srcs,
diff --git a/dir.c b/dir.c
index ba4a51c..d021c90 100644
--- a/dir.c
+++ b/dir.c
@@ -701,7 +701,7 @@
 	first = 0;
 	last = dir->dirs_nr;
 	while (last > first) {
-		int cmp, next = (last + first) >> 1;
+		int cmp, next = first + ((last - first) >> 1);
 		d = dir->dirs[next];
 		cmp = strncmp(name, d->name, len);
 		if (!cmp && strlen(d->name) > len)
diff --git a/editor.c b/editor.c
index 7154767..f079abb 100644
--- a/editor.c
+++ b/editor.c
@@ -96,10 +96,10 @@
 
 		if (print_waiting_for_editor && !is_terminal_dumb())
 			/*
-			 * Go back to the beginning and erase the entire line to
-			 * avoid wasting the vertical space.
+			 * Erase the entire line to avoid wasting the
+			 * vertical space.
 			 */
-			fputs("\r\033[K", stderr);
+			term_clear_line();
 	}
 
 	if (!buffer)
diff --git a/fast-import.c b/fast-import.c
index 76a7bd3..6dfdd68 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -644,7 +644,7 @@
 	struct tree_content *r = new_tree_content(t->entry_count + amt);
 	r->entry_count = t->entry_count;
 	r->delta_depth = t->delta_depth;
-	memcpy(r->entries,t->entries,t->entry_count*sizeof(t->entries[0]));
+	COPY_ARRAY(r->entries, t->entries, t->entry_count);
 	release_tree_content(t);
 	return r;
 }
diff --git a/fetch-pack.c b/fetch-pack.c
index 1c10f54..65be043 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -286,7 +286,7 @@
 		 * we cannot trust the object flags).
 		 */
 		if (!args->no_dependents &&
-		    ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+		    ((o = lookup_object(the_repository, remote)) != NULL) &&
 				(o->flags & COMPLETE)) {
 			continue;
 		}
@@ -364,7 +364,7 @@
 			if (skip_prefix(reader.line, "unshallow ", &arg)) {
 				if (get_oid_hex(arg, &oid))
 					die(_("invalid unshallow line: %s"), reader.line);
-				if (!lookup_object(the_repository, oid.hash))
+				if (!lookup_object(the_repository, &oid))
 					die(_("object not found: %s"), reader.line);
 				/* make sure that it is parsed as shallow */
 				if (!parse_object(the_repository, &oid))
@@ -707,7 +707,7 @@
 	for (ref = *refs; ref; ref = ref->next) {
 		struct object *o = deref_tag(the_repository,
 					     lookup_object(the_repository,
-					     ref->old_oid.hash),
+					     &ref->old_oid),
 					     NULL, 0);
 
 		if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
@@ -734,7 +734,7 @@
 		const struct object_id *remote = &ref->old_oid;
 		struct object *o;
 
-		o = lookup_object(the_repository, remote->hash);
+		o = lookup_object(the_repository, remote);
 		if (!o || !(o->flags & COMPLETE)) {
 			retval = 0;
 			print_verbose(args, "want %s (%s)", oid_to_hex(remote),
@@ -902,72 +902,85 @@
 	sort_ref_list(&ref, ref_compare_name);
 	QSORT(sought, nr_sought, cmp_ref_by_name);
 
-	if ((args->depth > 0 || is_repository_shallow(the_repository)) && !server_supports("shallow"))
-		die(_("Server does not support shallow clients"));
-	if (args->depth > 0 || args->deepen_since || args->deepen_not)
-		args->deepen = 1;
-	if (server_supports("multi_ack_detailed")) {
-		print_verbose(args, _("Server supports multi_ack_detailed"));
-		multi_ack = 2;
-		if (server_supports("no-done")) {
-			print_verbose(args, _("Server supports no-done"));
-			if (args->stateless_rpc)
-				no_done = 1;
-		}
-	}
-	else if (server_supports("multi_ack")) {
-		print_verbose(args, _("Server supports multi_ack"));
-		multi_ack = 1;
-	}
-	if (server_supports("side-band-64k")) {
-		print_verbose(args, _("Server supports side-band-64k"));
-		use_sideband = 2;
-	}
-	else if (server_supports("side-band")) {
-		print_verbose(args, _("Server supports side-band"));
-		use_sideband = 1;
-	}
-	if (server_supports("allow-tip-sha1-in-want")) {
-		print_verbose(args, _("Server supports allow-tip-sha1-in-want"));
-		allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
-	}
-	if (server_supports("allow-reachable-sha1-in-want")) {
-		print_verbose(args, _("Server supports allow-reachable-sha1-in-want"));
-		allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
-	}
-	if (!server_supports("thin-pack"))
-		args->use_thin_pack = 0;
-	if (!server_supports("no-progress"))
-		args->no_progress = 0;
-	if (!server_supports("include-tag"))
-		args->include_tag = 0;
-	if (server_supports("ofs-delta"))
-		print_verbose(args, _("Server supports ofs-delta"));
-	else
-		prefer_ofs_delta = 0;
-
-	if (server_supports("filter")) {
-		server_supports_filtering = 1;
-		print_verbose(args, _("Server supports filter"));
-	} else if (args->filter_options.choice) {
-		warning("filtering not recognized by server, ignoring");
-	}
-
 	if ((agent_feature = server_feature_value("agent", &agent_len))) {
 		agent_supported = 1;
 		if (agent_len)
 			print_verbose(args, _("Server version is %.*s"),
 				      agent_len, agent_feature);
 	}
-	if (server_supports("deepen-since"))
+
+	if (server_supports("shallow"))
+		print_verbose(args, _("Server supports %s"), "shallow");
+	else if (args->depth > 0 || is_repository_shallow(the_repository))
+		die(_("Server does not support shallow clients"));
+	if (args->depth > 0 || args->deepen_since || args->deepen_not)
+		args->deepen = 1;
+	if (server_supports("multi_ack_detailed")) {
+		print_verbose(args, _("Server supports %s"), "multi_ack_detailed");
+		multi_ack = 2;
+		if (server_supports("no-done")) {
+			print_verbose(args, _("Server supports %s"), "no-done");
+			if (args->stateless_rpc)
+				no_done = 1;
+		}
+	}
+	else if (server_supports("multi_ack")) {
+		print_verbose(args, _("Server supports %s"), "multi_ack");
+		multi_ack = 1;
+	}
+	if (server_supports("side-band-64k")) {
+		print_verbose(args, _("Server supports %s"), "side-band-64k");
+		use_sideband = 2;
+	}
+	else if (server_supports("side-band")) {
+		print_verbose(args, _("Server supports %s"), "side-band");
+		use_sideband = 1;
+	}
+	if (server_supports("allow-tip-sha1-in-want")) {
+		print_verbose(args, _("Server supports %s"), "allow-tip-sha1-in-want");
+		allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
+	}
+	if (server_supports("allow-reachable-sha1-in-want")) {
+		print_verbose(args, _("Server supports %s"), "allow-reachable-sha1-in-want");
+		allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
+	}
+	if (server_supports("thin-pack"))
+		print_verbose(args, _("Server supports %s"), "thin-pack");
+	else
+		args->use_thin_pack = 0;
+	if (server_supports("no-progress"))
+		print_verbose(args, _("Server supports %s"), "no-progress");
+	else
+		args->no_progress = 0;
+	if (server_supports("include-tag"))
+		print_verbose(args, _("Server supports %s"), "include-tag");
+	else
+		args->include_tag = 0;
+	if (server_supports("ofs-delta"))
+		print_verbose(args, _("Server supports %s"), "ofs-delta");
+	else
+		prefer_ofs_delta = 0;
+
+	if (server_supports("filter")) {
+		server_supports_filtering = 1;
+		print_verbose(args, _("Server supports %s"), "filter");
+	} else if (args->filter_options.choice) {
+		warning("filtering not recognized by server, ignoring");
+	}
+
+	if (server_supports("deepen-since")) {
+		print_verbose(args, _("Server supports %s"), "deepen-since");
 		deepen_since_ok = 1;
-	else if (args->deepen_since)
+	} else if (args->deepen_since)
 		die(_("Server does not support --shallow-since"));
-	if (server_supports("deepen-not"))
+	if (server_supports("deepen-not")) {
+		print_verbose(args, _("Server supports %s"), "deepen-not");
 		deepen_not_ok = 1;
-	else if (args->deepen_not)
+	} else if (args->deepen_not)
 		die(_("Server does not support --shallow-exclude"));
-	if (!server_supports("deepen-relative") && args->deepen_relative)
+	if (server_supports("deepen-relative"))
+		print_verbose(args, _("Server supports %s"), "deepen-relative");
+	else if (args->deepen_relative)
 		die(_("Server does not support --deepen"));
 
 	if (!args->no_dependents) {
@@ -1048,7 +1061,7 @@
 		 * we cannot trust the object flags).
 		 */
 		if (!no_dependents &&
-		    ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+		    ((o = lookup_object(the_repository, remote)) != NULL) &&
 		    (o->flags & COMPLETE)) {
 			continue;
 		}
@@ -1275,7 +1288,7 @@
 		if (skip_prefix(reader->line, "unshallow ", &arg)) {
 			if (get_oid_hex(arg, &oid))
 				die(_("invalid unshallow line: %s"), reader->line);
-			if (!lookup_object(the_repository, oid.hash))
+			if (!lookup_object(the_repository, &oid))
 				die(_("object not found: %s"), reader->line);
 			/* make sure that it is parsed as shallow */
 			if (!parse_object(the_repository, &oid))
diff --git a/fsck.c b/fsck.c
index 4703f55..117c4a9 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1092,7 +1092,7 @@
 
 		blob = lookup_blob(the_repository, oid);
 		if (!blob) {
-			struct object *obj = lookup_unknown_object(oid->hash);
+			struct object *obj = lookup_unknown_object(oid);
 			ret |= report(options, obj,
 				      FSCK_MSG_GITMODULES_BLOB,
 				      "non-blob found at .gitmodules");
diff --git a/fsmonitor.c b/fsmonitor.c
index 1dee0ad..231e83a 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -56,7 +56,7 @@
 
 void fill_fsmonitor_bitmap(struct index_state *istate)
 {
-	int i;
+	unsigned int i;
 	istate->fsmonitor_dirty = ewah_new();
 	for (i = 0; i < istate->cache_nr; i++)
 		if (!(istate->cache[i]->ce_flags & CE_FSMONITOR_VALID))
@@ -134,7 +134,7 @@
 	size_t bol; /* beginning of line */
 	uint64_t last_update;
 	char *buf;
-	int i;
+	unsigned int i;
 
 	if (!core_fsmonitor || istate->fsmonitor_has_run_once)
 		return;
@@ -192,7 +192,7 @@
 
 void add_fsmonitor(struct index_state *istate)
 {
-	int i;
+	unsigned int i;
 
 	if (!istate->fsmonitor_last_update) {
 		trace_printf_key(&trace_fsmonitor, "add fsmonitor");
@@ -225,7 +225,7 @@
 
 void tweak_fsmonitor(struct index_state *istate)
 {
-	int i;
+	unsigned int i;
 	int fsmonitor_enabled = git_config_get_fsmonitor();
 
 	if (istate->fsmonitor_dirty) {
diff --git a/gettext.c b/gettext.c
index d4021d6..3f2aca5 100644
--- a/gettext.c
+++ b/gettext.c
@@ -12,7 +12,25 @@
 #ifndef NO_GETTEXT
 #	include <locale.h>
 #	include <libintl.h>
-#	ifdef HAVE_LIBCHARSET_H
+#	ifdef GIT_WINDOWS_NATIVE
+
+static const char *locale_charset(void)
+{
+	const char *env = getenv("LC_ALL"), *dot;
+
+	if (!env || !*env)
+		env = getenv("LC_CTYPE");
+	if (!env || !*env)
+		env = getenv("LANG");
+
+	if (!env)
+		return "UTF-8";
+
+	dot = strchr(env, '.');
+	return !dot ? env : dot + 1;
+}
+
+#	elif defined HAVE_LIBCHARSET_H
 #		include <libcharset.h>
 #	else
 #		include <langinfo.h>
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 20eb81c..c20ae9e 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -149,6 +149,20 @@
 		FILTER => undef,
 		IS_REVERSE => 0,
 	},
+	'worktree_head' => {
+		DIFF => 'diff-index -p',
+		APPLY => sub { apply_patch 'apply -R', @_ },
+		APPLY_CHECK => 'apply -R',
+		FILTER => undef,
+		IS_REVERSE => 1,
+	},
+	'worktree_nothead' => {
+		DIFF => 'diff-index -R -p',
+		APPLY => sub { apply_patch 'apply', @_ },
+		APPLY_CHECK => 'apply',
+		FILTER => undef,
+		IS_REVERSE => 0,
+	},
 );
 
 $patch_mode = 'stage';
@@ -972,7 +986,11 @@
 			next;
 		}
 		if ($ofs_delta) {
-			$n_ofs += $ofs_delta;
+			if ($patch_mode_flavour{IS_REVERSE}) {
+				$o_ofs -= $ofs_delta;
+			} else {
+				$n_ofs += $ofs_delta;
+			}
 			$_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
 							     $n_ofs, $n_cnt);
 		}
@@ -1050,6 +1068,12 @@
 	checkout_nothead => N__(
 "If the patch applies cleanly, the edited hunk will immediately be
 marked for applying."),
+	worktree_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding."),
+	worktree_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
 );
 
 sub recount_edited_hunk {
@@ -1260,6 +1284,18 @@
 q - quit; do not apply this hunk or any of the remaining ones
 a - apply this hunk and all later hunks in the file
 d - do not apply this hunk or any of the later hunks in the file"),
+	worktree_head => N__(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+	worktree_nothead => N__(
+"y - apply this hunk to worktree
+n - do not apply this hunk to worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
 );
 
 sub help_patch_cmd {
@@ -1421,6 +1457,16 @@
 		deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
 		hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
 	},
+	worktree_head => {
+		mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
+		deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+		hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+	},
+	worktree_nothead => {
+		mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
+		deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
+		hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+	},
 );
 
 sub patch_update_file {
@@ -1756,6 +1802,16 @@
 						       'checkout_head' : 'checkout_nothead');
 					$arg = shift @ARGV or die __("missing --");
 				}
+			} elsif ($1 eq 'worktree') {
+				$arg = shift @ARGV or die __("missing --");
+				if ($arg eq '--') {
+					$patch_mode = 'checkout_index';
+				} else {
+					$patch_mode_revision = $arg;
+					$patch_mode = ($arg eq 'HEAD' ?
+						       'worktree_head' : 'worktree_nothead');
+					$arg = shift @ARGV or die __("missing --");
+				}
 			} elsif ($1 eq 'stage' or $1 eq 'stash') {
 				$patch_mode = $1;
 				$arg = shift @ARGV or die __("missing --");
diff --git a/git-compat-util.h b/git-compat-util.h
index cc0e7e9..83be89d 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -1,6 +1,15 @@
 #ifndef GIT_COMPAT_UTIL_H
 #define GIT_COMPAT_UTIL_H
 
+#ifdef USE_MSVC_CRTDBG
+/*
+ * For these to work they must appear very early in each
+ * file -- before most of the standard header files.
+ */
+#include <stdlib.h>
+#include <crtdbg.h>
+#endif
+
 #define _FILE_OFFSET_BITS 64
 
 
diff --git a/git-mergetool.sh b/git-mergetool.sh
index 88fa6a9..e3f6d54 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -228,9 +228,8 @@
 }
 
 checkout_staged_file () {
-	tmpfile=$(expr \
-		"$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
-		: '\([^	]*\)	')
+	tmpfile="$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" &&
+	tmpfile=${tmpfile%%'	'*}
 
 	if test $? -eq 0 && test -n "$tmpfile"
 	then
@@ -255,13 +254,16 @@
 		return 1
 	fi
 
-	if BASE=$(expr "$MERGED" : '\(.*\)\.[^/]*$')
-	then
-		ext=$(expr "$MERGED" : '.*\(\.[^/]*\)$')
-	else
+	# extract file extension from the last path component
+	case "${MERGED##*/}" in
+	*.*)
+		ext=.${MERGED##*.}
+		BASE=${MERGED%"$ext"}
+		;;
+	*)
 		BASE=$MERGED
 		ext=
-	fi
+	esac
 
 	mergetool_tmpdir_init
 
@@ -277,15 +279,30 @@
 	REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
 	BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
 
-	base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
-	local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
-	remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
+	base_mode= local_mode= remote_mode=
+
+	# here, $IFS is just a LF
+	for line in $f
+	do
+		mode=${line%% *}		# 1st word
+		sha1=${line#"$mode "}
+		sha1=${sha1%% *}		# 2nd word
+		case "${line#$mode $sha1 }" in	# remainder
+		'1	'*)
+			base_mode=$mode
+			;;
+		'2	'*)
+			local_mode=$mode local_sha1=$sha1
+			;;
+		'3	'*)
+			remote_mode=$mode remote_sha1=$sha1
+			;;
+		esac
+	done
 
 	if is_submodule "$local_mode" || is_submodule "$remote_mode"
 	then
 		echo "Submodule merge conflict for '$MERGED':"
-		local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}')
-		remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}')
 		describe_file "$local_mode" "local" "$local_sha1"
 		describe_file "$remote_mode" "remote" "$remote_sha1"
 		resolve_submodule_merge
@@ -406,7 +423,7 @@
 		-t|--tool*)
 			case "$#,$1" in
 			*,*=*)
-				merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
+				merge_tool=${1#*=}
 				;;
 			1,*)
 				usage ;;
diff --git a/git-p4.py b/git-p4.py
index c71a683..3991e7d 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -1316,7 +1316,7 @@
         self.needsGit = True
         self.verbose = False
 
-    # This is required for the "append" cloneExclude action
+    # This is required for the "append" update_shelve action
     def ensure_value(self, attr, value):
         if not hasattr(self, attr) or getattr(self, attr) is None:
             setattr(self, attr, value)
@@ -2530,6 +2530,11 @@
         die( "Error: %s is not found in client spec path" % depot_path )
         return ""
 
+def cloneExcludeCallback(option, opt_str, value, parser):
+    # prepend "/" because the first "/" was consumed as part of the option itself.
+    # ("-//depot/A/..." becomes "/depot/A/..." after option parsing)
+    parser.values.cloneExclude += ["/" + re.sub(r"\.\.\.$", "", value)]
+
 class P4Sync(Command, P4UserMap):
 
     def __init__(self):
@@ -2553,7 +2558,7 @@
                 optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
                                      help="Only sync files that are included in the Perforce Client Spec"),
                 optparse.make_option("-/", dest="cloneExclude",
-                                     action="append", type="string",
+                                     action="callback", callback=cloneExcludeCallback, type="string",
                                      help="exclude depot path"),
         ]
         self.description = """Imports from Perforce into a git repository.\n
@@ -2618,20 +2623,25 @@
         if self.verbose:
             print("checkpoint finished: " + out)
 
+    def isPathWanted(self, path):
+        for p in self.cloneExclude:
+            if p.endswith("/"):
+                if p4PathStartsWith(path, p):
+                    return False
+            # "-//depot/file1" without a trailing "/" should only exclude "file1", but not "file111" or "file1_dir/file2"
+            elif path.lower() == p.lower():
+                return False
+        for p in self.depotPaths:
+            if p4PathStartsWith(path, p):
+                return True
+        return False
+
     def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0):
-        self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
-                             for path in self.cloneExclude]
         files = []
         fnum = 0
         while "depotFile%s" % fnum in commit:
             path =  commit["depotFile%s" % fnum]
-
-            if [p for p in self.cloneExclude
-                if p4PathStartsWith(path, p)]:
-                found = False
-            else:
-                found = [p for p in self.depotPaths
-                         if p4PathStartsWith(path, p)]
+            found = self.isPathWanted(path)
             if not found:
                 fnum = fnum + 1
                 continue
@@ -2668,7 +2678,7 @@
             path = self.clientSpecDirs.map_in_client(path)
             if self.detectBranches:
                 for b in self.knownBranches:
-                    if path.startswith(b + "/"):
+                    if p4PathStartsWith(path, b + "/"):
                         path = path[len(b)+1:]
 
         elif self.keepRepoPath:
@@ -2700,8 +2710,7 @@
         fnum = 0
         while "depotFile%s" % fnum in commit:
             path =  commit["depotFile%s" % fnum]
-            found = [p for p in self.depotPaths
-                     if p4PathStartsWith(path, p)]
+            found = self.isPathWanted(path)
             if not found:
                 fnum = fnum + 1
                 continue
@@ -2723,7 +2732,7 @@
             for branch in self.knownBranches.keys():
                 # add a trailing slash so that a commit into qt/4.2foo
                 # doesn't end up in qt/4.2, e.g.
-                if relPath.startswith(branch + "/"):
+                if p4PathStartsWith(relPath, branch + "/"):
                     if branch not in branches:
                         branches[branch] = []
                     branches[branch].append(file)
@@ -3325,7 +3334,9 @@
             if currentChange < change:
                 earliestCommit = "^%s" % next
             else:
-                latestCommit = "%s" % next
+                if next == latestCommit:
+                    die("Infinite loop while looking in ref %s for change %s. Check your branch mappings" % (ref, change))
+                latestCommit = "%s^@" % next
 
         return ""
 
@@ -3888,7 +3899,6 @@
             self.cloneDestination = depotPaths[-1]
             depotPaths = depotPaths[:-1]
 
-        self.cloneExclude = ["/"+p for p in self.cloneExclude]
         for p in depotPaths:
             if not p.startswith("//"):
                 sys.stderr.write('Depot paths must start with "//": %s\n' % p)
diff --git a/git.c b/git.c
index c2eec47..f4c0478 100644
--- a/git.c
+++ b/git.c
@@ -566,6 +566,7 @@
 	{ "replace", cmd_replace, RUN_SETUP },
 	{ "rerere", cmd_rerere, RUN_SETUP },
 	{ "reset", cmd_reset, RUN_SETUP },
+	{ "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
 	{ "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
 	{ "rev-parse", cmd_rev_parse, NO_PARSEOPT },
 	{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
@@ -586,6 +587,7 @@
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
 	{ "stripspace", cmd_stripspace },
 	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
+	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
 	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
 	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
 	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
diff --git a/git.rc b/git.rc
index 49002e0..cc3fdc6 100644
--- a/git.rc
+++ b/git.rc
@@ -20,3 +20,5 @@
     VALUE "Translation", 0x409, 1200
   END
 END
+
+1 RT_MANIFEST "compat/win32/git.manifest"
diff --git a/hash.h b/hash.h
index 661c9f2..52a4f1a 100644
--- a/hash.h
+++ b/hash.h
@@ -139,4 +139,28 @@
 	return p - hash_algos;
 }
 
+/* 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_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];
+};
+
+#define the_hash_algo the_repository->hash_algo
+
 #endif
diff --git a/hashmap.h b/hashmap.h
index f95593b..8424911 100644
--- a/hashmap.h
+++ b/hashmap.h
@@ -1,6 +1,8 @@
 #ifndef HASHMAP_H
 #define HASHMAP_H
 
+#include "hash.h"
+
 /*
  * Generic implementation of hash-based key-value mappings.
  *
@@ -118,14 +120,14 @@
  * the results will be different on big-endian and little-endian
  * platforms, so they should not be stored or transferred over the net.
  */
-static inline unsigned int sha1hash(const unsigned char *sha1)
+static inline unsigned int oidhash(const struct object_id *oid)
 {
 	/*
-	 * Equivalent to 'return *(unsigned int *)sha1;', but safe on
+	 * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
 	 * platforms that don't support unaligned reads.
 	 */
 	unsigned int hash;
-	memcpy(&hash, sha1, sizeof(hash));
+	memcpy(&hash, oid->hash, sizeof(hash));
 	return hash;
 }
 
diff --git a/http-push.c b/http-push.c
index e36561a..0353f9f 100644
--- a/http-push.c
+++ b/http-push.c
@@ -723,7 +723,7 @@
 {
 	struct object *obj;
 
-	obj = lookup_object(the_repository, oid->hash);
+	obj = lookup_object(the_repository, oid);
 	if (!obj)
 		obj = parse_object(the_repository, oid);
 
@@ -1432,7 +1432,7 @@
 	 * may be required for updating server info later.
 	 */
 	if (repo->can_update_info_refs && !has_object_file(&ref->old_oid)) {
-		obj = lookup_unknown_object(ref->old_oid.hash);
+		obj = lookup_unknown_object(&ref->old_oid);
 		fprintf(stderr,	"  fetch %s for %s\n",
 			oid_to_hex(&ref->old_oid), refname);
 		add_fetch_request(obj);
diff --git a/khash.h b/khash.h
index af747a6..21c2095 100644
--- a/khash.h
+++ b/khash.h
@@ -324,30 +324,20 @@
 		code;												\
 	} }
 
-#define __kh_oid_cmp(a, b) (hashcmp(a, b) == 0)
-
-KHASH_INIT(sha1, const unsigned char *, void *, 1, sha1hash, __kh_oid_cmp)
-typedef kh_sha1_t khash_sha1;
-
-KHASH_INIT(sha1_pos, const unsigned char *, int, 1, sha1hash, __kh_oid_cmp)
-typedef kh_sha1_pos_t khash_sha1_pos;
-
-static inline unsigned int oid_hash(struct object_id oid)
+static inline unsigned int oidhash_by_value(struct object_id oid)
 {
-	return sha1hash(oid.hash);
+	return oidhash(&oid);
 }
 
-static inline int oid_equal(struct object_id a, struct object_id b)
+static inline int oideq_by_value(struct object_id a, struct object_id b)
 {
 	return oideq(&a, &b);
 }
 
-KHASH_INIT(oid, struct object_id, int, 0, oid_hash, oid_equal)
+KHASH_INIT(oid_set, struct object_id, int, 0, oidhash_by_value, oideq_by_value)
 
-KHASH_INIT(oid_map, struct object_id, void *, 1, oid_hash, oid_equal)
-typedef kh_oid_t khash_oid_map;
+KHASH_INIT(oid_map, struct object_id, void *, 1, oidhash_by_value, oideq_by_value)
 
-KHASH_INIT(oid_pos, struct object_id, int, 1, oid_hash, oid_equal)
-typedef kh_oid_pos_t khash_oid_pos;
+KHASH_INIT(oid_pos, struct object_id, int, 1, oidhash_by_value, oideq_by_value)
 
 #endif /* __AC_KHASH_H */
diff --git a/kwset.c b/kwset.c
index 4fb6455..fc439e0 100644
--- a/kwset.c
+++ b/kwset.c
@@ -38,7 +38,13 @@
 #include "compat/obstack.h"
 
 #define NCHAR (UCHAR_MAX + 1)
-#define obstack_chunk_alloc xmalloc
+/* adapter for `xmalloc()`, which takes `size_t`, not `long` */
+static void *obstack_chunk_alloc(long size)
+{
+	if (size < 0)
+		BUG("Cannot allocate a negative amount: %ld", size);
+	return xmalloc(size);
+}
 #define obstack_chunk_free free
 
 #define U(c) ((unsigned char) (c))
@@ -475,7 +481,7 @@
 	for (i = 0; i < NCHAR; ++i)
 	  kwset->next[i] = next[U(trans[i])];
       else
-	memcpy(kwset->next, next, NCHAR * sizeof(struct trie *));
+	COPY_ARRAY(kwset->next, next, NCHAR);
     }
 
   /* Fix things up for any translation table. */
diff --git a/name-hash.c b/name-hash.c
index b4861bc..6959086 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -345,8 +345,9 @@
 	else {
 		int begin = k_start;
 		int end = k_end;
+		assert(begin >= 0);
 		while (begin < end) {
-			int mid = (begin + end) >> 1;
+			int mid = begin + ((end - begin) >> 1);
 			int cmp = strncmp(istate->cache[mid]->name, prefix->buf, prefix->len);
 			if (cmp == 0) /* mid has same prefix; look in second part */
 				begin = mid + 1;
diff --git a/object.c b/object.c
index e81d47a..07bdd5b 100644
--- a/object.c
+++ b/object.c
@@ -59,9 +59,9 @@
  * the specified sha1.  n must be a power of 2.  Please note that the
  * return value is *not* consistent across computer architectures.
  */
-static unsigned int hash_obj(const unsigned char *sha1, unsigned int n)
+static unsigned int hash_obj(const struct object_id *oid, unsigned int n)
 {
-	return sha1hash(sha1) & (n - 1);
+	return oidhash(oid) & (n - 1);
 }
 
 /*
@@ -71,7 +71,7 @@
  */
 static void insert_obj_hash(struct object *obj, struct object **hash, unsigned int size)
 {
-	unsigned int j = hash_obj(obj->oid.hash, size);
+	unsigned int j = hash_obj(&obj->oid, size);
 
 	while (hash[j]) {
 		j++;
@@ -85,7 +85,7 @@
  * Look up the record for the given sha1 in the hash map stored in
  * obj_hash.  Return NULL if it was not found.
  */
-struct object *lookup_object(struct repository *r, const unsigned char *sha1)
+struct object *lookup_object(struct repository *r, const struct object_id *oid)
 {
 	unsigned int i, first;
 	struct object *obj;
@@ -93,9 +93,9 @@
 	if (!r->parsed_objects->obj_hash)
 		return NULL;
 
-	first = i = hash_obj(sha1, r->parsed_objects->obj_hash_size);
+	first = i = hash_obj(oid, r->parsed_objects->obj_hash_size);
 	while ((obj = r->parsed_objects->obj_hash[i]) != NULL) {
-		if (hasheq(sha1, obj->oid.hash))
+		if (oideq(oid, &obj->oid))
 			break;
 		i++;
 		if (i == r->parsed_objects->obj_hash_size)
@@ -141,13 +141,13 @@
 	r->parsed_objects->obj_hash_size = new_hash_size;
 }
 
-void *create_object(struct repository *r, const unsigned char *sha1, void *o)
+void *create_object(struct repository *r, const struct object_id *oid, void *o)
 {
 	struct object *obj = o;
 
 	obj->parsed = 0;
 	obj->flags = 0;
-	hashcpy(obj->oid.hash, sha1);
+	oidcpy(&obj->oid, oid);
 
 	if (r->parsed_objects->obj_hash_size - 1 <= r->parsed_objects->nr_objs * 2)
 		grow_object_hash(r);
@@ -178,11 +178,11 @@
 	}
 }
 
-struct object *lookup_unknown_object(const unsigned char *sha1)
+struct object *lookup_unknown_object(const struct object_id *oid)
 {
-	struct object *obj = lookup_object(the_repository, sha1);
+	struct object *obj = lookup_object(the_repository, oid);
 	if (!obj)
-		obj = create_object(the_repository, sha1,
+		obj = create_object(the_repository, oid,
 				    alloc_object_node(the_repository));
 	return obj;
 }
@@ -256,7 +256,7 @@
 	void *buffer;
 	struct object *obj;
 
-	obj = lookup_object(r, oid->hash);
+	obj = lookup_object(r, oid);
 	if (obj && obj->parsed)
 		return obj;
 
@@ -268,7 +268,7 @@
 			return NULL;
 		}
 		parse_blob_buffer(lookup_blob(r, oid), NULL, 0);
-		return lookup_object(r, oid->hash);
+		return lookup_object(r, oid);
 	}
 
 	buffer = repo_read_object_file(r, oid, &type, &size);
@@ -517,7 +517,7 @@
 	o->loaded_alternates = 0;
 
 	INIT_LIST_HEAD(&o->packed_git_mru);
-	close_all_packs(o);
+	close_object_store(o);
 	o->packed_git = NULL;
 }
 
diff --git a/object.h b/object.h
index 4526979..0120892 100644
--- a/object.h
+++ b/object.h
@@ -116,9 +116,9 @@
  * half-initialised objects, the caller is expected to initialize them
  * by calling parse_object() on them.
  */
-struct object *lookup_object(struct repository *r, const unsigned char *sha1);
+struct object *lookup_object(struct repository *r, const struct object_id *oid);
 
-void *create_object(struct repository *r, const unsigned char *sha1, void *obj);
+void *create_object(struct repository *r, const struct object_id *oid, void *obj);
 
 void *object_as_type(struct repository *r, struct object *obj, enum object_type type, int quiet);
 
@@ -143,7 +143,7 @@
 struct object *parse_object_buffer(struct repository *r, const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p);
 
 /** Returns the object, with potentially excess memory allocated. **/
-struct object *lookup_unknown_object(const unsigned  char *sha1);
+struct object *lookup_unknown_object(const struct object_id *oid);
 
 struct object_list *object_list_insert(struct object *item,
 				       struct object_list **list_p);
diff --git a/oidset.c b/oidset.c
index fe4eb92..8bdecb1 100644
--- a/oidset.c
+++ b/oidset.c
@@ -5,33 +5,33 @@
 {
 	memset(&set->set, 0, sizeof(set->set));
 	if (initial_size)
-		kh_resize_oid(&set->set, initial_size);
+		kh_resize_oid_set(&set->set, initial_size);
 }
 
 int oidset_contains(const struct oidset *set, const struct object_id *oid)
 {
-	khiter_t pos = kh_get_oid(&set->set, *oid);
+	khiter_t pos = kh_get_oid_set(&set->set, *oid);
 	return pos != kh_end(&set->set);
 }
 
 int oidset_insert(struct oidset *set, const struct object_id *oid)
 {
 	int added;
-	kh_put_oid(&set->set, *oid, &added);
+	kh_put_oid_set(&set->set, *oid, &added);
 	return !added;
 }
 
 int oidset_remove(struct oidset *set, const struct object_id *oid)
 {
-	khiter_t pos = kh_get_oid(&set->set, *oid);
+	khiter_t pos = kh_get_oid_set(&set->set, *oid);
 	if (pos == kh_end(&set->set))
 		return 0;
-	kh_del_oid(&set->set, pos);
+	kh_del_oid_set(&set->set, pos);
 	return 1;
 }
 
 void oidset_clear(struct oidset *set)
 {
-	kh_release_oid(&set->set);
+	kh_release_oid_set(&set->set);
 	oidset_init(set, 0);
 }
diff --git a/oidset.h b/oidset.h
index 14f18f7..505fad5 100644
--- a/oidset.h
+++ b/oidset.h
@@ -20,7 +20,7 @@
  * A single oidset; should be zero-initialized (or use OIDSET_INIT).
  */
 struct oidset {
-	kh_oid_t set;
+	kh_oid_set_t set;
 };
 
 #define OIDSET_INIT { { 0 } }
@@ -62,7 +62,7 @@
 void oidset_clear(struct oidset *set);
 
 struct oidset_iter {
-	kh_oid_t *set;
+	kh_oid_set_t *set;
 	khiter_t iter;
 };
 
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index 802ed62..fa78a46 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -28,8 +28,8 @@
 	struct ewah_bitmap *blobs;
 	struct ewah_bitmap *tags;
 
-	khash_sha1 *bitmaps;
-	khash_sha1 *reused;
+	kh_oid_map_t *bitmaps;
+	kh_oid_map_t *reused;
 	struct packing_data *to_pack;
 
 	struct bitmapped_commit *selected;
@@ -142,13 +142,13 @@
 	seen_objects_nr = 0;
 }
 
-static uint32_t find_object_pos(const unsigned char *hash)
+static uint32_t find_object_pos(const struct object_id *oid)
 {
-	struct object_entry *entry = packlist_find(writer.to_pack, hash, NULL);
+	struct object_entry *entry = packlist_find(writer.to_pack, oid, NULL);
 
 	if (!entry) {
 		die("Failed to write bitmap index. Packfile doesn't have full closure "
-			"(object %s is missing)", hash_to_hex(hash));
+			"(object %s is missing)", oid_to_hex(oid));
 	}
 
 	return oe_in_pack_pos(writer.to_pack, entry);
@@ -157,7 +157,7 @@
 static void show_object(struct object *object, const char *name, void *data)
 {
 	struct bitmap *base = data;
-	bitmap_set(base, find_object_pos(object->oid.hash));
+	bitmap_set(base, find_object_pos(&object->oid));
 	mark_as_seen(object);
 }
 
@@ -170,12 +170,12 @@
 add_to_include_set(struct bitmap *base, struct commit *commit)
 {
 	khiter_t hash_pos;
-	uint32_t bitmap_pos = find_object_pos(commit->object.oid.hash);
+	uint32_t bitmap_pos = find_object_pos(&commit->object.oid);
 
 	if (bitmap_get(base, bitmap_pos))
 		return 0;
 
-	hash_pos = kh_get_sha1(writer.bitmaps, commit->object.oid.hash);
+	hash_pos = kh_get_oid_map(writer.bitmaps, commit->object.oid);
 	if (hash_pos < kh_end(writer.bitmaps)) {
 		struct bitmapped_commit *bc = kh_value(writer.bitmaps, hash_pos);
 		bitmap_or_ewah(base, bc->bitmap);
@@ -256,7 +256,7 @@
 	struct bitmap *base = bitmap_new();
 	struct rev_info revs;
 
-	writer.bitmaps = kh_init_sha1();
+	writer.bitmaps = kh_init_oid_map();
 	writer.to_pack = to_pack;
 
 	if (writer.show_progress)
@@ -311,7 +311,7 @@
 		if (i >= reuse_after)
 			stored->flags |= BITMAP_FLAG_REUSE;
 
-		hash_pos = kh_put_sha1(writer.bitmaps, object->oid.hash, &hash_ret);
+		hash_pos = kh_put_oid_map(writer.bitmaps, object->oid, &hash_ret);
 		if (hash_ret == 0)
 			die("Duplicate entry when writing index: %s",
 			    oid_to_hex(&object->oid));
@@ -366,7 +366,7 @@
 	if (!(bitmap_git = prepare_bitmap_git(to_pack->repo)))
 		return;
 
-	writer.reused = kh_init_sha1();
+	writer.reused = kh_init_oid_map();
 	rebuild_existing_bitmaps(bitmap_git, to_pack, writer.reused,
 				 writer.show_progress);
 	/*
@@ -375,14 +375,14 @@
 	 */
 }
 
-static struct ewah_bitmap *find_reused_bitmap(const unsigned char *sha1)
+static struct ewah_bitmap *find_reused_bitmap(const struct object_id *oid)
 {
 	khiter_t hash_pos;
 
 	if (!writer.reused)
 		return NULL;
 
-	hash_pos = kh_get_sha1(writer.reused, sha1);
+	hash_pos = kh_get_oid_map(writer.reused, *oid);
 	if (hash_pos >= kh_end(writer.reused))
 		return NULL;
 
@@ -422,14 +422,14 @@
 
 		if (next == 0) {
 			chosen = indexed_commits[i];
-			reused_bitmap = find_reused_bitmap(chosen->object.oid.hash);
+			reused_bitmap = find_reused_bitmap(&chosen->object.oid);
 		} else {
 			chosen = indexed_commits[i + next];
 
 			for (j = 0; j <= next; ++j) {
 				struct commit *cm = indexed_commits[i + j];
 
-				reused_bitmap = find_reused_bitmap(cm->object.oid.hash);
+				reused_bitmap = find_reused_bitmap(&cm->object.oid);
 				if (reused_bitmap || (cm->object.flags & NEEDS_BITMAP) != 0) {
 					chosen = cm;
 					break;
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 6069b2f..ed2befa 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -365,7 +365,7 @@
 static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
 					   const struct object_id *oid)
 {
-	khash_oid_pos *positions = bitmap_git->ext_index.positions;
+	kh_oid_pos_t *positions = bitmap_git->ext_index.positions;
 	khiter_t pos = kh_get_oid_pos(positions, *oid);
 
 	if (pos < kh_end(positions)) {
@@ -1041,7 +1041,7 @@
 
 int rebuild_existing_bitmaps(struct bitmap_index *bitmap_git,
 			     struct packing_data *mapping,
-			     khash_sha1 *reused_bitmaps,
+			     kh_oid_map_t *reused_bitmaps,
 			     int show_progress)
 {
 	uint32_t i, num_objects;
@@ -1057,13 +1057,13 @@
 	reposition = xcalloc(num_objects, sizeof(uint32_t));
 
 	for (i = 0; i < num_objects; ++i) {
-		const unsigned char *sha1;
+		struct object_id oid;
 		struct revindex_entry *entry;
 		struct object_entry *oe;
 
 		entry = &bitmap_git->pack->revindex[i];
-		sha1 = nth_packed_object_sha1(bitmap_git->pack, entry->nr);
-		oe = packlist_find(mapping, sha1, NULL);
+		nth_packed_object_oid(&oid, bitmap_git->pack, entry->nr);
+		oe = packlist_find(mapping, &oid, NULL);
 
 		if (oe)
 			reposition[i] = oe_in_pack_pos(mapping, oe) + 1;
@@ -1080,9 +1080,9 @@
 			if (!rebuild_bitmap(reposition,
 					    lookup_stored_bitmap(stored),
 					    rebuild)) {
-				hash_pos = kh_put_sha1(reused_bitmaps,
-						       stored->oid.hash,
-						       &hash_ret);
+				hash_pos = kh_put_oid_map(reused_bitmaps,
+							  stored->oid,
+							  &hash_ret);
 				kh_value(reused_bitmaps, hash_pos) =
 					bitmap_to_ewah(rebuild);
 			}
diff --git a/pack-bitmap.h b/pack-bitmap.h
index ee97922..00de3ec 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -51,7 +51,7 @@
 				       struct packed_git **packfile,
 				       uint32_t *entries, off_t *up_to);
 int rebuild_existing_bitmaps(struct bitmap_index *, struct packing_data *mapping,
-			     khash_sha1 *reused_bitmaps, int show_progress);
+			     kh_oid_map_t *reused_bitmaps, int show_progress);
 void free_bitmap_index(struct bitmap_index *);
 
 /*
diff --git a/pack-objects.c b/pack-objects.c
index ce33b89..5256029 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -6,17 +6,17 @@
 #include "config.h"
 
 static uint32_t locate_object_entry_hash(struct packing_data *pdata,
-					 const unsigned char *sha1,
+					 const struct object_id *oid,
 					 int *found)
 {
 	uint32_t i, mask = (pdata->index_size - 1);
 
-	i = sha1hash(sha1) & mask;
+	i = oidhash(oid) & mask;
 
 	while (pdata->index[i] > 0) {
 		uint32_t pos = pdata->index[i] - 1;
 
-		if (hasheq(sha1, pdata->objects[pos].idx.oid.hash)) {
+		if (oideq(oid, &pdata->objects[pos].idx.oid)) {
 			*found = 1;
 			return i;
 		}
@@ -56,7 +56,7 @@
 	for (i = 0; i < pdata->nr_objects; i++) {
 		int found;
 		uint32_t ix = locate_object_entry_hash(pdata,
-						       entry->idx.oid.hash,
+						       &entry->idx.oid,
 						       &found);
 
 		if (found)
@@ -68,7 +68,7 @@
 }
 
 struct object_entry *packlist_find(struct packing_data *pdata,
-				   const unsigned char *sha1,
+				   const struct object_id *oid,
 				   uint32_t *index_pos)
 {
 	uint32_t i;
@@ -77,7 +77,7 @@
 	if (!pdata->index_size)
 		return NULL;
 
-	i = locate_object_entry_hash(pdata, sha1, &found);
+	i = locate_object_entry_hash(pdata, oid, &found);
 
 	if (index_pos)
 		*index_pos = i;
diff --git a/pack-objects.h b/pack-objects.h
index 6fde7ce..857d438 100644
--- a/pack-objects.h
+++ b/pack-objects.h
@@ -187,7 +187,7 @@
 				    uint32_t index_pos);
 
 struct object_entry *packlist_find(struct packing_data *pdata,
-				   const unsigned char *sha1,
+				   const struct object_id *oid,
 				   uint32_t *index_pos);
 
 static inline uint32_t pack_name_hash(const char *name)
diff --git a/packfile.c b/packfile.c
index d786ec7..c0d83fd 100644
--- a/packfile.c
+++ b/packfile.c
@@ -16,6 +16,7 @@
 #include "tree.h"
 #include "object-store.h"
 #include "midx.h"
+#include "commit-graph.h"
 
 char *odb_pack_name(struct strbuf *buf,
 		    const unsigned char *sha1,
@@ -336,7 +337,7 @@
 	close_pack_index(p);
 }
 
-void close_all_packs(struct raw_object_store *o)
+void close_object_store(struct raw_object_store *o)
 {
 	struct packed_git *p;
 
@@ -350,6 +351,8 @@
 		close_midx(o->multi_pack_index);
 		o->multi_pack_index = NULL;
 	}
+
+	close_commit_graph(o);
 }
 
 /*
@@ -1269,7 +1272,7 @@
 		if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
 			poi_stack_alloc = alloc_nr(poi_stack_nr);
 			ALLOC_ARRAY(poi_stack, poi_stack_alloc);
-			memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
+			COPY_ARRAY(poi_stack, small_poi_stack, poi_stack_nr);
 		} else {
 			ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
 		}
@@ -1679,8 +1682,8 @@
 		    && delta_stack == small_delta_stack) {
 			delta_stack_alloc = alloc_nr(delta_stack_nr);
 			ALLOC_ARRAY(delta_stack, delta_stack_alloc);
-			memcpy(delta_stack, small_delta_stack,
-			       sizeof(*delta_stack)*delta_stack_nr);
+			COPY_ARRAY(delta_stack, small_delta_stack,
+				   delta_stack_nr);
 		} else {
 			ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
 		}
diff --git a/packfile.h b/packfile.h
index b678d35..81e868d 100644
--- a/packfile.h
+++ b/packfile.h
@@ -90,7 +90,7 @@
 unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 void close_pack_windows(struct packed_git *);
 void close_pack(struct packed_git *);
-void close_all_packs(struct raw_object_store *o);
+void close_object_store(struct raw_object_store *o);
 void unuse_pack(struct pack_window **);
 void clear_delta_base_cache(void);
 struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
diff --git a/pager.c b/pager.c
index 4168460..41446d4 100644
--- a/pager.c
+++ b/pager.c
@@ -178,6 +178,26 @@
 }
 
 /*
+ * Clear the entire line, leave cursor in first column.
+ */
+void term_clear_line(void)
+{
+	if (is_terminal_dumb())
+		/*
+		 * Fall back to print a terminal width worth of space
+		 * characters (hoping that the terminal is still as wide
+		 * as it was upon the first call to term_columns()).
+		 */
+		fprintf(stderr, "\r%*s\r", term_columns(), "");
+	else
+		/*
+		 * On non-dumb terminals use an escape sequence to clear
+		 * the whole line, no matter how wide the terminal.
+		 */
+		fputs("\r\033[K", stderr);
+}
+
+/*
  * How many columns do we need to show this number in decimal?
  */
 int decimal_width(uintmax_t number)
diff --git a/parse-options-cb.c b/parse-options-cb.c
index a3de795..1240a85 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -159,6 +159,23 @@
 	return 0;
 }
 
+struct option *parse_options_dup(const struct option *o)
+{
+	struct option *opts;
+	int nr = 0;
+
+	while (o && o->type != OPTION_END) {
+		nr++;
+		o++;
+	}
+
+	ALLOC_ARRAY(opts, nr + 1);
+	memcpy(opts, o - nr, sizeof(*o) * nr);
+	memset(opts + nr, 0, sizeof(*opts));
+	opts[nr].type = OPTION_END;
+	return opts;
+}
+
 struct option *parse_options_concat(struct option *a, struct option *b)
 {
 	struct option *ret;
diff --git a/parse-options.h b/parse-options.h
index ac6ba8a..a4bd40b 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -276,6 +276,7 @@
 
 int parse_options_end(struct parse_opt_ctx_t *ctx);
 
+struct option *parse_options_dup(const struct option *a);
 struct option *parse_options_concat(struct option *a, struct option *b);
 
 /*----- some often used options -----*/
diff --git a/patch-ids.c b/patch-ids.c
index f70d396..e8c150d 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -83,7 +83,7 @@
 	if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1, 0))
 		return -1;
 
-	hashmap_entry_init(patch, sha1hash(header_only_patch_id.hash));
+	hashmap_entry_init(patch, oidhash(&header_only_patch_id));
 	return 0;
 }
 
diff --git a/pretty.c b/pretty.c
index ced0485..e4ed14e 100644
--- a/pretty.c
+++ b/pretty.c
@@ -106,8 +106,8 @@
 	commit_formats_len = ARRAY_SIZE(builtin_formats);
 	builtin_formats_len = commit_formats_len;
 	ALLOC_GROW(commit_formats, commit_formats_len, commit_formats_alloc);
-	memcpy(commit_formats, builtin_formats,
-	       sizeof(*builtin_formats)*ARRAY_SIZE(builtin_formats));
+	COPY_ARRAY(commit_formats, builtin_formats,
+		   ARRAY_SIZE(builtin_formats));
 
 	git_config(git_pretty_formats_config, NULL);
 }
diff --git a/progress.c b/progress.c
index a2e8cf6..277db8a 100644
--- a/progress.c
+++ b/progress.c
@@ -88,7 +88,6 @@
 	const char *tp;
 	struct strbuf *counters_sb = &progress->counters_sb;
 	int show_update = 0;
-	int last_count_len = counters_sb->len;
 
 	if (progress->delay && (!progress_update || --progress->delay))
 		return;
@@ -116,26 +115,21 @@
 	if (show_update) {
 		if (is_foreground_fd(fileno(stderr)) || done) {
 			const char *eol = done ? done : "\r";
-			size_t clear_len = counters_sb->len < last_count_len ?
-					last_count_len - counters_sb->len + 1 :
-					0;
-			size_t progress_line_len = progress->title_len +
-						counters_sb->len + 2;
-			int cols = term_columns();
 
+			term_clear_line();
 			if (progress->split) {
-				fprintf(stderr, "  %s%*s", counters_sb->buf,
-					(int) clear_len, eol);
-			} else if (!done && cols < progress_line_len) {
-				clear_len = progress->title_len + 1 < cols ?
-					    cols - progress->title_len - 1 : 0;
-				fprintf(stderr, "%s:%*s\n  %s%s",
-					progress->title, (int) clear_len, "",
-					counters_sb->buf, eol);
+				fprintf(stderr, "  %s%s", counters_sb->buf,
+					eol);
+			} else if (!done &&
+				   /* The "+ 2" accounts for the ": ". */
+				   term_columns() < progress->title_len +
+						    counters_sb->len + 2) {
+				fprintf(stderr, "%s:\n  %s%s",
+					progress->title, counters_sb->buf, eol);
 				progress->split = 1;
 			} else {
-				fprintf(stderr, "%s: %s%*s", progress->title,
-					counters_sb->buf, (int) clear_len, eol);
+				fprintf(stderr, "%s: %s%s", progress->title,
+					counters_sb->buf, eol);
 			}
 			fflush(stderr);
 		}
@@ -150,8 +144,7 @@
 	strbuf_addstr(buf, ", ");
 	strbuf_humanise_bytes(buf, total);
 	strbuf_addstr(buf, " | ");
-	strbuf_humanise_bytes(buf, rate * 1024);
-	strbuf_addstr(buf, "/s");
+	strbuf_humanise_rate(buf, rate * 1024);
 }
 
 void display_throughput(struct progress *progress, uint64_t total)
diff --git a/reachable.c b/reachable.c
index 0d00a91..8f50235 100644
--- a/reachable.c
+++ b/reachable.c
@@ -109,7 +109,7 @@
 			    const char *path, void *data)
 {
 	struct stat st;
-	struct object *obj = lookup_object(the_repository, oid->hash);
+	struct object *obj = lookup_object(the_repository, oid);
 
 	if (obj && obj->flags & SEEN)
 		return 0;
@@ -134,7 +134,7 @@
 			     struct packed_git *p, uint32_t pos,
 			     void *data)
 {
-	struct object *obj = lookup_object(the_repository, oid->hash);
+	struct object *obj = lookup_object(the_repository, oid);
 
 	if (obj && obj->flags & SEEN)
 		return 0;
diff --git a/read-cache.c b/read-cache.c
index 4dd22f4..c701f7f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -549,7 +549,7 @@
 	first = 0;
 	last = istate->cache_nr;
 	while (last > first) {
-		int next = (last + first) >> 1;
+		int next = first + ((last - first) >> 1);
 		struct cache_entry *ce = istate->cache[next];
 		int cmp = cache_name_stage_compare(name, namelen, stage, ce->name, ce_namelen(ce), ce_stage(ce));
 		if (!cmp)
diff --git a/ref-filter.c b/ref-filter.c
index 8500671..791f064 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -20,6 +20,8 @@
 #include "commit-slab.h"
 #include "commit-graph.h"
 #include "commit-reach.h"
+#include "worktree.h"
+#include "hashmap.h"
 
 static struct ref_msg {
 	const char *gone;
@@ -75,6 +77,27 @@
 	struct object_info info;
 } oi, oi_deref;
 
+struct ref_to_worktree_entry {
+	struct hashmap_entry ent; /* must be the first member! */
+	struct worktree *wt; /* key is wt->head_ref */
+};
+
+static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata,
+				      const void *existing_hashmap_entry_to_test,
+				      const void *key,
+				      const void *keydata_aka_refname)
+{
+	const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test;
+	const struct ref_to_worktree_entry *k = key;
+	return strcmp(e->wt->head_ref,
+		keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);
+}
+
+static struct ref_to_worktree_map {
+	struct hashmap map;
+	struct worktree **worktrees;
+} ref_to_worktree_map;
+
 /*
  * An atom is a valid field atom listed below, possibly prefixed with
  * a "*" to denote deref_tag().
@@ -480,6 +503,7 @@
 	{ "flag", SOURCE_NONE },
 	{ "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser },
 	{ "color", SOURCE_NONE, FIELD_STR, color_atom_parser },
+	{ "worktreepath", SOURCE_NONE },
 	{ "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
 	{ "end", SOURCE_NONE },
 	{ "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
@@ -1447,35 +1471,35 @@
 	struct wt_status_state state;
 	memset(&state, 0, sizeof(state));
 	wt_status_get_state(the_repository, &state, 1);
+
+	/*
+	 * The ( character must be hard-coded and not part of a localizable
+	 * string, since the description is used as a sort key and compared
+	 * with ref names.
+	 */
+	strbuf_addch(&desc, '(');
 	if (state.rebase_in_progress ||
 	    state.rebase_interactive_in_progress) {
 		if (state.branch)
-			strbuf_addf(&desc, _("(no branch, rebasing %s)"),
+			strbuf_addf(&desc, _("no branch, rebasing %s"),
 				    state.branch);
 		else
-			strbuf_addf(&desc, _("(no branch, rebasing detached HEAD %s)"),
+			strbuf_addf(&desc, _("no branch, rebasing detached HEAD %s"),
 				    state.detached_from);
 	} else if (state.bisect_in_progress)
-		strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
+		strbuf_addf(&desc, _("no branch, bisect started on %s"),
 			    state.branch);
 	else if (state.detached_from) {
 		if (state.detached_at)
-			/*
-			 * TRANSLATORS: make sure this matches "HEAD
-			 * detached at " in wt-status.c
-			 */
-			strbuf_addf(&desc, _("(HEAD detached at %s)"),
-				state.detached_from);
+			strbuf_addstr(&desc, HEAD_DETACHED_AT);
 		else
-			/*
-			 * TRANSLATORS: make sure this matches "HEAD
-			 * detached from " in wt-status.c
-			 */
-			strbuf_addf(&desc, _("(HEAD detached from %s)"),
-				state.detached_from);
+			strbuf_addstr(&desc, HEAD_DETACHED_FROM);
+		strbuf_addstr(&desc, state.detached_from);
 	}
 	else
-		strbuf_addstr(&desc, _("(no branch)"));
+		strbuf_addstr(&desc, _("no branch"));
+	strbuf_addch(&desc, ')');
+
 	free(state.branch);
 	free(state.onto);
 	free(state.detached_from);
@@ -1531,6 +1555,48 @@
 	return 0;
 }
 
+static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
+{
+	int i;
+
+	for (i = 0; worktrees[i]; i++) {
+		if (worktrees[i]->head_ref) {
+			struct ref_to_worktree_entry *entry;
+			entry = xmalloc(sizeof(*entry));
+			entry->wt = worktrees[i];
+			hashmap_entry_init(entry, strhash(worktrees[i]->head_ref));
+
+			hashmap_add(map, entry);
+		}
+	}
+}
+
+static void lazy_init_worktree_map(void)
+{
+	if (ref_to_worktree_map.worktrees)
+		return;
+
+	ref_to_worktree_map.worktrees = get_worktrees(0);
+	hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0);
+	populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
+}
+
+static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)
+{
+	struct hashmap_entry entry;
+	struct ref_to_worktree_entry *lookup_result;
+
+	lazy_init_worktree_map();
+
+	hashmap_entry_init(&entry, strhash(ref->refname));
+	lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);
+
+	if (lookup_result)
+		return xstrdup(lookup_result->wt->path);
+	else
+		return xstrdup("");
+}
+
 /*
  * Parse the object referred by ref, and grab needed value.
  */
@@ -1568,6 +1634,13 @@
 
 		if (starts_with(name, "refname"))
 			refname = get_refname(atom, ref);
+		else if (!strcmp(name, "worktreepath")) {
+			if (ref->kind == FILTER_REFS_BRANCHES)
+				v->s = get_worktree_path(atom, ref);
+			else
+				v->s = xstrdup("");
+			continue;
+		}
 		else if (starts_with(name, "symref"))
 			refname = get_symref(atom, ref);
 		else if (starts_with(name, "upstream")) {
@@ -2051,6 +2124,11 @@
 		free_array_item(array->items[i]);
 	FREE_AND_NULL(array->items);
 	array->nr = array->alloc = 0;
+	if (ref_to_worktree_map.worktrees) {
+		hashmap_free(&(ref_to_worktree_map.map), 1);
+		free_worktrees(ref_to_worktree_map.worktrees);
+		ref_to_worktree_map.worktrees = NULL;
+	}
 }
 
 static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
diff --git a/refs.c b/refs.c
index b8a8430..cd297ee 100644
--- a/refs.c
+++ b/refs.c
@@ -379,7 +379,7 @@
 
 enum peel_status peel_object(const struct object_id *name, struct object_id *oid)
 {
-	struct object *o = lookup_unknown_object(name->hash);
+	struct object *o = lookup_unknown_object(name);
 
 	if (o->type == OBJ_NONE) {
 		int type = oid_object_info(the_repository, name, NULL);
diff --git a/sequencer.c b/sequencer.c
index ab74b6b..cf26270 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -279,7 +279,7 @@
 int sequencer_remove_state(struct replay_opts *opts)
 {
 	struct strbuf buf = STRBUF_INIT;
-	int i;
+	int i, ret = 0;
 
 	if (is_rebase_i(opts) &&
 	    strbuf_read_file(&buf, rebase_path_refs_to_delete(), 0) > 0) {
@@ -288,8 +288,10 @@
 			char *eol = strchr(p, '\n');
 			if (eol)
 				*eol = '\0';
-			if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0)
+			if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0) {
 				warning(_("could not delete '%s'"), p);
+				ret = -1;
+			}
 			if (!eol)
 				break;
 			p = eol + 1;
@@ -305,10 +307,11 @@
 
 	strbuf_reset(&buf);
 	strbuf_addstr(&buf, get_dir(opts));
-	remove_dir_recursively(&buf, 0);
+	if (remove_dir_recursively(&buf, 0))
+		ret = error(_("could not remove '%s'"), buf.buf);
 	strbuf_release(&buf);
 
-	return 0;
+	return ret;
 }
 
 static const char *action_name(const struct replay_opts *opts)
@@ -2311,19 +2314,21 @@
 	return ret;
 }
 
-void sequencer_post_commit_cleanup(struct repository *r)
+void sequencer_post_commit_cleanup(struct repository *r, int verbose)
 {
 	struct replay_opts opts = REPLAY_OPTS_INIT;
 	int need_cleanup = 0;
 
 	if (file_exists(git_path_cherry_pick_head(r))) {
-		unlink(git_path_cherry_pick_head(r));
+		if (!unlink(git_path_cherry_pick_head(r)) && verbose)
+			warning(_("cancelling a cherry picking in progress"));
 		opts.action = REPLAY_PICK;
 		need_cleanup = 1;
 	}
 
 	if (file_exists(git_path_revert_head(r))) {
-		unlink(git_path_revert_head(r));
+		if (!unlink(git_path_revert_head(r)) && verbose)
+			warning(_("cancelling a revert in progress"));
 		opts.action = REPLAY_REVERT;
 		need_cleanup = 1;
 	}
@@ -3736,8 +3741,11 @@
 			unlink(git_path_merge_head(the_repository));
 			delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 
-			if (item->command == TODO_BREAK)
+			if (item->command == TODO_BREAK) {
+				if (!opts->verbose)
+					term_clear_line();
 				return stopped_at_head(r);
+			}
 		}
 		if (item->command <= TODO_SQUASH) {
 			if (is_rebase_i(opts))
@@ -3759,11 +3767,14 @@
 			}
 			if (item->command == TODO_EDIT) {
 				struct commit *commit = item->commit;
-				if (!res)
+				if (!res) {
+					if (!opts->verbose)
+						term_clear_line();
 					fprintf(stderr,
 						_("Stopped at %s...  %.*s\n"),
 						short_commit_name(commit),
 						item->arg_len, arg);
+				}
 				return error_with_patch(r, commit,
 					arg, item->arg_len, opts, res, !res);
 			}
@@ -3801,6 +3812,8 @@
 			int saved = *end_of_arg;
 			struct stat st;
 
+			if (!opts->verbose)
+				term_clear_line();
 			*end_of_arg = '\0';
 			res = do_exec(r, arg);
 			*end_of_arg = saved;
@@ -3959,10 +3972,13 @@
 		}
 		apply_autostash(opts);
 
-		if (!opts->quiet)
+		if (!opts->quiet) {
+			if (!opts->verbose)
+				term_clear_line();
 			fprintf(stderr,
 				"Successfully rebased and updated %s.\n",
 				head_ref.buf);
+		}
 
 		strbuf_release(&buf);
 		strbuf_release(&head_ref);
diff --git a/sequencer.h b/sequencer.h
index 0c494b8..3d0b68c 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -200,6 +200,6 @@
 void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
 int write_basic_state(struct replay_opts *opts, const char *head_name,
 		      struct commit *onto, const char *orig_head);
-void sequencer_post_commit_cleanup(struct repository *r);
+void sequencer_post_commit_cleanup(struct repository *r, int verbose);
 int sequencer_get_last_command(struct repository* r,
 			       enum replay_action *action);
diff --git a/sh-i18n--envsubst.c b/sh-i18n--envsubst.c
index cecfdd3..e7430b9 100644
--- a/sh-i18n--envsubst.c
+++ b/sh-i18n--envsubst.c
@@ -249,7 +249,7 @@
 	{
 	  /* Here we know that if s is in the list, it is at an index j
 	     with j1 <= j < j2.  */
-	  size_t j = (j1 + j2) >> 1;
+	  size_t j = j1 + ((j2 - j1) >> 1);
 	  int result = strcmp (slp->item[j], s);
 
 	  if (result > 0)
diff --git a/sha1-name.c b/sha1-name.c
index 728e6f1..49855ad 100644
--- a/sha1-name.c
+++ b/sha1-name.c
@@ -801,7 +801,7 @@
 	"because it will be ignored when you just specify 40-hex. These refs\n"
 	"may be created by mistake. For example,\n"
 	"\n"
-	"  git checkout -b $br $(git rev-parse ...)\n"
+	"  git switch -c $br $(git rev-parse ...)\n"
 	"\n"
 	"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
 	"examine these refs and maybe delete them. Turn this message off by\n"
diff --git a/strbuf.c b/strbuf.c
index 0e18b25..d30f916 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -811,25 +811,57 @@
 	strbuf_add_urlencode(sb, s, strlen(s), reserved);
 }
 
-void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes)
+static void strbuf_humanise(struct strbuf *buf, off_t bytes,
+				 int humanise_rate)
 {
 	if (bytes > 1 << 30) {
-		strbuf_addf(buf, "%u.%2.2u GiB",
+		strbuf_addf(buf,
+				humanise_rate == 0 ?
+					/* TRANSLATORS: IEC 80000-13:2008 gibibyte */
+					_("%u.%2.2u GiB") :
+					/* TRANSLATORS: IEC 80000-13:2008 gibibyte/second */
+					_("%u.%2.2u GiB/s"),
 			    (unsigned)(bytes >> 30),
 			    (unsigned)(bytes & ((1 << 30) - 1)) / 10737419);
 	} else if (bytes > 1 << 20) {
 		unsigned x = bytes + 5243;  /* for rounding */
-		strbuf_addf(buf, "%u.%2.2u MiB",
+		strbuf_addf(buf,
+				humanise_rate == 0 ?
+					/* TRANSLATORS: IEC 80000-13:2008 mebibyte */
+					_("%u.%2.2u MiB") :
+					/* TRANSLATORS: IEC 80000-13:2008 mebibyte/second */
+					_("%u.%2.2u MiB/s"),
 			    x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
 	} else if (bytes > 1 << 10) {
 		unsigned x = bytes + 5;  /* for rounding */
-		strbuf_addf(buf, "%u.%2.2u KiB",
+		strbuf_addf(buf,
+				humanise_rate == 0 ?
+					/* TRANSLATORS: IEC 80000-13:2008 kibibyte */
+					_("%u.%2.2u KiB") :
+					/* TRANSLATORS: IEC 80000-13:2008 kibibyte/second */
+					_("%u.%2.2u KiB/s"),
 			    x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
 	} else {
-		strbuf_addf(buf, "%u bytes", (unsigned)bytes);
+		strbuf_addf(buf,
+				humanise_rate == 0 ?
+					/* TRANSLATORS: IEC 80000-13:2008 byte */
+					Q_("%u byte", "%u bytes", (unsigned)bytes) :
+					/* TRANSLATORS: IEC 80000-13:2008 byte/second */
+					Q_("%u byte/s", "%u bytes/s", (unsigned)bytes),
+				(unsigned)bytes);
 	}
 }
 
+void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes)
+{
+	strbuf_humanise(buf, bytes, 0);
+}
+
+void strbuf_humanise_rate(struct strbuf *buf, off_t bytes)
+{
+	strbuf_humanise(buf, bytes, 1);
+}
+
 void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
 {
 	if (!*path)
diff --git a/strbuf.h b/strbuf.h
index c8d98df..f62278a 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -373,6 +373,12 @@
 void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
 
 /**
+ * Append the given byte rate as a human-readable string (i.e. 12.23 KiB/s,
+ * 3.50 MiB/s).
+ */
+void strbuf_humanise_rate(struct strbuf *buf, off_t bytes);
+
+/**
  * Add a formatted string to the buffer.
  */
 __attribute__((format (printf,2,3)))
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
index a20a616..c8a1cde 100644
--- a/t/helper/test-example-decorate.c
+++ b/t/helper/test-example-decorate.c
@@ -26,8 +26,8 @@
 	 * Add 2 objects, one with a non-NULL decoration and one with a NULL
 	 * decoration.
 	 */
-	one = lookup_unknown_object(one_oid.hash);
-	two = lookup_unknown_object(two_oid.hash);
+	one = lookup_unknown_object(&one_oid);
+	two = lookup_unknown_object(&two_oid);
 	ret = add_decoration(&n, one, &decoration_a);
 	if (ret)
 		BUG("when adding a brand-new object, NULL should be returned");
@@ -56,7 +56,7 @@
 	ret = lookup_decoration(&n, two);
 	if (ret != &decoration_b)
 		BUG("lookup should return added declaration");
-	three = lookup_unknown_object(three_oid.hash);
+	three = lookup_unknown_object(&three_oid);
 	ret = lookup_decoration(&n, three);
 	if (ret)
 		BUG("lookup for unknown object should return NULL");
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
index 06c3c91..cfd76bf 100644
--- a/t/lib-patch-mode.sh
+++ b/t/lib-patch-mode.sh
@@ -2,28 +2,40 @@
 
 . ./test-lib.sh
 
+# set_state <path> <worktree-content> <index-content>
+#
+# Prepare the content for path in worktree and the index as specified.
 set_state () {
 	echo "$3" > "$1" &&
 	git add "$1" &&
 	echo "$2" > "$1"
 }
 
+# save_state <path>
+#
+# Save index/worktree content of <path> in the files _worktree_<path>
+# and _index_<path>
 save_state () {
 	noslash="$(echo "$1" | tr / _)" &&
 	cat "$1" > _worktree_"$noslash" &&
 	git show :"$1" > _index_"$noslash"
 }
 
+# set_and_save_state <path> <worktree-content> <index-content>
 set_and_save_state () {
 	set_state "$@" &&
 	save_state "$1"
 }
 
+# verify_state <path> <expected-worktree-content> <expected-index-content>
 verify_state () {
 	test "$(cat "$1")" = "$2" &&
 	test "$(git show :"$1")" = "$3"
 }
 
+# verify_saved_state <path>
+#
+# Call verify_state with expected contents from the last save_state
 verify_saved_state () {
 	noslash="$(echo "$1" | tr / _)" &&
 	verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 0276d14..26f8206 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -311,8 +311,8 @@
 test_expect_success 'init with separate gitdir' '
 	rm -rf newdir &&
 	git init --separate-git-dir realgitdir newdir &&
-	echo "gitdir: $(pwd)/realgitdir" >expected &&
-	test_cmp expected newdir/.git &&
+	newdir_git="$(cat newdir/.git)" &&
+	test_cmp_fspath "$(pwd)/realgitdir" "${newdir_git#gitdir: }" &&
 	test_path_is_dir realgitdir/refs
 '
 
@@ -361,12 +361,9 @@
 '
 
 test_expect_success 're-init to update git link' '
-	(
-	cd newdir &&
-	git init --separate-git-dir ../surrealgitdir
-	) &&
-	echo "gitdir: $(pwd)/surrealgitdir" >expected &&
-	test_cmp expected newdir/.git &&
+	git -C newdir init --separate-git-dir ../surrealgitdir &&
+	newdir_git="$(cat newdir/.git)" &&
+	test_cmp_fspath "$(pwd)/surrealgitdir" "${newdir_git#gitdir: }" &&
 	test_path_is_dir surrealgitdir/refs &&
 	test_path_is_missing realgitdir/refs
 '
@@ -374,12 +371,9 @@
 test_expect_success 're-init to move gitdir' '
 	rm -rf newdir realgitdir surrealgitdir &&
 	git init newdir &&
-	(
-	cd newdir &&
-	git init --separate-git-dir ../realgitdir
-	) &&
-	echo "gitdir: $(pwd)/realgitdir" >expected &&
-	test_cmp expected newdir/.git &&
+	git -C newdir init --separate-git-dir ../realgitdir &&
+	newdir_git="$(cat newdir/.git)" &&
+	test_cmp_fspath "$(pwd)/realgitdir" "${newdir_git#gitdir: }" &&
 	test_path_is_dir realgitdir/refs
 '
 
@@ -473,8 +467,8 @@
 		GIT_REDIRECT_STDOUT=output.txt \
 		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
+	grep "^\\.git\$" output.txt &&
+	grep "Needed a single revision" output.txt
 '
 
 test_done
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index 5868a87..1f600e2 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -17,7 +17,7 @@
 	test_cmp expect actual
 '
 
-test_expect_success !AUTOIDENT 'requested identites are strict' '
+test_expect_success !FAIL_PREREQS,!AUTOIDENT 'requested identites are strict' '
 	(
 		sane_unset GIT_COMMITTER_NAME &&
 		sane_unset GIT_COMMITTER_EMAIL &&
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 090b7fc..40cc004 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -31,20 +31,6 @@
 	test_path_is_file c
 '
 
-test_expect_success 'checkout -b checkout.optimizeNewBranch interaction' '
-	cp .git/info/sparse-checkout .git/info/sparse-checkout.bak &&
-	test_when_finished "
-		mv -f .git/info/sparse-checkout.bak .git/info/sparse-checkout
-		git checkout master
-	" &&
-	echo "/b" >>.git/info/sparse-checkout &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git -c checkout.optimizeNewBranch=true checkout -b fast &&
-	test "$(git ls-files -t b)" = "S b" &&
-	git checkout -b slow &&
-	test "$(git ls-files -t b)" = "H b"
-'
-
 test_expect_success 'merge feature branch into sparse checkout of master' '
 	git merge feature &&
 	test_path_is_file a &&
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 579a86b..9571e36 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -309,6 +309,45 @@
 	)
 '
 
+test_expect_success 'conditional include, onbranch' '
+	echo "[includeIf \"onbranch:foo-branch\"]path=bar9" >>.git/config &&
+	echo "[test]nine=9" >.git/bar9 &&
+	git checkout -b master &&
+	test_must_fail git config test.nine &&
+	git checkout -b foo-branch &&
+	echo 9 >expect &&
+	git config test.nine >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'conditional include, onbranch, wildcard' '
+	echo "[includeIf \"onbranch:?oo-*/**\"]path=bar10" >>.git/config &&
+	echo "[test]ten=10" >.git/bar10 &&
+	git checkout -b not-foo-branch/a &&
+	test_must_fail git config test.ten &&
+
+	echo 10 >expect &&
+	git checkout -b foo-branch/a/b/c &&
+	git config test.ten >actual &&
+	test_cmp expect actual &&
+
+	git checkout -b moo-bar/a &&
+	git config test.ten >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'conditional include, onbranch, implicit /** for /' '
+	echo "[includeIf \"onbranch:foo-dir/\"]path=bar11" >>.git/config &&
+	echo "[test]eleven=11" >.git/bar11 &&
+	git checkout -b not-foo-dir/a &&
+	test_must_fail git config test.eleven &&
+
+	echo 11 >expect &&
+	git checkout -b foo-dir/a/b/c &&
+	git config test.eleven >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'include cycles are detected' '
 	cat >.gitconfig <<-\EOF &&
 	[test]value = gitconfig
diff --git a/t/t2014-switch.sh b/t/t2014-checkout-switch.sh
similarity index 100%
rename from t/t2014-switch.sh
rename to t/t2014-checkout-switch.sh
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 1fa6706..b748db9 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -195,16 +195,22 @@
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
+
+	Or undo this operation with:
+
+	  git switch -
+
+	Turn off this advice by setting config variable advice.detachedHead to false
 
 	HEAD is now at \$commit three
 	EOF
@@ -271,16 +277,22 @@
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
-	Note: checking out 'HEAD^'.
+	Note: switching to 'HEAD^'.
 
 	You are in 'detached HEAD' state. You can look around, make experimental
 	changes and commit them, and you can discard any commits you make in this
-	state without impacting any branches by performing another checkout.
+	state without impacting any branches by switching back to a branch.
 
 	If you want to create a new branch to retain commits you create, you may
-	do so (now or later) by using -b with the checkout command again. Example:
+	do so (now or later) by using -c with the switch command. Example:
 
-	  git checkout -b <new-branch-name>
+	  git switch -c <new-branch-name>
+
+	Or undo this operation with:
+
+	  git switch -
+
+	Turn off this advice by setting config variable advice.detachedHead to false
 
 	HEAD is now at \$commit... three
 	EOF
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
new file mode 100755
index 0000000..f9efa29
--- /dev/null
+++ b/t/t2060-switch.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='switch basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	git branch first-branch &&
+	test_commit second &&
+	test_commit third &&
+	git remote add origin nohost:/nopath &&
+	git update-ref refs/remotes/origin/foo first-branch
+'
+
+test_expect_success 'switch branch no arguments' '
+	test_must_fail git switch
+'
+
+test_expect_success 'switch branch' '
+	git switch first-branch &&
+	test_path_is_missing second.t
+'
+
+test_expect_success 'switch and detach' '
+	test_when_finished git switch master &&
+	test_must_fail git switch master^{commit} &&
+	git switch --detach master^{commit} &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and detach current branch' '
+	test_when_finished git switch master &&
+	git switch master &&
+	git switch --detach &&
+	test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'switch and create branch' '
+	test_when_finished git switch master &&
+	git switch -c temp master^ &&
+	test_cmp_rev master^ refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'force create branch from HEAD' '
+	test_when_finished git switch master &&
+	git switch --detach master &&
+	test_must_fail git switch -c temp &&
+	git switch -C temp &&
+	test_cmp_rev master refs/heads/temp &&
+	echo refs/heads/temp >expected-branch &&
+	git symbolic-ref HEAD >actual-branch &&
+	test_cmp expected-branch actual-branch
+'
+
+test_expect_success 'new orphan branch from empty' '
+	test_when_finished git switch master &&
+	test_must_fail git switch --orphan new-orphan HEAD &&
+	git switch --orphan new-orphan &&
+	test_commit orphan &&
+	git cat-file commit refs/heads/new-orphan >commit &&
+	! grep ^parent commit &&
+	git ls-files >tracked-files &&
+	echo orphan.t >expected &&
+	test_cmp expected tracked-files
+'
+
+test_expect_success 'switching ignores file of same branch name' '
+	test_when_finished git switch master &&
+	: >first-branch &&
+	git switch first-branch &&
+	echo refs/heads/first-branch >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'guess and create branch ' '
+	test_when_finished git switch master &&
+	test_must_fail git switch --no-guess foo &&
+	git switch foo &&
+	echo refs/heads/foo >expected &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'not switching when something is in progress' '
+	test_when_finished rm -f .git/MERGE_HEAD &&
+	# fake a merge-in-progress
+	cp .git/HEAD .git/MERGE_HEAD &&
+	test_must_fail git switch -d @^
+'
+
+test_done
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
new file mode 100755
index 0000000..2650df1
--- /dev/null
+++ b/t/t2070-restore.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='restore basic functionality'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit first &&
+	echo first-and-a-half >>first.t &&
+	git add first.t &&
+	test_commit second &&
+	echo one >one &&
+	echo two >two &&
+	echo untracked >untracked &&
+	echo ignored >ignored &&
+	echo /ignored >.gitignore &&
+	git add one two .gitignore &&
+	git update-ref refs/heads/one master
+'
+
+test_expect_success 'restore without pathspec is not ok' '
+	test_must_fail git restore &&
+	test_must_fail git restore --source=first
+'
+
+test_expect_success 'restore a file, ignoring branch of same name' '
+	cat one >expected &&
+	echo dirty >>one &&
+	git restore one &&
+	test_cmp expected one
+'
+
+test_expect_success 'restore a file on worktree from another ref' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first first.t &&
+	test_cmp expected first.t &&
+	git cat-file blob HEAD:./first.t >expected &&
+	git show :first.t >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'restore a file in the index from another ref' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first --staged first.t &&
+	git show :first.t >actual &&
+	test_cmp expected actual &&
+	git cat-file blob HEAD:./first.t >expected &&
+	test_cmp expected first.t
+'
+
+test_expect_success 'restore a file in both the index and worktree from another ref' '
+	test_when_finished git reset --hard &&
+	git cat-file blob first:./first.t >expected &&
+	git restore --source=first --staged --worktree first.t &&
+	git show :first.t >actual &&
+	test_cmp expected actual &&
+	test_cmp expected first.t
+'
+
+test_expect_success 'restore --staged uses HEAD as source' '
+	test_when_finished git reset --hard &&
+	git cat-file blob :./first.t >expected &&
+	echo index-dirty >>first.t &&
+	git add first.t &&
+	git restore --staged first.t &&
+	git cat-file blob :./first.t >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'restore --ignore-unmerged ignores unmerged entries' '
+	git init unmerged &&
+	(
+		cd unmerged &&
+		echo one >unmerged &&
+		echo one >common &&
+		git add unmerged common &&
+		git commit -m common &&
+		git switch -c first &&
+		echo first >unmerged &&
+		git commit -am first &&
+		git switch -c second master &&
+		echo second >unmerged &&
+		git commit -am second &&
+		test_must_fail git merge first &&
+
+		echo dirty >>common &&
+		test_must_fail git restore . &&
+
+		git restore --ignore-unmerged --quiet . >output 2>&1 &&
+		git diff common >diff-output &&
+		test_must_be_empty output &&
+		test_must_be_empty diff-output
+	)
+'
+
+test_done
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
new file mode 100755
index 0000000..98b2476
--- /dev/null
+++ b/t/t2071-restore-patch.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+test_description='git restore --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success PERL 'setup' '
+	mkdir dir &&
+	echo parent >dir/foo &&
+	echo dummy >bar &&
+	git add bar dir/foo &&
+	git commit -m initial &&
+	test_tick &&
+	test_commit second dir/foo head &&
+	set_and_save_state bar bar_work bar_index &&
+	save_head
+'
+
+test_expect_success PERL 'restore -p without pathspec is fine' '
+	echo q >cmd &&
+	git restore -p <cmd
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success PERL 'saying "n" does nothing' '
+	set_and_save_state dir/foo work head &&
+	test_write_lines n n | git restore -p &&
+	verify_saved_state bar &&
+	verify_saved_state dir/foo
+'
+
+test_expect_success PERL 'git restore -p' '
+	set_and_save_state dir/foo work head &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'git restore -p with staged changes' '
+	set_state dir/foo work index &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD' '
+	set_state dir/foo work index &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines n y n | git restore -p --source=HEAD &&
+	verify_saved_state bar &&
+	verify_state dir/foo head index
+'
+
+test_expect_success PERL 'git restore -p --source=HEAD^' '
+	set_state dir/foo work index &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines n y n | git restore -p --source=HEAD^ &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent index
+'
+
+test_expect_success PERL 'git restore -p handles deletion' '
+	set_state dir/foo work index &&
+	rm dir/foo &&
+	test_write_lines n y | git restore -p &&
+	verify_saved_state bar &&
+	verify_state dir/foo index index
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success PERL 'path limiting works: dir' '
+	set_state dir/foo work head &&
+	test_write_lines y n | git restore -p dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: -- dir' '
+	set_state dir/foo work head &&
+	test_write_lines y n | git restore -p -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
+	set_state dir/foo work head &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines y n n | git restore -p --source=HEAD^ -- dir &&
+	verify_saved_state bar &&
+	verify_state dir/foo parent head
+'
+
+test_expect_success PERL 'path limiting works: foo inside dir' '
+	set_state dir/foo work head &&
+	# the third n is to get out in case it mistakenly does not apply
+	test_write_lines y n n | (cd dir && git restore -p foo) &&
+	verify_saved_state bar &&
+	verify_state dir/foo head head
+'
+
+test_expect_success PERL 'none of this moved HEAD' '
+	verify_saved_head
+'
+
+test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index e9d7084..411a70b 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -206,18 +206,22 @@
 	git worktree add -f bazdir2 baz &&
 	git branch -M baz bam &&
 	test $(git -C bazdir rev-parse --abbrev-ref HEAD) = bam &&
-	test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam
+	test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
+	rm -r bazdir bazdir2 &&
+	git worktree prune
 '
 
 test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
 	git checkout -b baz &&
-	git worktree add -f bazdir3 baz &&
+	git worktree add -f bazdir baz &&
 	(
-		cd bazdir3 &&
+		cd bazdir &&
 		git branch -M baz bam &&
 		test $(git rev-parse --abbrev-ref HEAD) = bam
 	) &&
-	test $(git rev-parse --abbrev-ref HEAD) = bam
+	test $(git rev-parse --abbrev-ref HEAD) = bam &&
+	rm -r bazdir &&
+	git worktree prune
 '
 
 test_expect_success 'git branch -M master should work when master is checked out' '
@@ -804,7 +808,9 @@
 test_expect_success 'deleting currently checked out branch fails' '
 	git worktree add -b my7 my7 &&
 	test_must_fail git -C my7 branch -d my7 &&
-	test_must_fail git branch -d my7
+	test_must_fail git branch -d my7 &&
+	rm -r my7 &&
+	git worktree prune
 '
 
 test_expect_success 'test --track without .fetch entries' '
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index be55148..71818b9 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -136,10 +136,13 @@
 	branch-two
 	EOF
 	git checkout branch-one &&
-	git worktree add worktree branch-two &&
+	test_when_finished "
+		git worktree remove worktree_dir
+	" &&
+	git worktree add worktree_dir branch-two &&
 	{
 		git branch --show-current &&
-		git -C worktree branch --show-current
+		git -C worktree_dir branch --show-current
 	} >actual &&
 	test_cmp expect actual
 '
@@ -284,6 +287,24 @@
 	test_i18ncmp expect actual
 '
 
+test_expect_success 'worktree colors correct' '
+	cat >expect <<-EOF &&
+	* <GREEN>(HEAD detached from fromtag)<RESET>
+	  ambiguous<RESET>
+	  branch-one<RESET>
+	+ <CYAN>branch-two<RESET>
+	  master<RESET>
+	  ref-to-branch<RESET> -> branch-one
+	  ref-to-remote<RESET> -> origin/branch-one
+	EOF
+	git worktree add worktree_dir branch-two &&
+	git branch --color >actual.raw &&
+	rm -r worktree_dir &&
+	git worktree prune &&
+	test_decode_color <actual.raw >actual &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success "set up color tests" '
 	echo "<RED>master<RESET>" >expect.color &&
 	echo "master" >expect.bare &&
@@ -308,4 +329,23 @@
 	test_cmp expect.color actual
 '
 
+test_expect_success 'verbose output lists worktree path' '
+	one=$(git rev-parse --short HEAD) &&
+	two=$(git rev-parse --short master) &&
+	cat >expect <<-EOF &&
+	* (HEAD detached from fromtag) $one one
+	  ambiguous                    $one one
+	  branch-one                   $two two
+	+ branch-two                   $one ($(pwd)/worktree_dir) one
+	  master                       $two two
+	  ref-to-branch                $two two
+	  ref-to-remote                $two two
+	EOF
+	git worktree add worktree_dir branch-two &&
+	git branch -vv >actual &&
+	rm -r worktree_dir &&
+	git worktree prune &&
+	test_i18ncmp expect actual
+'
+
 test_done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 1723e1a..461dd53 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -75,11 +75,10 @@
 	test_line_count = 6 actual
 '
 
-cat > expect <<EOF
-error: nothing to do
-EOF
-
 test_expect_success 'rebase -i with empty HEAD' '
+	cat >expect <<-\EOF &&
+	error: nothing to do
+	EOF
 	set_fake_editor &&
 	test_must_fail env FAKE_LINES="1 exec_true" git rebase -i HEAD^ >actual 2>&1 &&
 	test_i18ncmp expect actual
@@ -237,25 +236,23 @@
 	test G = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
-cat > expect << EOF
-diff --git a/file1 b/file1
-index f70f10e..fd79235 100644
---- a/file1
-+++ b/file1
-@@ -1 +1 @@
--A
-+G
-EOF
-
-cat > expect2 << EOF
-<<<<<<< HEAD
-D
-=======
-G
->>>>>>> 5d18e54... G
-EOF
-
 test_expect_success 'stop on conflicting pick' '
+	cat >expect <<-\EOF &&
+	diff --git a/file1 b/file1
+	index f70f10e..fd79235 100644
+	--- a/file1
+	+++ b/file1
+	@@ -1 +1 @@
+	-A
+	+G
+	EOF
+	cat >expect2 <<-\EOF &&
+	<<<<<<< HEAD
+	D
+	=======
+	G
+	>>>>>>> 5d18e54... G
+	EOF
 	git tag new-branch1 &&
 	set_fake_editor &&
 	test_must_fail git rebase -i master &&
@@ -495,15 +492,14 @@
 	git branch -D conflict-squash
 '
 
-cat > expect-squash-fixup << EOF
-B
-
-D
-
-ONCE
-EOF
-
 test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messages' '
+	cat >expect-squash-fixup <<-\EOF &&
+	B
+
+	D
+
+	ONCE
+	EOF
 	git checkout -b squash-fixup E &&
 	base=$(git rev-parse HEAD~4) &&
 	set_fake_editor &&
@@ -799,13 +795,12 @@
 	test "a note" = "$(git notes show HEAD)"
 '
 
-cat >expect <<EOF
-an earlier note
-
-a note
-EOF
-
 test_expect_success 'rebase -i can copy notes over a fixup' '
+	cat >expect <<-\EOF &&
+	an earlier note
+
+	a note
+	EOF
 	git reset --hard n3 &&
 	git notes add -m"an earlier note" n2 &&
 	set_fake_editor &&
@@ -1031,7 +1026,7 @@
 	test -z "$(git show -s --format=%p HEAD^)"
 '
 
-test_expect_success 'rebase -i --root when root has untracked file confilct' '
+test_expect_success 'rebase -i --root when root has untracked file conflict' '
 	test_when_finished "reset_rebase" &&
 	git checkout -b failing-root-pick A &&
 	echo x >file2 &&
@@ -1304,52 +1299,37 @@
 		actual
 '
 
-cat >expect <<EOF
-Warning: some commits may have been dropped accidentally.
-Dropped commits (newer to older):
- - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
-To avoid this message, use "drop" to explicitly remove a commit.
-
-Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
-The possible behaviours are: ignore, warn, error.
-
-Rebasing (1/4)
-Rebasing (2/4)
-Rebasing (3/4)
-Rebasing (4/4)
-Successfully rebased and updated refs/heads/missing-commit.
-EOF
-
-cr_to_nl () {
-	tr '\015' '\012'
-}
-
 test_expect_success 'rebase -i respects rebase.missingCommitsCheck = warn' '
+	cat >expect <<-EOF &&
+	Warning: some commits may have been dropped accidentally.
+	Dropped commits (newer to older):
+	 - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+	To avoid this message, use "drop" to explicitly remove a commit.
+	EOF
 	test_config rebase.missingCommitsCheck warn &&
 	rebase_setup_and_clean missing-commit &&
 	set_fake_editor &&
 	FAKE_LINES="1 2 3 4" \
 		git rebase -i --root 2>actual.2 &&
-	cr_to_nl <actual.2 >actual &&
+	head -n4 actual.2 >actual &&
 	test_i18ncmp expect actual &&
 	test D = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
-cat >expect <<EOF
-Warning: some commits may have been dropped accidentally.
-Dropped commits (newer to older):
- - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
- - $(git rev-list --pretty=oneline --abbrev-commit -1 master~2)
-To avoid this message, use "drop" to explicitly remove a commit.
-
-Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
-The possible behaviours are: ignore, warn, error.
-
-You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
-Or you can abort the rebase with 'git rebase --abort'.
-EOF
-
 test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '
+	cat >expect <<-EOF &&
+	Warning: some commits may have been dropped accidentally.
+	Dropped commits (newer to older):
+	 - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+	 - $(git rev-list --pretty=oneline --abbrev-commit -1 master~2)
+	To avoid this message, use "drop" to explicitly remove a commit.
+
+	Use '\''git config rebase.missingCommitsCheck'\'' to change the level of warnings.
+	The possible behaviours are: ignore, warn, error.
+
+	You can fix this with '\''git rebase --edit-todo'\'' and then run '\''git rebase --continue'\''.
+	Or you can abort the rebase with '\''git rebase --abort'\''.
+	EOF
 	test_config rebase.missingCommitsCheck error &&
 	rebase_setup_and_clean missing-commit &&
 	set_fake_editor &&
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index bdaa511..4eff14d 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -265,4 +265,12 @@
 	test_i18ngrep "has been rescheduled" err
 '
 
+test_expect_success 'rebase.reschedulefailedexec only affects `rebase -i`' '
+	test_config rebase.reschedulefailedexec true &&
+	test_must_fail git rebase -x false HEAD^ &&
+	grep "^exec false" .git/rebase-merge/git-rebase-todo &&
+	git rebase --abort &&
+	git rebase HEAD^
+'
+
 test_done
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 2d1094e..9186e90 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -49,7 +49,7 @@
 	$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
 	HEAD is now at $(git rev-parse --short feature-branch) third commit
 	Rebasing (1/2)QRebasing (2/2)QApplied autostash.
-	Successfully rebased and updated refs/heads/rebased-feature-branch.
+	Q                                                                                QSuccessfully rebased and updated refs/heads/rebased-feature-branch.
 	EOF
 }
 
@@ -73,7 +73,7 @@
 	Rebasing (1/2)QRebasing (2/2)QApplying autostash resulted in conflicts.
 	Your changes are safe in the stash.
 	You can run "git stash pop" or "git stash drop" at any time.
-	Successfully rebased and updated refs/heads/rebased-feature-branch.
+	Q                                                                                QSuccessfully rebased and updated refs/heads/rebased-feature-branch.
 	EOF
 }
 
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 2315649..7b6c484 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -237,8 +237,24 @@
 	test_cmp_rev HEAD "$(cat wt/b)"
 '
 
+test_expect_success '--abort cleans up refs/rewritten' '
+	git checkout -b abort-cleans-refs-rewritten H &&
+	GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
+	git rev-parse --verify refs/rewritten/onto &&
+	git rebase --abort &&
+	test_must_fail git rev-parse --verify refs/rewritten/onto
+'
+
+test_expect_success '--quit cleans up refs/rewritten' '
+	git checkout -b quit-cleans-refs-rewritten H &&
+	GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
+	git rev-parse --verify refs/rewritten/onto &&
+	git rebase --quit &&
+	test_must_fail git rev-parse --verify refs/rewritten/onto
+'
+
 test_expect_success 'post-rewrite hook and fixups work for merges' '
-	git checkout -b post-rewrite &&
+	git checkout -b post-rewrite H &&
 	test_commit same1 &&
 	git reset --hard HEAD^ &&
 	test_commit same2 &&
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 65dfbc0..69991a3 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -639,4 +639,12 @@
 	test_cmp expected-2 actual
 '
 
+test_expect_success 'checkout -p works with pathological context lines' '
+	test_write_lines a a a a a a >a &&
+	git add a &&
+	test_write_lines a b a b a b a b a b a > a&&
+	test_write_lines s n n y q | git checkout -p &&
+	test_write_lines a b a b a a b a b a >expect &&
+	test_cmp expect a
+'
 test_done
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index ea30d5f..b22e671 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -708,6 +708,24 @@
 	git stash drop
 '
 
+test_expect_success 'valid ref of the form "n", n < N' '
+	git stash clear &&
+	echo bar5 >file &&
+	echo bar6 >file2 &&
+	git add file2 &&
+	git stash &&
+	git stash show 0 &&
+	git stash branch tmp 0 &&
+	git checkout master &&
+	git stash &&
+	git stash apply 0 &&
+	git reset --hard &&
+	git stash pop 0 &&
+	git stash &&
+	git stash drop 0 &&
+	test_must_fail git stash drop
+'
+
 test_expect_success 'branch: do not drop the stash if the branch exists' '
 	git stash clear &&
 	echo foo >file &&
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 840ad4d..5267c4b 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -23,6 +23,14 @@
 	test_path_is_file info/commit-graph
 '
 
+test_expect_success 'close with correct error on bad input' '
+	cd "$TRASH_DIRECTORY/full" &&
+	echo doesnotexist >in &&
+	{ git commit-graph write --stdin-packs <in 2>stderr; ret=$?; } &&
+	test "$ret" = 1 &&
+	test_i18ngrep "error adding pack" stderr
+'
+
 test_expect_success 'create commits and repack' '
 	cd "$TRASH_DIRECTORY/full" &&
 	for i in $(test_seq 3)
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index e98d90d..139f710 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -978,4 +978,27 @@
 	check_negotiation_tip
 '
 
+test_expect_success '--no-show-forced-updates' '
+	mkdir forced-updates &&
+	(
+		cd forced-updates &&
+		git init &&
+		test_commit 1 &&
+		test_commit 2
+	) &&
+	git clone forced-updates forced-update-clone &&
+	git clone forced-updates no-forced-update-clone &&
+	git -C forced-updates reset --hard HEAD~1 &&
+	(
+		cd forced-update-clone &&
+		git fetch --show-forced-updates origin 2>output &&
+		test_i18ngrep "(forced update)" output
+	) &&
+	(
+		cd no-forced-update-clone &&
+		git fetch --no-show-forced-updates origin 2>output &&
+		! test_i18ngrep "(forced update)" output
+	)
+'
+
 test_done
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index 0030c92..5426d4b 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -105,9 +105,12 @@
 	 git remote rm origin &&
 	 git remote add one ../one &&
 	 git remote add two ../two &&
-	 git fetch --multiple one two &&
+	 GIT_TRACE=1 git fetch --multiple one two 2>trace &&
 	 git branch -r > output &&
-	 test_cmp ../expect output)
+	 test_cmp ../expect output &&
+	 grep "built-in: git gc" trace >gc &&
+	 test_line_count = 1 gc
+	)
 '
 
 test_expect_success 'git fetch --multiple (bad remote names)' '
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index 8ef8763..2e4802e 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -213,7 +213,7 @@
 	cd "$ROOT_PATH"/test_repo_clone &&
 	test_commit noisy &&
 	test_terminal git push >output 2>&1 &&
-	test_i18ngrep "^Writing objects" output
+	test_i18ngrep "Writing objects" output
 '
 
 test_expect_success TTY 'push --quiet silences status and progress' '
@@ -228,7 +228,7 @@
 	test_commit no-progress &&
 	test_terminal git push --no-progress >output 2>&1 &&
 	test_i18ngrep "^To http" output &&
-	test_i18ngrep ! "^Writing objects" output
+	test_i18ngrep ! "Writing objects" output
 '
 
 test_expect_success 'push --progress shows progress to non-tty' '
@@ -236,7 +236,7 @@
 	test_commit progress &&
 	git push --progress >output 2>&1 &&
 	test_i18ngrep "^To http" output &&
-	test_i18ngrep "^Writing objects" output
+	test_i18ngrep "Writing objects" output
 '
 
 test_expect_success 'http push gives sane defaults to reflog' '
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index ac74626..e38e543 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -199,7 +199,7 @@
 
 test_expect_success 'invalid Content-Type rejected' '
 	test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual &&
-	grep "not valid:" actual
+	test_i18ngrep "not valid:" actual
 '
 
 test_expect_success 'create namespaced refs' '
@@ -301,11 +301,10 @@
 	)
 '
 
-test_expect_success 'large fetch-pack requests can be split across POSTs' '
+test_expect_success 'large fetch-pack requests can be sent using chunked encoding' '
 	GIT_TRACE_CURL=true git -c http.postbuffer=65536 \
 		clone --bare "$HTTPD_URL/smart/repo.git" split.git 2>err &&
-	grep "^=> Send header: POST" err >posts &&
-	test_line_count = 2 posts
+	grep "^=> Send header: Transfer-Encoding: chunked" err
 '
 
 test_expect_success 'test allowreachablesha1inwant' '
@@ -466,7 +465,7 @@
 
 test_expect_success 'server-side error detected' '
 	test_must_fail git clone $HTTPD_URL/error_smart/repo.git 2>actual &&
-	grep "server-side error" actual
+	test_i18ngrep "server-side error" actual
 '
 
 test_done
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index d04f800..2d6c4a2 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -126,7 +126,7 @@
 '
 
 test_expect_success 'cloning without refspec' '
-	GIT_REMOTE_TESTGIT_REFSPEC="" \
+	GIT_REMOTE_TESTGIT_NOREFSPEC=1 \
 	git clone "testgit::${PWD}/server" local2 2>error &&
 	test_i18ngrep "this remote helper should implement refspec capability" error &&
 	compare_refs local2 HEAD server HEAD
@@ -135,7 +135,7 @@
 test_expect_success 'pulling without refspecs' '
 	(cd local2 &&
 	git reset --hard &&
-	GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) &&
+	GIT_REMOTE_TESTGIT_NOREFSPEC=1 git pull 2>../error) &&
 	test_i18ngrep "this remote helper should implement refspec capability" error &&
 	compare_refs local2 HEAD server HEAD
 '
@@ -145,8 +145,8 @@
 	(cd local2 &&
 	echo content >>file &&
 	git commit -a -m ten &&
-	GIT_REMOTE_TESTGIT_REFSPEC="" &&
-	export GIT_REMOTE_TESTGIT_REFSPEC &&
+	GIT_REMOTE_TESTGIT_NOREFSPEC=1 &&
+	export GIT_REMOTE_TESTGIT_NOREFSPEC &&
 	test_must_fail git push 2>../error) &&
 	test_i18ngrep "remote-helper doesn.t support push; refspec needed" error
 '
@@ -303,4 +303,14 @@
 	compare_refs server HEAD local FETCH_HEAD
 '
 
+test_expect_success 'fetch tag' '
+	(cd server &&
+	 git tag v1.0
+	) &&
+	(cd local &&
+	 git fetch
+	) &&
+	compare_refs local v1.0 server v1.0
+'
+
 test_done
diff --git a/t/t5801/git-remote-testgit b/t/t5801/git-remote-testgit
index 752c763..6b9f0b5 100755
--- a/t/t5801/git-remote-testgit
+++ b/t/t5801/git-remote-testgit
@@ -11,13 +11,15 @@
 url=$2
 
 dir="$GIT_DIR/testgit/$alias"
-prefix="refs/testgit/$alias"
 
-default_refspec="refs/heads/*:${prefix}/heads/*"
+h_refspec="refs/heads/*:refs/testgit/$alias/heads/*"
+t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
 
-refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
-
-test -z "$refspec" && prefix="refs"
+if test -n "$GIT_REMOTE_TESTGIT_NOREFSPEC"
+then
+	h_refspec=""
+	t_refspec=""
+fi
 
 GIT_DIR="$url/.git"
 export GIT_DIR
@@ -40,7 +42,8 @@
 	capabilities)
 		echo 'import'
 		echo 'export'
-		test -n "$refspec" && echo "refspec $refspec"
+		test -n "$h_refspec" && echo "refspec $h_refspec"
+		test -n "$t_refspec" && echo "refspec $t_refspec"
 		if test -n "$gitmarks"
 		then
 			echo "*import-marks $gitmarks"
@@ -52,7 +55,7 @@
 		echo
 		;;
 	list)
-		git for-each-ref --format='? %(refname)' 'refs/heads/'
+		git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
 		head=$(git symbolic-ref HEAD)
 		echo "@$head HEAD"
 		echo
@@ -81,10 +84,11 @@
 
 		echo "feature done"
 		git fast-export \
+			${h_refspec:+"--refspec=$h_refspec"} \
+			${t_refspec:+"--refspec=$t_refspec"} \
 			${testgitmarks:+"--import-marks=$testgitmarks"} \
 			${testgitmarks:+"--export-marks=$testgitmarks"} \
-			$refs |
-		sed -e "s#refs/heads/#${prefix}/heads/#g"
+			$refs
 		echo "done"
 		;;
 	export)
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index 0507999..52a9e38 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -48,6 +48,26 @@
 	! grep one output
 '
 
+test_expect_success 'rev-list --objects --no-object-names has no space/names' '
+	git rev-list --objects --no-object-names HEAD >output &&
+	! grep wanted_file output &&
+	! grep unwanted_file output &&
+	! grep " " output
+'
+
+test_expect_success 'rev-list --objects --no-object-names works with cat-file' '
+	git rev-list --objects --no-object-names --all >list-output &&
+	git cat-file --batch-check <list-output >cat-output &&
+	! grep missing cat-output
+'
+
+test_expect_success '--no-object-names and --object-names are last-one-wins' '
+	git rev-list --objects --no-object-names --object-names --all >output &&
+	grep wanted_file output &&
+	git rev-list --objects --object-names --no-object-names --all >output &&
+	! grep wanted_file output
+'
+
 test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
 	git commit --allow-empty -m another &&
 	git tag -a -m "annotated" v1.0 &&
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 716283b..febf63f 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -160,6 +160,19 @@
 '
 
 cat >expect <<\EOF
+## b1...origin/master [different]
+EOF
+
+test_expect_success 'status.aheadbehind=false status -s -b (diverged from upstream)' '
+	(
+		cd test &&
+		git checkout b1 >/dev/null &&
+		git -c status.aheadbehind=false status -s -b | head -1
+	) >actual &&
+	test_i18ncmp expect actual
+'
+
+cat >expect <<\EOF
 On branch b1
 Your branch and 'origin/master' have diverged,
 and have 1 and 1 different commits each, respectively.
@@ -174,6 +187,15 @@
 	test_i18ncmp expect actual
 '
 
+test_expect_success 'status --long --branch' '
+	(
+		cd test &&
+		git checkout b1 >/dev/null &&
+		git -c status.aheadbehind=true status --long -b | head -3
+	) >actual &&
+	test_i18ncmp expect actual
+'
+
 cat >expect <<\EOF
 On branch b1
 Your branch and 'origin/master' refer to different commits.
@@ -188,6 +210,15 @@
 	test_i18ncmp expect actual
 '
 
+test_expect_success 'status.aheadbehind=false status --long --branch' '
+	(
+		cd test &&
+		git checkout b1 >/dev/null &&
+		git -c status.aheadbehind=false status --long -b | head -2
+	) >actual &&
+	test_i18ncmp expect actual
+'
+
 cat >expect <<\EOF
 ## b5...brokenbase [gone]
 EOF
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index fc067ed..35408d5 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -441,4 +441,17 @@
 	test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
 '
 
+test_expect_success 'validate worktree atom' '
+	cat >expect <<-EOF &&
+	master: $(pwd)
+	master_worktree: $(pwd)/worktree_dir
+	side: not checked out
+	EOF
+	git worktree add -b master_worktree worktree_dir master &&
+	git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual &&
+	rm -r worktree_dir &&
+	git worktree prune &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 6aeeb27..80eb13d 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -932,6 +932,27 @@
 	test_cmp expect actual
 '
 
+get_tag_header gpgsign-enabled $commit commit $time >expect
+echo "A message" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+	'git tag configured tag.gpgsign enables GPG sign' \
+	'test_config tag.gpgsign true &&
+	git tag -m "A message" gpgsign-enabled &&
+	get_tag_msg gpgsign-enabled>actual &&
+	test_cmp expect actual
+'
+
+get_tag_header no-sign $commit commit $time >expect
+echo "A message" >>expect
+test_expect_success GPG \
+	'git tag --no-sign configured tag.gpgsign skip GPG sign' \
+	'test_config tag.gpgsign true &&
+	git tag -a --no-sign -m "A message" no-sign &&
+	get_tag_msg no-sign>actual &&
+	test_cmp expect actual
+'
+
 test_expect_success GPG \
 	'trying to create a signed tag with non-existing -F file should fail' '
 	! test -f nonexistingfile &&
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index 11eccc2..537787e 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -445,6 +445,14 @@
 		EOF
 
 		git status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&
+		test_cmp expect actual &&
+
+		# Confirm that "status.aheadbehind" DOES NOT work on V2 format.
+		git -c status.aheadbehind=false status --porcelain=v2 --branch --untracked-files=all >actual &&
+		test_cmp expect actual &&
+
+		# Confirm that "status.aheadbehind" DOES NOT work on V2 format.
+		git -c status.aheadbehind=true status --porcelain=v2 --branch --untracked-files=all >actual &&
 		test_cmp expect actual
 	)
 '
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index 706ae76..6b2aa91 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -421,4 +421,11 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'option-like arguments passed to foreach recurse correctly' '
+	git -C clone2 submodule foreach --recursive "echo be --an-option" >expect &&
+	git -C clone2 submodule foreach --recursive echo be --an-option >actual &&
+	grep -e "--an-option" expect &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 5733d9c..14c92e4 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -402,7 +402,7 @@
 exit 0
 EOF
 
-test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' '
+test_expect_success !FAIL_PREREQS,!AUTOIDENT 'do not fire editor when committer is bogus' '
 	>.git/result &&
 
 	echo >>negative &&
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index e1f1129..681bc31 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -94,13 +94,13 @@
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
+#   (use "git restore --staged <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
 # Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
+#   (use "git restore <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -128,13 +128,13 @@
 #   (use "git pull" to merge the remote branch into yours)
 #
 # Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
+#   (use "git restore --staged <file>..." to unstage)
 #
 #	new file:   dir2/added
 #
 # Changes not staged for commit:
 #   (use "git add <file>..." to update what will be committed)
-#   (use "git checkout -- <file>..." to discard changes in working directory)
+#   (use "git restore <file>..." to discard changes in working directory)
 #
 #	modified:   dir1/modified
 #
@@ -278,13 +278,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -347,13 +347,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -420,13 +420,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -484,13 +484,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -542,13 +542,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -605,13 +605,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   ../dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   modified
 
@@ -676,13 +676,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	<GREEN>new file:   dir2/added<RESET>
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	<RED>modified:   dir1/modified<RESET>
 
@@ -802,13 +802,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -852,7 +852,7 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   dir1/modified
 
@@ -896,14 +896,14 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -956,14 +956,14 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1019,7 +1019,7 @@
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1068,14 +1068,14 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD^1 <file>..." to unstage)
+  (use "git restore --source=HEAD^1 --staged <file>..." to unstage)
 
 	new file:   dir2/added
 	new file:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1123,13 +1123,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1235,13 +1235,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
   (commit or discard the untracked or modified content in submodules)
 
 	modified:   dir1/modified
@@ -1295,13 +1295,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 	modified:   sm (new commits)
@@ -1379,13 +1379,13 @@
 ;   (use "git pull" to merge the remote branch into yours)
 ;
 ; Changes to be committed:
-;   (use "git reset HEAD <file>..." to unstage)
+;   (use "git restore --staged <file>..." to unstage)
 ;
 ;	modified:   sm
 ;
 ; Changes not staged for commit:
 ;   (use "git add <file>..." to update what will be committed)
-;   (use "git checkout -- <file>..." to discard changes in working directory)
+;   (use "git restore <file>..." to discard changes in working directory)
 ;
 ;	modified:   dir1/modified
 ;	modified:   sm (new commits)
@@ -1431,7 +1431,7 @@
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1458,13 +1458,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
@@ -1581,13 +1581,13 @@
   (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   sm
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   dir1/modified
 
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index c1eb725..b9f5d73 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -85,7 +85,7 @@
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -110,7 +110,7 @@
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -148,7 +148,7 @@
   (use "git rebase --abort" to check out the original branch)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   main.txt
@@ -176,7 +176,7 @@
   (all conflicts fixed: run "git rebase --continue")
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   main.txt
 
@@ -246,7 +246,7 @@
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -354,7 +354,7 @@
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -453,7 +453,7 @@
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -557,7 +557,7 @@
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
-  (use "git checkout -- <file>..." to discard changes in working directory)
+  (use "git restore <file>..." to discard changes in working directory)
 
 	modified:   main.txt
 
@@ -834,7 +834,7 @@
   (use "git revert --abort" to cancel the revert operation)
 
 Unmerged paths:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
   (use "git add <file>..." to mark resolution)
 
 	both modified:   to-revert.txt
@@ -855,7 +855,7 @@
   (use "git revert --abort" to cancel the revert operation)
 
 Changes to be committed:
-  (use "git reset HEAD <file>..." to unstage)
+  (use "git restore --staged <file>..." to unstage)
 
 	modified:   to-revert.txt
 
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index c441861..f19202b 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -538,33 +538,50 @@
 	test_cmp expected actual
 '
 
-test_expect_success 'with message that has comments' '
-	cat basic_message >message_with_comments &&
-	sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
-		# comment
+# Cover multiple comment characters with the same test input.
+for char in "#" ";"
+do
+	case "$char" in
+	"#")
+		# This is the default, so let's explicitly _not_
+		# set any config to make sure it behaves as we expect.
+		;;
+	*)
+		config="-c core.commentChar=$char"
+		;;
+	esac
 
-		# other comment
-		Cc: Z
-		# yet another comment
-		Reviewed-by: Johan
-		Reviewed-by: Z
-		# last comment
+	test_expect_success "with message that has comments ($char)" '
+		cat basic_message >message_with_comments &&
+		sed -e "s/ Z\$/ /" \
+		    -e "s/#/$char/g" >>message_with_comments <<-EOF &&
+			# comment
 
-	EOF
-	cat basic_patch >>message_with_comments &&
-	cat basic_message >expected &&
-	cat >>expected <<-\EOF &&
-		# comment
+			# other comment
+			Cc: Z
+			# yet another comment
+			Reviewed-by: Johan
+			Reviewed-by: Z
+			# last comment
 
-		Reviewed-by: Johan
-		Cc: Peff
-		# last comment
+		EOF
+		cat basic_patch >>message_with_comments &&
+		cat basic_message >expected &&
+		sed -e "s/#/$char/g" >>expected <<-\EOF &&
+			# comment
 
-	EOF
-	cat basic_patch >>expected &&
-	git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
-	test_cmp expected actual
-'
+			Reviewed-by: Johan
+			Cc: Peff
+			# last comment
+
+		EOF
+		cat basic_patch >>expected &&
+		git $config interpret-trailers \
+			--trim-empty --trailer "Cc: Peff" \
+			message_with_comments >actual &&
+		test_cmp expected actual
+	'
+done
 
 test_expect_success 'with message that has an old style conflict block' '
 	cat basic_message >message_with_comments &&
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 5b61c10..ad288dd 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -131,17 +131,21 @@
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
 	test_must_fail git merge master &&
-	( yes "" | git mergetool both ) &&
-	( yes "" | git mergetool file1 file1 ) &&
-	( yes "" | git mergetool file2 "spaced name" ) &&
-	( yes "" | git mergetool subdir/file3 ) &&
-	( yes "d" | git mergetool file11 ) &&
-	( yes "d" | git mergetool file12 ) &&
-	( yes "l" | git mergetool submod ) &&
-	test "$(cat file1)" = "master updated" &&
-	test "$(cat file2)" = "master new" &&
-	test "$(cat subdir/file3)" = "master new sub" &&
-	test "$(cat submod/bar)" = "branch1 submodule" &&
+	yes "" | git mergetool both &&
+	yes "" | git mergetool file1 file1 &&
+	yes "" | git mergetool file2 "spaced name" &&
+	yes "" | git mergetool subdir/file3 &&
+	yes "d" | git mergetool file11 &&
+	yes "d" | git mergetool file12 &&
+	yes "l" | git mergetool submod &&
+	echo "master updated" >expect &&
+	test_cmp expect file1 &&
+	echo "master new" >expect &&
+	test_cmp expect file2 &&
+	echo "master new sub" >expect &&
+	test_cmp expect subdir/file3 &&
+	echo "branch1 submodule" >expect &&
+	test_cmp expect submod/bar &&
 	git commit -m "branch1 resolved with mergetool"
 '
 
@@ -153,17 +157,21 @@
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
 	test_must_fail git merge master &&
-	( yes "" | git mergetool --gui both ) &&
-	( yes "" | git mergetool -g file1 file1 ) &&
-	( yes "" | git mergetool --gui file2 "spaced name" ) &&
-	( yes "" | git mergetool --gui subdir/file3 ) &&
-	( yes "d" | git mergetool --gui file11 ) &&
-	( yes "d" | git mergetool --gui file12 ) &&
-	( yes "l" | git mergetool --gui submod ) &&
-	test "$(cat file1)" = "gui master updated" &&
-	test "$(cat file2)" = "gui master new" &&
-	test "$(cat subdir/file3)" = "gui master new sub" &&
-	test "$(cat submod/bar)" = "branch1 submodule" &&
+	yes "" | git mergetool --gui both &&
+	yes "" | git mergetool -g file1 file1 &&
+	yes "" | git mergetool --gui file2 "spaced name" &&
+	yes "" | git mergetool --gui subdir/file3 &&
+	yes "d" | git mergetool --gui file11 &&
+	yes "d" | git mergetool --gui file12 &&
+	yes "l" | git mergetool --gui submod &&
+	echo "gui master updated" >expect &&
+	test_cmp expect file1 &&
+	echo "gui master new" >expect &&
+	test_cmp expect file2 &&
+	echo "gui master new sub" >expect &&
+	test_cmp expect subdir/file3 &&
+	echo "branch1 submodule" >expect &&
+	test_cmp expect submod/bar &&
 	git commit -m "branch1 resolved with mergetool"
 '
 
@@ -172,17 +180,21 @@
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
 	test_must_fail git merge master &&
-	( yes "" | git mergetool --gui both ) &&
-	( yes "" | git mergetool -g file1 file1 ) &&
-	( yes "" | git mergetool --gui file2 "spaced name" ) &&
-	( yes "" | git mergetool --gui subdir/file3 ) &&
-	( yes "d" | git mergetool --gui file11 ) &&
-	( yes "d" | git mergetool --gui file12 ) &&
-	( yes "l" | git mergetool --gui submod ) &&
-	test "$(cat file1)" = "master updated" &&
-	test "$(cat file2)" = "master new" &&
-	test "$(cat subdir/file3)" = "master new sub" &&
-	test "$(cat submod/bar)" = "branch1 submodule" &&
+	yes "" | git mergetool --gui both &&
+	yes "" | git mergetool -g file1 file1 &&
+	yes "" | git mergetool --gui file2 "spaced name" &&
+	yes "" | git mergetool --gui subdir/file3 &&
+	yes "d" | git mergetool --gui file11 &&
+	yes "d" | git mergetool --gui file12 &&
+	yes "l" | git mergetool --gui submod &&
+	echo "master updated" >expect &&
+	test_cmp expect file1 &&
+	echo "master new" >expect &&
+	test_cmp expect file2 &&
+	echo "master new sub" >expect &&
+	test_cmp expect subdir/file3 &&
+	echo "branch1 submodule" >expect &&
+	test_cmp expect submod/bar &&
 	git commit -m "branch1 resolved with mergetool"
 '
 
@@ -195,19 +207,20 @@
 	test_config core.autocrlf true &&
 	git checkout -b test$test_count branch1 &&
 	test_must_fail git merge master &&
-	( yes "" | git mergetool file1 ) &&
-	( yes "" | git mergetool file2 ) &&
-	( yes "" | git mergetool "spaced name" ) &&
-	( yes "" | git mergetool both ) &&
-	( yes "" | git mergetool subdir/file3 ) &&
-	( yes "d" | git mergetool file11 ) &&
-	( yes "d" | git mergetool file12 ) &&
-	( yes "r" | git mergetool submod ) &&
+	yes "" | git mergetool file1 &&
+	yes "" | git mergetool file2 &&
+	yes "" | git mergetool "spaced name" &&
+	yes "" | git mergetool both &&
+	yes "" | git mergetool subdir/file3 &&
+	yes "d" | git mergetool file11 &&
+	yes "d" | git mergetool file12 &&
+	yes "r" | git mergetool submod &&
 	test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
 	test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
 	test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
 	git submodule update -N &&
-	test "$(cat submod/bar)" = "master submodule" &&
+	echo "master submodule" >expect &&
+	test_cmp expect submod/bar &&
 	git commit -m "branch1 resolved with mergetool - autocrlf"
 '
 
@@ -218,8 +231,9 @@
 	(
 		cd subdir &&
 		test_must_fail git merge master &&
-		( yes "" | git mergetool file3 ) &&
-		test "$(cat file3)" = "master new sub"
+		yes "" | git mergetool file3 &&
+		echo "master new sub" >expect &&
+		test_cmp expect file3
 	)
 '
 
@@ -230,16 +244,19 @@
 	(
 		cd subdir &&
 		test_must_fail git merge master &&
-		( yes "" | git mergetool file3 ) &&
-		( yes "" | git mergetool ../file1 ) &&
-		( yes "" | git mergetool ../file2 ../spaced\ name ) &&
-		( yes "" | git mergetool ../both ) &&
-		( yes "d" | git mergetool ../file11 ) &&
-		( yes "d" | git mergetool ../file12 ) &&
-		( yes "l" | git mergetool ../submod ) &&
-		test "$(cat ../file1)" = "master updated" &&
-		test "$(cat ../file2)" = "master new" &&
-		test "$(cat ../submod/bar)" = "branch1 submodule" &&
+		yes "" | git mergetool file3 &&
+		yes "" | git mergetool ../file1 &&
+		yes "" | git mergetool ../file2 ../spaced\ name &&
+		yes "" | git mergetool ../both &&
+		yes "d" | git mergetool ../file11 &&
+		yes "d" | git mergetool ../file12 &&
+		yes "l" | git mergetool ../submod &&
+		echo "master updated" >expect &&
+		test_cmp expect ../file1 &&
+		echo "master new" >expect &&
+		test_cmp expect ../file2 &&
+		echo "branch1 submodule" >expect &&
+		test_cmp expect ../submod/bar &&
 		git commit -m "branch1 resolved with mergetool - subdir"
 	)
 '
@@ -250,9 +267,9 @@
 	git submodule update -N &&
 	test_must_fail git merge master &&
 	test -n "$(git ls-files -u)" &&
-	( yes "d" | git mergetool file11 ) &&
-	( yes "d" | git mergetool file12 ) &&
-	( yes "l" | git mergetool submod ) &&
+	yes "d" | git mergetool file11 &&
+	yes "d" | git mergetool file12 &&
+	yes "l" | git mergetool submod &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging"
 '
@@ -264,13 +281,17 @@
 	(
 		cd subdir &&
 		test_must_fail git merge master &&
-		( yes "r" | git mergetool ../submod ) &&
-		( yes "d" "d" | git mergetool --no-prompt ) &&
-		test "$(cat ../file1)" = "master updated" &&
-		test "$(cat ../file2)" = "master new" &&
-		test "$(cat file3)" = "master new sub" &&
+		yes "r" | git mergetool ../submod &&
+		yes "d" "d" | git mergetool --no-prompt &&
+		echo "master updated" >expect &&
+		test_cmp expect ../file1 &&
+		echo "master new" >expect &&
+		test_cmp expect ../file2 &&
+		echo "master new sub" >expect &&
+		test_cmp expect file3 &&
 		( cd .. && git submodule update -N ) &&
-		test "$(cat ../submod/bar)" = "master submodule" &&
+		echo "master submodule" >expect &&
+		test_cmp expect ../submod/bar &&
 		git commit -m "branch2 resolved by mergetool from subdir"
 	)
 '
@@ -283,13 +304,17 @@
 	(
 		cd subdir &&
 		test_must_fail git merge master &&
-		( yes "r" | git mergetool ../submod ) &&
-		( yes "d" "d" | git mergetool --no-prompt ) &&
-		test "$(cat ../file1)" = "master updated" &&
-		test "$(cat ../file2)" = "master new" &&
-		test "$(cat file3)" = "master new sub" &&
+		yes "r" | git mergetool ../submod &&
+		yes "d" "d" | git mergetool --no-prompt &&
+		echo "master updated" >expect &&
+		test_cmp expect ../file1 &&
+		echo "master new" >expect &&
+		test_cmp expect ../file2 &&
+		echo "master new sub" >expect &&
+		test_cmp expect file3 &&
 		( cd .. && git submodule update -N ) &&
-		test "$(cat ../submod/bar)" = "master submodule" &&
+		echo "master submodule" >expect &&
+		test_cmp expect ../submod/bar &&
 		git commit -m "branch2 resolved by mergetool from subdir"
 	)
 '
@@ -301,8 +326,8 @@
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
 	test_must_fail git merge master &&
-	( yes "l" | git mergetool --no-prompt submod ) &&
-	( yes "d" "d" | git mergetool --no-prompt ) &&
+	yes "l" | git mergetool --no-prompt submod &&
+	yes "d" "d" | git mergetool --no-prompt &&
 	git submodule update -N &&
 	output="$(yes "n" | git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging"
@@ -343,9 +368,10 @@
 	git submodule update -N &&
 	test_must_fail git merge master &&
 
-	( yes "" | git mergetool subdir ) &&
+	yes "" | git mergetool subdir &&
 
-	test "$(cat subdir/file3)" = "master new sub"
+	echo "master new sub" >expect &&
+	test_cmp expect subdir/file3
 '
 
 test_expect_success 'mergetool delete/delete conflict' '
@@ -410,14 +436,16 @@
 	git checkout -b test$test_count.a test$test_count &&
 	test_must_fail git merge master &&
 	test -n "$(git ls-files -u)" &&
-	( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-	( yes "" | git mergetool both ) &&
-	( yes "d" | git mergetool file11 file12 ) &&
-	( yes "r" | git mergetool submod ) &&
+	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+	yes "" | git mergetool both &&
+	yes "d" | git mergetool file11 file12 &&
+	yes "r" | git mergetool submod &&
 	rmdir submod && mv submod-movedaside submod &&
-	test "$(cat submod/bar)" = "branch1 submodule" &&
+	echo "branch1 submodule" >expect &&
+	test_cmp expect submod/bar &&
 	git submodule update -N &&
-	test "$(cat submod/bar)" = "master submodule" &&
+	echo "master submodule" >expect &&
+	test_cmp expect submod/bar &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
 	git commit -m "Merge resolved by keeping module" &&
@@ -427,10 +455,10 @@
 	git submodule update -N &&
 	test_must_fail git merge master &&
 	test -n "$(git ls-files -u)" &&
-	( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-	( yes "" | git mergetool both ) &&
-	( yes "d" | git mergetool file11 file12 ) &&
-	( yes "l" | git mergetool submod ) &&
+	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+	yes "" | git mergetool both &&
+	yes "d" | git mergetool file11 file12 &&
+	yes "l" | git mergetool submod &&
 	test ! -e submod &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
@@ -441,10 +469,10 @@
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
 	test -n "$(git ls-files -u)" &&
-	( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-	( yes "" | git mergetool both ) &&
-	( yes "d" | git mergetool file11 file12 ) &&
-	( yes "r" | git mergetool submod ) &&
+	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+	yes "" | git mergetool both &&
+	yes "d" | git mergetool file11 file12 &&
+	yes "r" | git mergetool submod &&
 	test ! -e submod &&
 	test -d submod.orig &&
 	git submodule update -N &&
@@ -457,13 +485,15 @@
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
 	test -n "$(git ls-files -u)" &&
-	( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-	( yes "" | git mergetool both ) &&
-	( yes "d" | git mergetool file11 file12 ) &&
-	( yes "l" | git mergetool submod ) &&
-	test "$(cat submod/bar)" = "master submodule" &&
+	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+	yes "" | git mergetool both &&
+	yes "d" | git mergetool file11 file12 &&
+	yes "l" | git mergetool submod &&
+	echo "master submodule" >expect &&
+	test_cmp expect submod/bar &&
 	git submodule update -N &&
-	test "$(cat submod/bar)" = "master submodule" &&
+	echo "master submodule" >expect &&
+	test_cmp expect submod/bar &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
 	git commit -m "Merge resolved by keeping module"
@@ -481,14 +511,16 @@
 	git checkout -b test$test_count.a branch1 &&
 	test_must_fail git merge master &&
 	test -n "$(git ls-files -u)" &&
-	( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-	( yes "" | git mergetool both ) &&
-	( yes "d" | git mergetool file11 file12 ) &&
-	( yes "r" | git mergetool submod ) &&
+	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+	yes "" | git mergetool both &&
+	yes "d" | git mergetool file11 file12 &&
+	yes "r" | git mergetool submod &&
 	rmdir submod && mv submod-movedaside submod &&
-	test "$(cat submod/bar)" = "branch1 submodule" &&
+	echo "branch1 submodule" >expect &&
+	test_cmp expect submod/bar &&
 	git submodule update -N &&
-	test "$(cat submod/bar)" = "master submodule" &&
+	echo "master submodule" >expect &&
+	test_cmp expect submod/bar &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
 	git commit -m "Merge resolved by keeping module" &&
@@ -497,12 +529,13 @@
 	git checkout -b test$test_count.b test$test_count &&
 	test_must_fail git merge master &&
 	test -n "$(git ls-files -u)" &&
-	( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-	( yes "" | git mergetool both ) &&
-	( yes "d" | git mergetool file11 file12 ) &&
-	( yes "l" | git mergetool submod ) &&
+	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+	yes "" | git mergetool both &&
+	yes "d" | git mergetool file11 file12 &&
+	yes "l" | git mergetool submod &&
 	git submodule update -N &&
-	test "$(cat submod)" = "not a submodule" &&
+	echo "not a submodule" >expect &&
+	test_cmp expect submod &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
 	git commit -m "Merge resolved by keeping file" &&
@@ -513,13 +546,14 @@
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
 	test -n "$(git ls-files -u)" &&
-	( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-	( yes "" | git mergetool both ) &&
-	( yes "d" | git mergetool file11 file12 ) &&
-	( yes "r" | git mergetool submod ) &&
+	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+	yes "" | git mergetool both &&
+	yes "d" | git mergetool file11 file12 &&
+	yes "r" | git mergetool submod &&
 	test -d submod.orig &&
 	git submodule update -N &&
-	test "$(cat submod)" = "not a submodule" &&
+	echo "not a submodule" >expect &&
+	test_cmp expect submod &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
 	git commit -m "Merge resolved by keeping file" &&
@@ -529,13 +563,15 @@
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
 	test -n "$(git ls-files -u)" &&
-	( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
-	( yes "" | git mergetool both ) &&
-	( yes "d" | git mergetool file11 file12 ) &&
-	( yes "l" | git mergetool submod ) &&
-	test "$(cat submod/bar)" = "master submodule" &&
+	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+	yes "" | git mergetool both &&
+	yes "d" | git mergetool file11 file12 &&
+	yes "l" | git mergetool submod &&
+	echo "master submodule" >expect &&
+	test_cmp expect submod/bar &&
 	git submodule update -N &&
-	test "$(cat submod/bar)" = "master submodule" &&
+	echo "master submodule" >expect &&
+	test_cmp expect submod/bar &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
 	git commit -m "Merge resolved by keeping module"
@@ -587,19 +623,23 @@
 	test_must_fail git merge test$test_count.a &&
 	(
 		cd subdir &&
-		( yes "l" | git mergetool subdir_module )
+		yes "l" | git mergetool subdir_module
 	) &&
-	test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
+	echo "test$test_count.b" >expect &&
+	test_cmp expect subdir/subdir_module/file15 &&
 	git submodule update -N &&
-	test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
+	echo "test$test_count.b" >expect &&
+	test_cmp expect subdir/subdir_module/file15 &&
 	git reset --hard &&
 	git submodule update -N &&
 
 	test_must_fail git merge test$test_count.a &&
-	( yes "r" | git mergetool subdir/subdir_module ) &&
-	test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
+	yes "r" | git mergetool subdir/subdir_module &&
+	echo "test$test_count.b" >expect &&
+	test_cmp expect subdir/subdir_module/file15 &&
 	git submodule update -N &&
-	test "$(cat subdir/subdir_module/file15)" = "test$test_count.a" &&
+	echo "test$test_count.a" >expect &&
+	test_cmp expect subdir/subdir_module/file15 &&
 	git commit -m "branch1 resolved with mergetool"
 '
 
@@ -615,22 +655,25 @@
 
 	test_must_fail git merge master &&
 	test -n "$(git ls-files -u)" &&
-	( yes "l" | git mergetool submod ) &&
-	test "$(cat submod/file16)" = "not a submodule" &&
+	yes "l" | git mergetool submod &&
+	echo "not a submodule" >expect &&
+	test_cmp expect submod/file16 &&
 	rm -rf submod.orig &&
 
 	git reset --hard &&
 	test_must_fail git merge master &&
 	test -n "$(git ls-files -u)" &&
 	test ! -e submod.orig &&
-	( yes "r" | git mergetool submod ) &&
+	yes "r" | git mergetool submod &&
 	test -d submod.orig &&
-	test "$(cat submod.orig/file16)" = "not a submodule" &&
+	echo "not a submodule" >expect &&
+	test_cmp expect submod.orig/file16 &&
 	rm -r submod.orig &&
 	mv submod-movedaside/.git submod &&
 	( cd submod && git clean -f && git reset --hard ) &&
 	git submodule update -N &&
-	test "$(cat submod/bar)" = "master submodule" &&
+	echo "master submodule" >expect &&
+	test_cmp expect submod/bar &&
 	git reset --hard &&
 	rm -rf submod-movedaside &&
 
@@ -638,17 +681,19 @@
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
 	test -n "$(git ls-files -u)" &&
-	( yes "l" | git mergetool submod ) &&
+	yes "l" | git mergetool submod &&
 	git submodule update -N &&
-	test "$(cat submod/bar)" = "master submodule" &&
+	echo "master submodule" >expect &&
+	test_cmp expect submod/bar &&
 
 	git reset --hard &&
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
 	test -n "$(git ls-files -u)" &&
 	test ! -e submod.orig &&
-	( yes "r" | git mergetool submod ) &&
-	test "$(cat submod/file16)" = "not a submodule" &&
+	yes "r" | git mergetool submod &&
+	echo "not a submodule" >expect &&
+	test_cmp expect submod/file16 &&
 
 	git reset --hard master &&
 	( cd submod && git clean -f && git reset --hard ) &&
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index 38d6b90..67ff271 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -411,6 +411,46 @@
 	)
 '
 
+# Check that excluded files are omitted during import
+test_expect_success 'git p4 clone complex branches with excluded files' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList branch1:branch2 &&
+		git config --add git-p4.branchList branch1:branch3 &&
+		git config --add git-p4.branchList branch1:branch4 &&
+		git config --add git-p4.branchList branch1:branch5 &&
+		git config --add git-p4.branchList branch1:branch6 &&
+		git p4 clone --dest=. --detect-branches -//depot/branch1/file2 -//depot/branch2/file2 -//depot/branch3/file2 -//depot/branch4/file2 -//depot/branch5/file2 -//depot/branch6/file2 //depot@all &&
+		git log --all --graph --decorate --stat &&
+		git reset --hard p4/depot/branch1 &&
+		test_path_is_file file1 &&
+		test_path_is_missing file2 &&
+		test_path_is_file file3 &&
+		git reset --hard p4/depot/branch2 &&
+		test_path_is_file file1 &&
+		test_path_is_missing file2 &&
+		test_path_is_missing file3 &&
+		git reset --hard p4/depot/branch3 &&
+		test_path_is_file file1 &&
+		test_path_is_missing file2 &&
+		test_path_is_missing file3 &&
+		git reset --hard p4/depot/branch4 &&
+		test_path_is_file file1 &&
+		test_path_is_missing file2 &&
+		test_path_is_file file3 &&
+		git reset --hard p4/depot/branch5 &&
+		test_path_is_file file1 &&
+		test_path_is_missing file2 &&
+		test_path_is_file file3 &&
+		git reset --hard p4/depot/branch6 &&
+		test_path_is_file file1 &&
+		test_path_is_missing file2 &&
+		test_path_is_missing file3
+	)
+'
+
 # From a report in http://stackoverflow.com/questions/11893688
 # where --use-client-spec caused branch prefixes not to be removed;
 # every file in git appeared into a subdirectory of the branch name.
@@ -610,4 +650,96 @@
 	)
 '
 
+test_expect_success 'restart p4d (case folding enabled)' '
+	stop_and_cleanup_p4d &&
+	start_p4d -C1
+'
+
+#
+# 1: //depot/main/mf1
+# 2: integrate //depot/main/... -> //depot/branch1/...
+# 3: //depot/main/mf2
+# 4: //depot/BRANCH1/B1f3
+# 5: //depot/branch1/b1f4
+#
+test_expect_success !CASE_INSENSITIVE_FS 'basic p4 branches for case folding' '
+	(
+		cd "$cli" &&
+		mkdir -p main &&
+
+		echo mf1 >main/mf1 &&
+		p4 add main/mf1 &&
+		p4 submit -d "main/mf1" &&
+
+		p4 integrate //depot/main/... //depot/branch1/... &&
+		p4 submit -d "integrate main to branch1" &&
+
+		echo mf2 >main/mf2 &&
+		p4 add main/mf2 &&
+		p4 submit -d "main/mf2" &&
+
+		mkdir BRANCH1 &&
+		echo B1f3 >BRANCH1/B1f3 &&
+		p4 add BRANCH1/B1f3 &&
+		p4 submit -d "BRANCH1/B1f3" &&
+
+		echo b1f4 >branch1/b1f4 &&
+		p4 add branch1/b1f4 &&
+		p4 submit -d "branch1/b1f4"
+	)
+'
+
+# Check that files are properly split across branches when ignorecase is set
+test_expect_success !CASE_INSENSITIVE_FS 'git p4 clone, branchList branch definition, ignorecase' '
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList main:branch1 &&
+		git config --type=bool core.ignoreCase true &&
+		git p4 clone --dest=. --detect-branches //depot@all &&
+
+		git log --all --graph --decorate --stat &&
+
+		git reset --hard p4/master &&
+		test_path_is_file mf1 &&
+		test_path_is_file mf2 &&
+		test_path_is_missing B1f3 &&
+		test_path_is_missing b1f4 &&
+
+		git reset --hard p4/depot/branch1 &&
+		test_path_is_file mf1 &&
+		test_path_is_missing mf2 &&
+		test_path_is_file B1f3 &&
+		test_path_is_file b1f4
+	)
+'
+
+# Check that files are properly split across branches when ignorecase is set, use-client-spec case
+test_expect_success !CASE_INSENSITIVE_FS 'git p4 clone with client-spec, branchList branch definition, ignorecase' '
+	client_view "//depot/... //client/..." &&
+	test_when_finished cleanup_git &&
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList main:branch1 &&
+		git config --type=bool core.ignoreCase true &&
+		git p4 clone --dest=. --use-client-spec --detect-branches //depot@all &&
+
+		git log --all --graph --decorate --stat &&
+
+		git reset --hard p4/master &&
+		test_path_is_file mf1 &&
+		test_path_is_file mf2 &&
+		test_path_is_missing B1f3 &&
+		test_path_is_missing b1f4 &&
+
+		git reset --hard p4/depot/branch1 &&
+		test_path_is_file mf1 &&
+		test_path_is_missing mf2 &&
+		test_path_is_file B1f3 &&
+		test_path_is_file b1f4
+	)
+'
+
 test_done
diff --git a/t/t9817-git-p4-exclude.sh b/t/t9817-git-p4-exclude.sh
index 96d25f0..ec3d937 100755
--- a/t/t9817-git-p4-exclude.sh
+++ b/t/t9817-git-p4-exclude.sh
@@ -22,7 +22,9 @@
 		mkdir -p wanted discard &&
 		echo wanted >wanted/foo &&
 		echo discard >discard/foo &&
-		p4 add wanted/foo discard/foo &&
+		echo discard_file >discard_file &&
+		echo discard_file_not >discard_file_not &&
+		p4 add wanted/foo discard/foo discard_file discard_file_not &&
 		p4 submit -d "initial revision"
 	)
 '
@@ -33,7 +35,9 @@
 	(
 		cd "$git" &&
 		test_path_is_file wanted/foo &&
-		test_path_is_file discard/foo
+		test_path_is_file discard/foo &&
+		test_path_is_file discard_file &&
+		test_path_is_file discard_file_not
 	)
 '
 
@@ -43,7 +47,21 @@
 	(
 		cd "$git" &&
 		test_path_is_file wanted/foo &&
-		test_path_is_missing discard/foo
+		test_path_is_missing discard/foo &&
+		test_path_is_file discard_file &&
+		test_path_is_file discard_file_not
+	)
+'
+
+test_expect_success 'clone, excluding single file, no trailing /' '
+	test_when_finished cleanup_git &&
+	git p4 clone -//depot/discard_file --dest="$git" //depot/...@all &&
+	(
+		cd "$git" &&
+		test_path_is_file wanted/foo &&
+		test_path_is_file discard/foo &&
+		test_path_is_missing discard_file &&
+		test_path_is_file discard_file_not
 	)
 '
 
@@ -52,15 +70,38 @@
 	git p4 clone -//depot/discard/... --dest="$git" //depot/...@all &&
 	(
 		cd "$cli" &&
-		p4 edit wanted/foo discard/foo &&
+		p4 edit wanted/foo discard/foo discard_file_not &&
 		date >>wanted/foo &&
 		date >>discard/foo &&
+		date >>discard_file_not &&
 		p4 submit -d "updating" &&
 
 		cd "$git" &&
 		git p4 sync -//depot/discard/... &&
 		test_path_is_file wanted/foo &&
-		test_path_is_missing discard/foo
+		test_path_is_missing discard/foo &&
+		test_path_is_file discard_file &&
+		test_path_is_file discard_file_not
+	)
+'
+
+test_expect_success 'clone, then sync with exclude, no trailing /' '
+	test_when_finished cleanup_git &&
+	git p4 clone -//depot/discard/... -//depot/discard_file --dest="$git" //depot/...@all &&
+	(
+		cd "$cli" &&
+		p4 edit wanted/foo discard/foo discard_file_not &&
+		date >>wanted/foo &&
+		date >>discard/foo &&
+		date >>discard_file_not &&
+		p4 submit -d "updating" &&
+
+		cd "$git" &&
+		git p4 sync -//depot/discard/... -//depot/discard_file &&
+		test_path_is_file wanted/foo &&
+		test_path_is_missing discard/foo &&
+		test_path_is_missing discard_file &&
+		test_path_is_file discard_file_not
 	)
 '
 
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 0367cec..7308f67 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -908,6 +908,21 @@
 	fi
 }
 
+# Compare paths respecting core.ignoreCase
+test_cmp_fspath () {
+	if test "x$1" = "x$2"
+	then
+		return 0
+	fi
+
+	if test true != "$(git config --get --type=bool core.ignorecase)"
+	then
+		return 1
+	fi
+
+	test "x$(echo "$1" | tr A-Z a-z)" =  "x$(echo "$2" | tr A-Z a-z)"
+}
+
 # Print a sequence of integers in increasing order, either with
 # two arguments (start and end):
 #
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 4b34646..d1ba337 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -386,7 +386,6 @@
 	my @env = keys %ENV;
 	my $ok = join("|", qw(
 		TRACE
-		TR2_
 		DEBUG
 		TEST
 		.*_TEST
diff --git a/tag.c b/tag.c
index 7445b8f..5db870e 100644
--- a/tag.c
+++ b/tag.c
@@ -100,10 +100,9 @@
 
 struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
 {
-	struct object *obj = lookup_object(r, oid->hash);
+	struct object *obj = lookup_object(r, oid);
 	if (!obj)
-		return create_object(r, oid->hash,
-				     alloc_tag_node(r));
+		return create_object(r, oid, alloc_tag_node(r));
 	return object_as_type(r, obj, OBJ_TAG, 0);
 }
 
diff --git a/tree.c b/tree.c
index f416afc..4720945 100644
--- a/tree.c
+++ b/tree.c
@@ -197,10 +197,9 @@
 
 struct tree *lookup_tree(struct repository *r, const struct object_id *oid)
 {
-	struct object *obj = lookup_object(r, oid->hash);
+	struct object *obj = lookup_object(r, oid);
 	if (!obj)
-		return create_object(r, oid->hash,
-				     alloc_tree_node(r));
+		return create_object(r, oid, alloc_tree_node(r));
 	return object_as_type(r, obj, OBJ_TREE, 0);
 }
 
diff --git a/unpack-trees.c b/unpack-trees.c
index 5018990..dab7132 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -315,7 +315,7 @@
 			total++;
 	}
 
-	return start_delayed_progress(_("Checking out files"), total);
+	return start_delayed_progress(_("Updating files"), total);
 }
 
 static void setup_collided_checkout_detection(struct checkout *state,
diff --git a/upload-pack.c b/upload-pack.c
index 4d2129e..222cd3a 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -528,13 +528,13 @@
 		return -1;
 
 	while ((i = read_in_full(cmd.out, namebuf, hexsz + 1)) == hexsz + 1) {
-		struct object_id sha1;
+		struct object_id oid;
 		const char *p;
 
-		if (parse_oid_hex(namebuf, &sha1, &p) || *p != '\n')
+		if (parse_oid_hex(namebuf, &oid, &p) || *p != '\n')
 			break;
 
-		o = lookup_object(the_repository, sha1.hash);
+		o = lookup_object(the_repository, &oid);
 		if (o && o->type == OBJ_COMMIT) {
 			o->flags &= ~TMP_MARK;
 		}
@@ -722,7 +722,7 @@
 {
 	struct commit_list *result;
 
-	close_commit_graph(the_repository);
+	close_commit_graph(the_repository->objects);
 	result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
 	send_shallow(writer, result);
 	free_commit_list(result);
@@ -960,7 +960,7 @@
 static int mark_our_ref(const char *refname, const char *refname_full,
 			const struct object_id *oid)
 {
-	struct object *o = lookup_unknown_object(oid->hash);
+	struct object *o = lookup_unknown_object(oid);
 
 	if (ref_is_hidden(refname, refname_full)) {
 		o->flags |= HIDDEN_REF;
diff --git a/walker.c b/walker.c
index d74ae59..06cd2bd 100644
--- a/walker.c
+++ b/walker.c
@@ -285,7 +285,7 @@
 			error("Could not interpret response from server '%s' as something to pull", target[i]);
 			goto done;
 		}
-		if (process(walker, lookup_unknown_object(oids[i].hash)))
+		if (process(walker, lookup_unknown_object(&oids[i])))
 			goto done;
 	}
 
diff --git a/wrapper.c b/wrapper.c
index ea3cf64..1e45ab7 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -502,7 +502,7 @@
 	 * Try TMP_MAX different filenames.
 	 */
 	gettimeofday(&tv, NULL);
-	value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+	value = ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
 	filename_template = &pattern[len - 6 - suffix_len];
 	for (count = 0; count < TMP_MAX; ++count) {
 		uint64_t v = value;
diff --git a/wt-status.c b/wt-status.c
index 0bccef5..7d776f8 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -19,6 +19,8 @@
 #include "lockfile.h"
 #include "sequencer.h"
 
+#define AB_DELAY_WARNING_IN_MS (2 * 1000)
+
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
 
@@ -179,9 +181,15 @@
 		return;
 	if (s->whence != FROM_COMMIT)
 		;
-	else if (!s->is_initial)
-		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-	else
+	else if (!s->is_initial) {
+		if (!strcmp(s->reference, "HEAD"))
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --staged <file>...\" to unstage)"));
+		else
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
+					 s->reference);
+	} else
 		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
 
 	if (!both_deleted) {
@@ -206,9 +214,15 @@
 		return;
 	if (s->whence != FROM_COMMIT)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
-	else if (!s->is_initial)
-		status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
-	else
+	else if (!s->is_initial) {
+		if (!strcmp(s->reference, "HEAD"))
+			status_printf_ln(s, c
+					 , _("  (use \"git restore --staged <file>...\" to unstage)"));
+		else
+			status_printf_ln(s, c,
+					 _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
+					 s->reference);
+	} else
 		status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
 	status_printf_ln(s, c, "%s", "");
 }
@@ -226,7 +240,7 @@
 		status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
 	else
 		status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
-	status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
+	status_printf_ln(s, c, _("  (use \"git restore <file>...\" to discard changes in working directory)"));
 	if (has_dirty_submodules)
 		status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
 	status_printf_ln(s, c, "%s", "");
@@ -1085,14 +1099,29 @@
 	struct branch *branch;
 	char comment_line_string[3];
 	int i;
+	uint64_t t_begin = 0;
 
 	assert(s->branch && !s->is_initial);
 	if (!skip_prefix(s->branch, "refs/heads/", &branch_name))
 		return;
 	branch = branch_get(branch_name);
+
+	t_begin = getnanotime();
+
 	if (!format_tracking_info(branch, &sb, s->ahead_behind_flags))
 		return;
 
+	if (advice_status_ahead_behind_warning &&
+	    s->ahead_behind_flags == AHEAD_BEHIND_FULL) {
+		uint64_t t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
+		if (t_delta_in_ms > AB_DELAY_WARNING_IN_MS) {
+			strbuf_addf(&sb, _("\n"
+					   "It took %.2f seconds to compute the branch ahead/behind values.\n"
+					   "You can use '--no-ahead-behind' to avoid this.\n"),
+				    t_delta_in_ms / 1000.0);
+		}
+	}
+
 	i = 0;
 	if (s->display_comment_prefix) {
 		comment_line_string[i++] = comment_line_char;
@@ -1676,9 +1705,9 @@
 			} else if (s->state.detached_from) {
 				branch_name = s->state.detached_from;
 				if (s->state.detached_at)
-					on_what = _("HEAD detached at ");
+					on_what = HEAD_DETACHED_AT;
 				else
-					on_what = _("HEAD detached from ");
+					on_what = HEAD_DETACHED_FROM;
 			} else {
 				branch_name = "";
 				on_what = _("Not currently on any branch.");
diff --git a/wt-status.h b/wt-status.h
index 64f1ddc..b0cfdc8 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -65,6 +65,9 @@
 	STATUS_FORMAT_UNSPECIFIED
 };
 
+#define HEAD_DETACHED_AT _("HEAD detached at ")
+#define HEAD_DETACHED_FROM _("HEAD detached from ")
+
 struct wt_status_state {
 	int merge_in_progress;
 	int am_in_progress;