Merge branch 'ds/fetch-disable-force-notice'
authorJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:46 +0000 (15:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:46 +0000 (15:25 -0700)
"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.

* ds/fetch-disable-force-notice:
  pull: add --[no-]show-forced-updates passthrough
  fetch: warn about forced updates in branch listing
  fetch: add --[no-]show-forced-updates argument

214 files changed:
.clang-format
.gitattributes
.gitignore
Documentation/MyFirstContribution.txt
Documentation/RelNotes/2.23.0.txt
Documentation/config.txt
Documentation/config/advice.txt
Documentation/config/alias.txt
Documentation/config/branch.txt
Documentation/config/checkout.txt
Documentation/config/diff.txt
Documentation/config/interactive.txt
Documentation/config/status.txt
Documentation/config/tag.txt
Documentation/fetch-options.txt
Documentation/git-branch.txt
Documentation/git-check-ref-format.txt
Documentation/git-checkout.txt
Documentation/git-clean.txt
Documentation/git-commit.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-hash-object.txt
Documentation/git-merge-base.txt
Documentation/git-merge.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-rerere.txt
Documentation/git-reset.txt
Documentation/git-restore.txt [new file with mode: 0644]
Documentation/git-rev-list.txt
Documentation/git-revert.txt
Documentation/git-send-email.txt
Documentation/git-stash.txt
Documentation/git-switch.txt [new file with mode: 0644]
Documentation/git-tag.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitcli.txt
Documentation/gitcore-tutorial.txt
Documentation/giteveryday.txt
Documentation/githooks.txt
Documentation/gitignore.txt
Documentation/gittutorial-2.txt
Documentation/gittutorial.txt
Documentation/gitweb.txt
Documentation/gitworkflows.txt
Documentation/rev-list-options.txt
Documentation/revisions.txt
Documentation/technical/api-trace2.txt
Documentation/technical/commit-graph.txt
Documentation/user-manual.txt
Makefile
advice.c
advice.h
blob.c
branch.c
branch.h
builtin.h
builtin/am.c
builtin/blame.c
builtin/branch.c
builtin/checkout.c
builtin/clone.c
builtin/commit-graph.c
builtin/commit.c
builtin/describe.c
builtin/fast-export.c
builtin/fetch.c
builtin/fsck.c
builtin/gc.c
builtin/index-pack.c
builtin/interpret-trailers.c
builtin/ls-files.c
builtin/merge.c
builtin/name-rev.c
builtin/pack-objects.c
builtin/prune.c
builtin/rebase.c
builtin/receive-pack.c
builtin/repack.c
builtin/reset.c
builtin/rev-list.c
builtin/revert.c
builtin/stash.c
builtin/submodule--helper.c
builtin/tag.c
builtin/unpack-objects.c
cache-tree.c
cache.h
command-list.txt
commit-graph.c
commit-graph.h
commit.c
compat/mingw.c
compat/mingw.h
compat/msvc.h
compat/obstack.h
compat/poll/poll.c
compat/vcbuild/.gitignore [new file with mode: 0644]
compat/vcbuild/README
compat/vcbuild/find_vs_env.bat [new file with mode: 0644]
compat/vcbuild/scripts/clink.pl
compat/vcbuild/vcpkg_copy_dlls.bat [new file with mode: 0644]
compat/vcbuild/vcpkg_install.bat [new file with mode: 0644]
compat/winansi.c
config.c
config.mak.uname
configure.ac
contrib/coccinelle/array.cocci
contrib/completion/git-completion.bash
decorate.c
delta-islands.c
delta-islands.h
diffcore-rename.c
dir.c
editor.c
fast-import.c
fetch-pack.c
fsck.c
fsmonitor.c
git-add--interactive.perl
git-compat-util.h
git-mergetool.sh
git-p4.py
git.c
hash.h
hashmap.h
http-push.c
khash.h
kwset.c
list-objects-filter-options.c
list-objects-filter.c
name-hash.c
object.c
object.h
oidset.c
oidset.h
pack-bitmap-write.c
pack-bitmap.c
pack-bitmap.h
pack-objects.c
pack-objects.h
packfile.c
packfile.h
pager.c
parse-options-cb.c
parse-options.h
patch-ids.c
pretty.c
progress.c
reachable.c
read-cache.c
ref-filter.c
refs.c
sequencer.c
sequencer.h
sh-i18n--envsubst.c
sha1-name.c
t/helper/test-example-decorate.c
t/lib-patch-mode.sh
t/t0001-init.sh
t/t0007-git-var.sh
t/t1090-sparse-checkout-scope.sh
t/t1305-config-include.sh
t/t2014-checkout-switch.sh [moved from t/t2014-switch.sh with 100% similarity]
t/t2020-checkout-detach.sh
t/t2060-switch.sh [new file with mode: 0755]
t/t2070-restore.sh [new file with mode: 0755]
t/t2071-restore-patch.sh [new file with mode: 0755]
t/t3200-branch.sh
t/t3203-branch-output.sh
t/t3404-rebase-interactive.sh
t/t3420-rebase-autostash.sh
t/t3430-rebase-merges.sh
t/t3701-add-interactive.sh
t/t3903-stash.sh
t/t4018-diff-funcname.sh
t/t4018/rust-fn [new file with mode: 0644]
t/t4018/rust-impl [new file with mode: 0644]
t/t4018/rust-struct [new file with mode: 0644]
t/t4018/rust-trait [new file with mode: 0644]
t/t5318-commit-graph.sh
t/t5514-fetch-multiple.sh
t/t5541-http-push-smart.sh
t/t5551-http-fetch-smart.sh
t/t5616-partial-clone.sh
t/t5801-remote-helpers.sh
t/t5801/git-remote-testgit
t/t6000-rev-list-misc.sh
t/t6040-tracking-info.sh
t/t6302-for-each-ref-filter.sh
t/t7004-tag.sh
t/t7064-wtstatus-pv2.sh
t/t7407-submodule-foreach.sh
t/t7502-commit-porcelain.sh
t/t7508-status.sh
t/t7512-status-help.sh
t/t7513-interpret-trailers.sh
t/t7610-mergetool.sh
t/t9801-git-p4-branch.sh
t/t9817-git-p4-exclude.sh
t/test-lib-functions.sh
t/test-lib.sh
tag.c
tree.c
unpack-trees.c
upload-pack.c
url.c
userdiff.c
walker.c
wrapper.c
wt-status.c
wt-status.h

index 41d4cd2..c592dda 100644 (file)
@@ -148,8 +148,21 @@ SpacesInSquareBrackets: false
 Cpp11BracedListStyle: false
 
 # A list of macros that should be interpreted as foreach loops instead of as
-# function calls.
-ForEachMacros: ['for_each_string_list_item', 'for_each_wanted_builtin', 'for_each_builtin', 'for_each_ut']
+# function calls. Taken from:
+#   git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' \
+#   | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$,  - '\1'," \
+#   | sort | uniq
+ForEachMacros:
+  - 'for_each_abbrev'
+  - 'for_each_builtin'
+  - 'for_each_string_list_item'
+  - 'for_each_ut'
+  - 'for_each_wanted_builtin'
+  - 'list_for_each'
+  - 'list_for_each_dir'
+  - 'list_for_each_prev'
+  - 'list_for_each_prev_safe'
+  - 'list_for_each_safe'
 
 # The maximum number of consecutive empty lines to keep.
 MaxEmptyLinesToKeep: 1
index 9fa72ad..b08a141 100644 (file)
@@ -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
index 4470d7c..e096e0a 100644 (file)
 /git-request-pull
 /git-rerere
 /git-reset
+/git-restore
 /git-rev-list
 /git-rev-parse
 /git-revert
 /git-submodule
 /git-submodule--helper
 /git-svn
+/git-switch
 /git-symbolic-ref
 /git-tag
 /git-unpack-file
 *.user
 *.idb
 *.pdb
+*.ilk
+*.iobj
+*.ipdb
+*.dll
+.vs/
 /Debug/
 /Release/
 *.dSYM
index 895b7cf..f867037 100644 (file)
@@ -428,7 +428,7 @@ git-psuh - Delight users' typo with a shy horse
 SYNOPSIS
 --------
 [verse]
-'git-psuh'
+'git-psuh [<arg>...]'
 
 DESCRIPTION
 -----------
@@ -491,14 +491,16 @@ Take a look at `Documentation/technical/api-parse-options.txt`. This is a handy
 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,
 };
 ----
index 53e8234..a6cd592 100644 (file)
@@ -43,6 +43,9 @@ UI, Workflows & Features
  * "git help git" was hard to discover (well, at least for some
    people).
 
+ * The pattern "git diff/grep" use to extract funcname and words
+   boundary for Rust has been added.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -148,7 +151,33 @@ Fixes since v2.22
    to honor the "--origin <name>" option.
    (merge 1c4a9f9114 xl/record-partial-clone-origin later to maint).
 
+ * "git fetch" into a lazy clone forgot to fetch base objects that are
+   necessary to complete delta in a thin packfile, which has been
+   corrected.
+   (merge 810e19322d jt/partial-clone-missing-ref-delta-base later to maint).
+
+ * The filter_data used in the list-objects-filter (which manages a
+   lazily sparse clone repository) did not use the dynamic array API
+   correctly---'nr' is supposed to point at one past the last element
+   of the array in use.  This has been corrected.
+   (merge 7140600e2e md/list-objects-filter-memfix later to maint).
+
+ * The description about slashes in gitignore patterns (used to
+   indicate things like "anchored to this level only" and "only
+   matches directories") has been revamped.
+   (merge 1a58bad014 an/ignore-doc-update later to maint).
+
+ * The URL decoding code has been updated to avoid going past the end
+   of the string while parsing %-<hex>-<hex> sequence.
+   (merge d37dc239a4 md/url-parse-harden later to maint).
+
+ * The list of for-each like macros used by clang-format has been
+   updated.
+   (merge fc7e03aace mo/clang-format-for-each-update 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).
index 7e2a6f6..e3f5bc3 100644 (file)
@@ -144,6 +144,20 @@ refer to linkgit:gitignore[5] for details. For convenience:
        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 @@ Example
        [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
 ~~~~~~
 
index 1f1e847..ee85c53 100644 (file)
@@ -41,12 +41,19 @@ advice.*::
                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
@@ -66,12 +73,14 @@ advice.*::
                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
index 0b14178..f1ca739 100644 (file)
@@ -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].
index 8f4b3fa..a592d52 100644 (file)
@@ -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.autoSetupMerge::
        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.
index c4118fa..6b64681 100644 (file)
@@ -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 @@ checkout.defaultRemote::
        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.
index 2c4c9ba..5afb5a2 100644 (file)
@@ -78,7 +78,8 @@ diff.external::
 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
index ad846dd..a2d3c7e 100644 (file)
@@ -2,7 +2,8 @@ interactive.singleKey::
        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.
index ed72fa7..0fc704a 100644 (file)
@@ -12,6 +12,11 @@ status.branch::
        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
index 663663b..ef5adb3 100644 (file)
@@ -8,6 +8,14 @@ tag.sort::
        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
index 5801d23..3c9b4f9 100644 (file)
@@ -88,6 +88,10 @@ ifndef::git-pull[]
        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
index 6ebd512..135206f 100644 (file)
@@ -8,12 +8,14 @@ git-branch - List, create, or delete branches
 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 @@ DESCRIPTION
 -----------
 
 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 @@ can leave out at most one of `A` and `B`, in which case it defaults to
 `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 @@ This option is only applicable in non-verbose mode.
 -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 @@ This option is only applicable in non-verbose mode.
        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 option is only applicable in non-verbose mode.
 +
 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 @@ Start development from a known tag::
 $ 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 @@ $ git branch -D test                                    <2>
 <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:
index d9de992..ee6a414 100644 (file)
@@ -88,7 +88,8 @@ but it is explicitly forbidden at the beginning of a branch name).
 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
index 964f912..cf3cac0 100644 (file)
@@ -23,31 +23,22 @@ or the specified tree.  If no paths are given, 'git checkout' will
 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 @@ if exists, for the current branch.
        `--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 @@ successful.
 '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 @@ OPTIONS
 --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 @@ OPTIONS
 -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 @@ on your side branch as `theirs` (i.e. "one contributor's work on top
 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 @@ If no `-b` 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
+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 @@ explicitly give a name with `-b` in such a case.
 --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 @@ whose full history contains proprietary or otherwise encumbered bits of
 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 @@ the conflicted merge in the specified paths.
 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 @@ Note that this option uses the no overlay mode by default (see also
        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.
-
---no-guess::
-       Do not attempt to create a branch if a remote tracking branch
-       of the same name exists.
+       Just like linkgit:git-submodule[1], this will detach `HEAD` of the
+       submodule.
 
---[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 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 <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 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 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 @@ a---b---c  branch 'master' (refers to commit 'c')
 ------------
 
 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 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 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 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   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 @@ a---b---c---d  branch 'master' (refers to commit 'd')
   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 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 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 @@ a---b---c---d  branch 'master' (refers to commit 'd')
 ------------
 
 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 branch foo        <2>
 $ 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 @@ $ git log -g -2 HEAD
 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 @@ EXAMPLES
 --------
 
 . 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 @@ $ git checkout hello.c            <3>
 +
 <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 -- hello.c
 $ 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 @@ $ edit frotz
 $ git add frotz
 ------------
 
+SEE ALSO
+--------
+linkgit:git-switch[1],
+linkgit:git-restore[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index db876f7..0028ff1 100644 (file)
@@ -63,7 +63,7 @@ OPTIONS
        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::
index a85c2c2..7628193 100644 (file)
@@ -359,7 +359,7 @@ When recording your own work, the contents of modified files in
 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,
index 774cecc..6dcd39f 100644 (file)
@@ -214,6 +214,11 @@ symref::
        `: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.
index 9ce5b8a..b9b97e6 100644 (file)
@@ -426,8 +426,8 @@ One way to test if your MUA is set up correctly is:
 * 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.
index 814e744..df9e2c5 100644 (file)
@@ -18,9 +18,7 @@ Computes the object ID value for an object with specified type
 with the contents of the named file (which can be outside of the
 work tree), and optionally writes the resulting object into the
 object database.  Reports its object ID to its standard output.
-This is used by 'git cvsimport' to update the index
-without modifying files in the work tree.  When <type> is not
-specified, it defaults to "blob".
+When <type> is not specified, it defaults to "blob".
 
 OPTIONS
 -------
index 9f07f4f..261d5c1 100644 (file)
@@ -149,7 +149,7 @@ instead.
 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:
index c01cfa6..01fd52d 100644 (file)
@@ -13,8 +13,7 @@ SYNOPSIS
        [-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 @@ will be appended to the specified message.
        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.
index 5e4e927..6156609 100644 (file)
@@ -12,12 +12,12 @@ SYNOPSIS
        [<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
index 0cad37f..9659abb 100644 (file)
@@ -230,7 +230,7 @@ $ git branch -r
   staging/master
   staging/staging-linus
   staging/staging-next
-$ git checkout -b staging staging/master
+$ git switch -c staging staging/master
 ...
 ------------
 
index 95763d7..4cfc883 100644 (file)
@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.
 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 @@ the upstream might have been advanced since the test merge `+`,
 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 @@ merges, you could blow away the test merge, and keep building on
 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
index 26e746c..97e0544 100644 (file)
@@ -25,12 +25,13 @@ The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
        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 @@ but carries forward unmerged index entries.
        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 @@ See also the `--amend` option to linkgit:git-commit[1].
 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 @@ working tree are not in any shape to be committed yet, but you
 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 @@ reset it while keeping the changes in your working tree.
 +
 ------------
 $ 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 (file)
index 0000000..d90093f
--- /dev/null
@@ -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
index 88609ff..9392760 100644 (file)
@@ -48,6 +48,7 @@ SYNOPSIS
             [ --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 ]
index 0c82ca5..fae4d66 100644 (file)
@@ -26,10 +26,13 @@ effect of some earlier commits (often only a faulty one).  If you want to
 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>...::
index a861934..d93e5d0 100644 (file)
@@ -508,8 +508,12 @@ app-specific or your regular password as appropriate.  If you have credential
 helper configured (see linkgit:git-credential[1]), the password will be saved in
 the credential store so you won't have to type it the next time.
 
-Note: the following perl modules are required
-      Net::SMTP::SSL, MIME::Base64 and Authen::SASL
+Note: the following core Perl modules that may be installed with your
+distribution of Perl are required:
+MIME::Base64, MIME::QuotedPrint, Net::Domain and Net::SMTP.
+These additional Perl modules are also required:
+Authen::SASL and Mail::Address.
+
 
 SEE ALSO
 --------
index e31ea7d..8fbe12c 100644 (file)
@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:
 +
 ----------------------------------------------------------------
 # ... 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 @@ SEE ALSO
 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 (file)
index 0000000..1979003
--- /dev/null
@@ -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
index a74e7b9..2e5599a 100644 (file)
@@ -64,6 +64,13 @@ OPTIONS
 -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>::
index f9b09db..e095514 100644 (file)
@@ -211,6 +211,26 @@ people via patch over e-mail.
 
 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)
 -----------------------------
index e387cc6..fb1d188 100644 (file)
@@ -112,7 +112,8 @@ Checking-out and checking-in
 
 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'.
 
@@ -833,6 +834,8 @@ patterns are available:
 
 - `ruby` suitable for source code in the Ruby language.
 
+- `rust` suitable for source code in the Rust language.
+
 - `tex` suitable for source code for LaTeX documents.
 
 
index 592e06d..1ed3ca3 100644 (file)
@@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.
    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 @@ See also http://marc.info/?l=git&m=116563135620359 and
 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
index e29a9ef..f880d21 100644 (file)
@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just
 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 @@ just telling 'git checkout' what the base of the checkout would be.
 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 @@ and check out the state at that time.
 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 @@ $ git branch <branchname> [startingpoint]
 
 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 @@ being the same as the original `master` branch, let's make sure we're in
 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 @@ does some work in the original branch, and simulate that by going back
 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 @@ to the `master` branch. Let's go back to `mybranch`, and run
 '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 @@ Remember, before running 'git merge', our `master` head was at
 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^
 ------------
 
index 9f2528f..1bd919f 100644 (file)
@@ -41,7 +41,7 @@ following commands.
 
   * 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 @@ following commands.
 
   * 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 @@ $ git tag v2.43 <2>
 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 @@ $ git commit -a -s <5>
 $ 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 @@ Clone the upstream and work on it.  Feed changes to upstream.::
 ------------
 $ 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$ edit/compile/test/commit
 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 @@ machine into the master branch.
 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 @@ $ mailx <3>
 & 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>
index 786e778..82cd573 100644 (file)
@@ -165,12 +165,13 @@ rebased, and is not set when rebasing the current branch.
 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 @@ exit with a zero status.
 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.
 
index b5bc9db..d47b1ae 100644 (file)
@@ -89,28 +89,28 @@ PATTERN FORMAT
    Put a backslash ("`\`") in front of the first "`!`" for patterns
    that begin with a literal "`!`", for example, "`\!important!.txt`".
 
- - If the pattern ends with a slash, it is removed for the
-   purpose of the following description, but it would only find
-   a match with a directory.  In other words, `foo/` will match a
-   directory `foo` and paths underneath it, but will not match a
-   regular file or a symbolic link `foo` (this is consistent
-   with the way how pathspec works in general in Git).
-
- - If the pattern does not contain a slash '/', Git treats it as
-   a shell glob pattern and checks for a match against the
-   pathname relative to the location of the `.gitignore` file
-   (relative to the toplevel of the work tree if not from a
-   `.gitignore` file).
-
- - Otherwise, Git treats the pattern as a shell glob: "`*`" matches
-   anything except "`/`", "`?`" matches any one character except "`/`"
-   and "`[]`" matches one character in a selected range. See
-   fnmatch(3) and the FNM_PATHNAME flag for a more detailed
  description.
-
- - A leading slash matches the beginning of the pathname.
-   For example, "/{asterisk}.c" matches "cat-file.c" but not
-   "mozilla-sha1/sha1.c".
+ - The slash '/' is used as the directory separator. Separators may
+   occur at the beginning, middle or end of the `.gitignore` search pattern.
+
+ - If there is a separator at the beginning or middle (or both) of the
+   pattern, then the pattern is relative to the directory level of the
+   particular `.gitignore` file itself. Otherwise the pattern may also
+   match at any level below the `.gitignore` level.
+
+ - If there is a separator at the end of the pattern then the pattern
+   will only match directories, otherwise the pattern can match both
+   files and directories.
+
+ - For example, a pattern `doc/frotz/` matches `doc/frotz` directory,
+   but not `a/doc/frotz` directory; however `frotz/` matches `frotz`
+   and `a/frotz` that is a directory (all paths are relative from
+   the `.gitignore` file).
+
- An asterisk "`*`" matches anything except a slash.
+   The character "`?`" matches any one character except "`/`".
+   The range notation, e.g. `[a-zA-Z]`, can be used to match
+   one of the characters in a range. See fnmatch(3) and the
+   FNM_PATHNAME flag for a more detailed description.
 
 Two consecutive asterisks ("`**`") in patterns matched against
 full pathname may have special meaning:
@@ -152,6 +152,28 @@ To stop tracking a file that is currently tracked, use
 EXAMPLES
 --------
 
+ - The pattern `hello.*` matches any file or folder
+   whose name begins with `hello`. If one wants to restrict
+   this only to the directory and not in its subdirectories,
+   one can prepend the pattern with a slash, i.e. `/hello.*`;
+   the pattern now matches `hello.txt`, `hello.c` but not
+   `a/hello.java`.
+
+ - The pattern `foo/` will match a directory `foo` and
+   paths underneath it, but will not match a regular file
+   or a symbolic link `foo` (this is consistent with the
+   way how pathspec works in general in Git)
+
+ - The pattern `doc/frotz` and `/doc/frotz` have the same effect
+   in any `.gitignore` file. In other words, a leading slash
+   is not relevant  if there is already a middle slash in
+   the pattern.
+
+ - The pattern "foo/*", matches "foo/test.json"
+   (a regular file), "foo/bar" (a directory), but it does not match
+   "foo/bar/hello.c" (a regular file), as the asterisk in the
+   pattern does not match "bar/hello.c" which has a slash in it.
+
 --------------------------------------------------------------
     $ git status
     [...]
index e0976f6..8bdb7d0 100644 (file)
@@ -370,13 +370,13 @@ situation:
 $ 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
 
index 242de31..59ef5ce 100644 (file)
@@ -110,7 +110,7 @@ $ git status
 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 @@ automatically.  The asterisk marks the branch you are currently on;
 type
 
 ------------------------------------------------
-$ git checkout experimental
+$ git switch experimental
 ------------------------------------------------
 
 to switch to the experimental branch.  Now edit a file, commit the
@@ -216,7 +216,7 @@ change, and switch back to the master branch:
 ------------------------------------------------
 (edit file)
 $ git commit -a
-$ git checkout master
+$ git switch master
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
index c743609..3cc9b03 100644 (file)
@@ -28,8 +28,7 @@ Gitweb provides a web interface to Git repositories.  Its features include:
   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.
 
 
index ca11c7b..abc0dc6 100644 (file)
@@ -301,8 +301,7 @@ topics on 'next':
 .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`
 * ...
index 71a1fcc..286fc16 100644 (file)
@@ -708,6 +708,16 @@ ifdef::git-rev-list[]
        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>'
index 82c1e57..97f995e 100644 (file)
@@ -115,7 +115,7 @@ Here's an example to make it more clear:
 ------------------------------
 $ 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
index 23c3cc7..f7ffe7d 100644 (file)
@@ -35,7 +35,7 @@ Format details are given in a later section.
 === 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.
 
index 7805b09..fb53341 100644 (file)
@@ -127,23 +127,6 @@ Design Details
   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
index eff7890..8bce75b 100644 (file)
@@ -122,10 +122,10 @@ Tags are expected to always point at the same version of a project,
 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 @@ a summary of the commands:
        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 @@ ref: refs/heads/master
 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 @@ You might want to build on one of these remote-tracking branches
 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 @@ If you get stuck and decide to just give up and throw the whole mess
 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 @@ mistake, you can return the entire working tree to the last committed
 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 @@ Checking out an old version of a file
 
 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 @@ $ git branch --track release origin/master
 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 @@ tested changes
 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 @@ When you are happy with the state of this change, you can merge it into the
 "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 @@ see the value of keeping each patch (or patch series) in its own branch.  It
 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 @@ Suppose that you create a branch `mywork` on a remote-tracking branch
 `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 @@ commits without any merges, you may instead choose to use
 linkgit:git-rebase[1]:
 
 -------------------------------------------------
-$ git checkout mywork
+$ git switch mywork
 $ git rebase origin
 -------------------------------------------------
 
@@ -3668,13 +3666,13 @@ change within the submodule, and then update the superproject to reference the
 new commit:
 
 -------------------------------------------------
-$ git checkout master
+$ git switch master
 -------------------------------------------------
 
 or
 
 -------------------------------------------------
-$ git checkout -b fix-up
+$ git switch -c fix-up
 -------------------------------------------------
 
 then
@@ -3800,8 +3798,8 @@ use linkgit:git-tag[1] for both.
 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 @@ start.
 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 @@ Managing branches
 -----------------
 
 -----------------------------------------------
-$ 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 @@ $ git branch new test~10 # ten commits before tip of branch "test"
 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 @@ $ git branch -r           # list
   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
index f58bf14..98a0588 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -771,9 +771,11 @@ BUILT_INS += git-format-patch$X
 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 @@ endif
 
 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
        $(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 @@ endif
        $(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
index 4b283be..67de6de 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -13,6 +13,7 @@ int advice_push_needs_force = 1;
 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;
@@ -70,6 +71,7 @@ static struct {
        { "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 },
@@ -195,13 +197,22 @@ void NORETURN die_conclude_merge(void)
 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);
 }
index 495e532..940c4c2 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -13,6 +13,7 @@ extern int advice_push_needs_force;
 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 (file)
--- a/blob.c
+++ b/blob.c
@@ -7,10 +7,9 @@ const char *blob_type = "blob";
 
 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);
 }
 
index e70838f..5794947 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -346,9 +346,9 @@ void remove_merge_branch_state(struct repository *r)
        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);
 }
index 064ee57..df0be61 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -70,7 +70,7 @@ void remove_merge_branch_state(struct repository *r);
  * 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"
index ec7e095..3d449a0 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -214,6 +214,7 @@ int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 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_status(int argc, const char **argv, const char *prefix);
 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);
index 78389d0..1aea657 100644 (file)
@@ -1801,7 +1801,7 @@ next:
         */
        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 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
        if (merge_tree(remote_tree))
                return -1;
 
-       remove_branch_state(the_repository);
+       remove_branch_state(the_repository, 0);
 
        return 0;
 }
index 21cde57..50e3d4a 100644 (file)
@@ -59,8 +59,8 @@ static size_t blame_date_width;
 
 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 @@ parse_done:
        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;
index d4359b3..2ef2146 100644 (file)
@@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = {
        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 @@ enum color_branch {
        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 @@ static const char *color_branch_slots[] = {
        [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 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
        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 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
                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 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                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."));
index ffa776c..91f8509 100644 (file)
@@ -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 @@ static const char * const checkout_usage[] = {
        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 @@ struct checkout_opts {
        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 @@ struct checkout_opts {
        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,17 +334,74 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
        }
 }
 
+static int checkout_worktree(const struct checkout_opts *opts)
+{
+       struct checkout state = CHECKOUT_INIT;
+       int nr_checkouts = 0, nr_unmerged = 0;
+       int errs = 0;
+       int pos;
+
+       state.force = 1;
+       state.refresh_cache = 1;
+       state.istate = &the_index;
+
+       enable_delayed_checkout(&state);
+       for (pos = 0; pos < active_nr; pos++) {
+               struct cache_entry *ce = active_cache[pos];
+               if (ce->ce_flags & CE_MATCHED) {
+                       if (!ce_stage(ce)) {
+                               errs |= checkout_entry(ce, &state,
+                                                      NULL, &nr_checkouts);
+                               continue;
+                       }
+                       if (opts->writeout_stage)
+                               errs |= checkout_stage(opts->writeout_stage,
+                                                      ce, pos,
+                                                      &state,
+                                                      &nr_checkouts, opts->overlay_mode);
+                       else if (opts->merge)
+                               errs |= checkout_merged(pos, &state,
+                                                       &nr_unmerged);
+                       pos = skip_same_name(ce, pos) - 1;
+               }
+       }
+       remove_marked_cache_entries(&the_index, 1);
+       remove_scheduled_dirs();
+       errs |= finish_delayed_checkout(&state, &nr_checkouts);
+
+       if (opts->count_checkout_paths) {
+               if (nr_unmerged)
+                       fprintf_ln(stderr, Q_("Recreated %d merge conflict",
+                                             "Recreated %d merge conflicts",
+                                             nr_unmerged),
+                                  nr_unmerged);
+               if (opts->source_tree)
+                       fprintf_ln(stderr, Q_("Updated %d path from %s",
+                                             "Updated %d paths from %s",
+                                             nr_checkouts),
+                                  nr_checkouts,
+                                  find_unique_abbrev(&opts->source_tree->object.oid,
+                                                     DEFAULT_ABBREV));
+               else if (!nr_unmerged || nr_checkouts)
+                       fprintf_ln(stderr, Q_("Updated %d path from the index",
+                                             "Updated %d paths from the index",
+                                             nr_checkouts),
+                                  nr_checkouts);
+       }
+
+       return errs;
+}
+
 static int checkout_paths(const struct checkout_opts *opts,
                          const char *revision)
 {
        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 checkout_index;
 
        trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
 
@@ -333,8 +411,9 @@ static int checkout_paths(const struct checkout_opts *opts,
        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->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");
@@ -342,16 +421,46 @@ static int checkout_paths(const struct checkout_opts *opts,
        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->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->patch_mode)
-               return run_add_interactive(revision, "--patch=checkout",
-                                          &opts->pathspec);
+       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)
@@ -392,8 +501,9 @@ static int checkout_paths(const struct checkout_opts *opts,
                if (ce->ce_flags & CE_MATCHED) {
                        if (!ce_stage(ce))
                                continue;
-                       if (opts->force) {
-                               warning(_("path '%s' is unmerged"), ce->name);
+                       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) {
@@ -409,57 +519,31 @@ static int checkout_paths(const struct checkout_opts *opts,
                return 1;
 
        /* Now we are committed to check them out */
-       state.force = 1;
-       state.refresh_cache = 1;
-       state.istate = &the_index;
+       if (opts->checkout_worktree)
+               errs |= checkout_worktree(opts);
 
-       enable_delayed_checkout(&state);
-       for (pos = 0; pos < active_nr; pos++) {
-               struct cache_entry *ce = active_cache[pos];
-               if (ce->ce_flags & CE_MATCHED) {
-                       if (!ce_stage(ce)) {
-                               errs |= checkout_entry(ce, &state,
-                                                      NULL, &nr_checkouts);
-                               continue;
-                       }
-                       if (opts->writeout_stage)
-                               errs |= checkout_stage(opts->writeout_stage,
-                                                      ce, pos,
-                                                      &state,
-                                                      &nr_checkouts, opts->overlay_mode);
-                       else if (opts->merge)
-                               errs |= checkout_merged(pos, &state,
-                                                       &nr_unmerged);
-                       pos = skip_same_name(ce, pos) - 1;
-               }
-       }
-       remove_marked_cache_entries(&the_index, 1);
-       remove_scheduled_dirs();
-       errs |= finish_delayed_checkout(&state, &nr_checkouts);
+       /*
+        * 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 (opts->count_checkout_paths) {
-               if (nr_unmerged)
-                       fprintf_ln(stderr, Q_("Recreated %d merge conflict",
-                                             "Recreated %d merge conflicts",
-                                             nr_unmerged),
-                                  nr_unmerged);
-               if (opts->source_tree)
-                       fprintf_ln(stderr, Q_("Updated %d path from %s",
-                                             "Updated %d paths from %s",
-                                             nr_checkouts),
-                                  nr_checkouts,
-                                  find_unique_abbrev(&opts->source_tree->object.oid,
-                                                     DEFAULT_ABBREV));
-               else if (!nr_unmerged || nr_checkouts)
-                       fprintf_ln(stderr, Q_("Updated %d path from the index",
-                                             "Updated %d paths from the index",
-                                             nr_checkouts),
-                                  nr_checkouts);
+       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);
        }
 
-       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
-               die(_("unable to write new index file"));
-
        read_ref_full("HEAD", 0, &rev, NULL);
        head = lookup_commit_reference_gently(the_repository, &rev, 1);
 
@@ -553,112 +637,6 @@ static void setup_branch_path(struct branch_info *branch)
        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 @@ static int merge_working_tree(const struct checkout_opts *opts,
 {
        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 @@ static int merge_working_tree(const struct checkout_opts *opts,
                                           &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 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        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 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        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 @@ static int merge_working_tree(const struct checkout_opts *opts,
        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 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                                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 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
        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 @@ static int switch_branches(const struct checkout_opts *opts,
        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 @@ static int switch_branches(const struct checkout_opts *opts,
        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 switch_branches(const struct checkout_opts *opts,
 
 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 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
        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 @@ static int parse_branchname_arg(int argc, const char **argv,
                                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 @@ static int parse_branchname_arg(int argc, const char **argv,
        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 @@ static int parse_branchname_arg(int argc, const char **argv,
                        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 @@ static int parse_branchname_arg(int argc, const char **argv,
        argv++;
        argc--;
 
-       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);
-       }
+       setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);
 
-       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 @@ static int parse_branchname_arg(int argc, const char **argv,
                 */
                if (argc)
                        verify_non_filename(opts->prefix, arg);
-       } else {
+       } else if (opts->accept_pathspec) {
                argcount++;
                argv++;
                argc--;
@@ -1285,6 +1291,60 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
        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 @@ static int checkout_branch(struct checkout_opts *opts,
                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 @@ static int checkout_branch(struct checkout_opts *opts,
        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 @@ static int checkout_branch(struct checkout_opts *opts,
        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 @@ static int checkout_branch(struct checkout_opts *opts,
                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 @@ static int checkout_branch(struct checkout_opts *opts,
        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"),
+               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('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;
+}
+
+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_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-               OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+               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,
+               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,
+               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")),
-               { 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_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(&opts, 0, sizeof(opts));
        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;
+       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;
 
-       argc = parse_options(argc, argv, prefix, options, checkout_usage,
-                            PARSE_OPT_KEEP_DASHDASH);
+       argc = parse_options(argc, argv, prefix, options,
+                            usagestr, parseopt_flags);
 
-       dwim_new_local_branch = !no_dwim_new_local_branch;
-       if (opts.show_progress < 0) {
-               if (opts.quiet)
-                       opts.show_progress = 0;
+       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 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                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 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
         * 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 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                               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;
+}
index 5b9ebe9..a4fe728 100644 (file)
@@ -494,7 +494,7 @@ static enum {
 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 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        transport_disconnect(transport);
 
        if (option_dissociate) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                dissociate_from_references();
        }
 
index 537fdfd..d8efa5b 100644 (file)
@@ -141,6 +141,8 @@ static int graph_write(int argc, const char **argv)
        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 @@ static int graph_write(int argc, const char **argv)
                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 @@ static int graph_write(int argc, const char **argv)
                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)
index 1c9e8e2..3b561c2 100644 (file)
@@ -1078,9 +1078,11 @@ static const char *read_commit_message(const char *name)
 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 @@ static void finalize_deferred_config(struct wt_status *s)
        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 @@ static int git_status_config(const char *k, const char *v, void *cb)
                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 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                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 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        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);
index 1409ced..2001542 100644 (file)
@@ -76,7 +76,7 @@ static int commit_name_neq(const void *unused_cmp_data,
 
 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 @@ static void add_to_known_names(const char *path,
                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;
                }
index c22cef3..f541f55 100644 (file)
@@ -275,7 +275,7 @@ static void export_blob(const struct object_id *oid)
        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 @@ static void show_filemodify(struct diff_queue_struct *q,
                                                  &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));
                        }
index cf7eb0d..53ce99d 100644 (file)
@@ -52,6 +52,7 @@ static int prune_tags = -1; /* unspecified */
 
 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;
@@ -178,6 +179,8 @@ static struct option builtin_fetch_options[] = {
        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()
@@ -250,6 +253,7 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
 struct refname_hash_entry {
        struct hashmap_entry ent; /* must be the first member */
        struct object_id oid;
+       int ignore;
        char refname[FLEX_ARRAY];
 };
 
@@ -298,6 +302,11 @@ static int refname_hash_exists(struct hashmap *map, const char *refname)
        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)
@@ -330,7 +339,7 @@ static void find_non_local_tags(const struct ref *refs,
                            !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;
                }
@@ -344,7 +353,7 @@ static void find_non_local_tags(const struct ref *refs,
                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;
 
@@ -365,7 +374,7 @@ static void find_non_local_tags(const struct ref *refs,
        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,
@@ -373,19 +382,21 @@ static void find_non_local_tags(const struct ref *refs,
         */
        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);
@@ -1456,7 +1467,7 @@ static int fetch_multiple(struct string_list *list)
                        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++) {
@@ -1704,13 +1715,15 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 
        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;
 }
index d26fb0a..18403a9 100644 (file)
@@ -238,7 +238,7 @@ static int mark_used(struct object *obj, int type, void *data, struct fsck_optio
 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 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
        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 int fsck_cache_tree(struct cache_tree *it)
 
 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 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                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))
index 8943bcc..be8e0bf 100644 (file)
@@ -653,7 +653,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        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 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        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; "
index ccf4eb7..0d55f73 100644 (file)
@@ -14,6 +14,7 @@
 #include "thread-utils.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "fetch-object.h"
 
 static const char index_pack_usage[] =
 "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -1351,6 +1352,25 @@ static void fix_unresolved_deltas(struct hashfile *f)
                sorted_by_pos[i] = &ref_deltas[i];
        QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
 
+       if (repository_format_partial_clone) {
+               /*
+                * Prefetch the delta bases.
+                */
+               struct oid_array to_fetch = OID_ARRAY_INIT;
+               for (i = 0; i < nr_ref_deltas; i++) {
+                       struct ref_delta_entry *d = sorted_by_pos[i];
+                       if (!oid_object_info_extended(the_repository, &d->oid,
+                                                     NULL,
+                                                     OBJECT_INFO_FOR_PREFETCH))
+                               continue;
+                       oid_array_append(&to_fetch, &d->oid);
+               }
+               if (to_fetch.nr)
+                       fetch_objects(repository_format_partial_clone,
+                                     to_fetch.oid, to_fetch.nr);
+               oid_array_clear(&to_fetch);
+       }
+
        for (i = 0; i < nr_ref_deltas; i++) {
                struct ref_delta_entry *d = sorted_by_pos[i];
                enum object_type type;
@@ -1650,8 +1670,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        int report_end_of_input = 0;
 
        /*
-        * index-pack never needs to fetch missing objects, since it only
-        * accesses the repo to do hash collision checks
+        * index-pack never needs to fetch missing objects except when
+        * REF_DELTA bases are missing (which are explicitly handled). It only
+        * accesses the repo to do hash collision checks and to check which
+        * REF_DELTA bases need to be fetched.
         */
        fetch_if_missing = 0;
 
index 8ae40de..f101d09 100644 (file)
@@ -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 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       git_config(git_default_config, NULL);
+
        argc = parse_options(argc, argv, prefix, options,
                             git_interpret_trailers_usage, 0);
 
index 7f83c9a..670e8fb 100644 (file)
@@ -373,7 +373,7 @@ static void prune_index(struct index_state *istate,
        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;
index 6e99aea..aad5a95 100644 (file)
@@ -453,7 +453,7 @@ static void finish(struct commit *head_commit,
                         * 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);
                }
        }
index 16df434..c785fe1 100644 (file)
@@ -378,8 +378,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
                        *(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);
                        }
index b2be886..000dc4b 100644 (file)
@@ -606,12 +606,12 @@ static int mark_tagged(const char *path, const struct object_id *oid, int flag,
                       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 @@ static int have_duplicate_entry(const struct object_id *oid,
 {
        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 @@ static int can_reuse_delta(const unsigned char *base_sha1,
        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 @@ static int can_reuse_delta(const unsigned char *base_sha1,
         * 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 @@ static void add_tag_chain(const struct object_id *oid)
         * 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 @@ static int add_ref_tag(const char *path, const struct object_id *oid, int flag,
 
        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 @@ static void show_object(struct object *obj, const char *name, void *data)
                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 @@ static void add_objects_in_unpacked_packs(void)
 
                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 @@ static void loosen_unused_packed_objects(void)
 
                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 @@ static void get_object_list(int ac, const char **av)
                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"));
index 97613ec..2b76872 100644 (file)
@@ -53,7 +53,7 @@ static int is_object_reachable(const struct object_id *oid,
 
        perform_reachability_traversal(revs);
 
-       obj = lookup_object(the_repository, oid->hash);
+       obj = lookup_object(the_repository, oid);
        return obj && (obj->flags & SEEN);
 }
 
index b8116db..3c7d8b8 100644 (file)
@@ -738,20 +738,30 @@ static int finish_rebase(struct rebase_options *opts)
 {
        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)
@@ -1600,7 +1610,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                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 +1630,24 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                               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:
@@ -2141,6 +2159,7 @@ run_rebase:
        ret = !!run_specific_rebase(&options, action);
 
 cleanup:
+       strbuf_release(&buf);
        strbuf_release(&revisions);
        free(options.head_name);
        free(options.gpg_sign_opt);
index 77b7122..610eadf 100644 (file)
@@ -2042,7 +2042,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        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);
index caca113..f834b55 100644 (file)
@@ -422,7 +422,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        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.
index 26ef9a7..c2bb35a 100644 (file)
@@ -421,7 +421,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                        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;
 }
index 660172b..301ccb9 100644 (file)
@@ -49,6 +49,7 @@ static const char rev_list_usage[] =
 "    --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 @@ enum missing_action {
 };
 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 @@ static void show_object(struct object *obj, const char *name, void *cb_data)
        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 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                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);
 
        }
index d4dcedb..4e71b2f 100644 (file)
@@ -203,7 +203,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
        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')
index 2a8e6d0..fde6397 100644 (file)
@@ -713,11 +713,11 @@ static int git_stash_config(const char *var, const char *value, void *cb)
 static int show_stash(int argc, const char **argv, const char *prefix)
 {
        int i;
-       int opts = 0;
        int ret = 0;
        struct stash_info info;
        struct rev_info rev;
        struct argv_array stash_args = ARGV_ARRAY_INIT;
+       struct argv_array revision_args = ARGV_ARRAY_INIT;
        struct option options[] = {
                OPT_END()
        };
@@ -726,11 +726,12 @@ static int show_stash(int argc, const char **argv, const char *prefix)
        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 @@ static int show_stash(int argc, const char **argv, const char *prefix)
         * 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 @@ static int show_stash(int argc, const char **argv, const char *prefix)
                }
        }
 
-       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);
index 13da32d..909e77e 100644 (file)
@@ -540,6 +540,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
                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))
index ef37dcc..e0a4c25 100644 (file)
@@ -33,6 +33,7 @@ static const char * const git_tag_usage[] = {
 
 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 @@ static int git_tag_config(const char *var, const char *value, void *cb)
        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 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        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 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        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);
 
index 8047880..a87a4bf 100644 (file)
@@ -332,7 +332,7 @@ static int resolve_against_held(unsigned nr, const struct object_id *base,
 {
        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);
index b13bfaf..706ffcf 100644 (file)
@@ -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 @@ static int do_invalidate_path(struct cache_tree *it, const char *path)
        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 @@ static int update_one(struct cache_tree *it,
                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 @@ static int update_one(struct cache_tree *it,
 
        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 @@ static void write_one(struct strbuf *buffer, struct cache_tree *it,
        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 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
                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 (file)
--- a/cache.h
+++ b/cache.h
@@ -43,30 +43,6 @@ int git_deflate_end_gently(git_zstream *);
 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 @@ void setup_pager(void);
 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);
index 3a9af10..a9ac72b 100644 (file)
@@ -59,7 +59,7 @@ git-cat-file                            plumbinginterrogators
 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-cvsimport                           foreignscminterface
 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-repack                              ancillarymanipulators           complete
 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-status                              mainporcelain           info
 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
index 7c5e548..8cc1d1d 100644 (file)
@@ -361,10 +361,10 @@ int generation_numbers_enabled(struct repository *r)
        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 @@ struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit
        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 @@ static void write_graph_chunk_fanout(struct hashfile *f,
         * 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_fanout(struct hashfile *f,
 }
 
 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 const unsigned char *commit_to_sha1(size_t index, void *table)
 }
 
 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 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                        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 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                        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_data(struct hashfile *f, int hash_len,
 }
 
 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 @@ static void write_graph_chunk_extra_edges(struct hashfile *f,
                /* 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 @@ static int commit_compare(const void *_a, const void *_b)
        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 @@ static void compute_generation_numbers(struct packed_commit_list* commits,
                        }
                }
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 }
 
 static int add_ref_to_list(const char *refname,
@@ -858,207 +861,187 @@ static int add_ref_to_list(const char *refname,
        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;
-
-       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;
+       struct strbuf packname = STRBUF_INIT;
+       int dirlen;
 
-       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 (open_pack_index(p)) {
+                       error(_("error opening index for %s"), packname.buf);
+                       return -1;
                }
+               for_each_object_in_pack(p, add_packed_commits, ctx,
+                                       FOR_EACH_OBJECT_PACK_ORDER);
+               close_pack(p);
+               free(p);
        }
 
-       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;
-               }
-               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);
+       stop_progress(&ctx->progress);
+       strbuf_reset(&progress_title);
+       strbuf_release(&packname);
+
+       return 0;
+}
+
+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 (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;
+
+               display_progress(ctx->progress, i + 1);
+               if (commit_hex->items[i].string &&
+                   parse_oid_hex(commit_hex->items[i].string, &oid, &end))
+                       continue;
 
-       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;
-
-                       display_progress(progress, i + 1);
-                       if (commit_hex->items[i].string &&
-                           parse_oid_hex(commit_hex->items[i].string, &oid, &end))
-                               continue;
-
-                       result = lookup_commit_reference_gently(the_repository, &oid, 1);
-
-                       if (result) {
-                               ALLOC_GROW(oids.list, oids.nr + 1, oids.alloc);
-                               oidcpy(&oids.list[oids.nr], &(result->object.oid));
-                               oids.nr++;
-                       }
+               result = lookup_commit_reference_gently(ctx->r, &oid, 1);
+
+               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(&progress);
-               strbuf_reset(&progress_title);
        }
+       stop_progress(&ctx->progress);
+      &nb