Git 2.23 introduces two new commands meant to replace two common uses of git checkout
: git switch
to switch to a new branch after creating it if necessary, and git restore
to restore changes from a given commit.
As mentioned, both functions implemented through git switch
and git restore
were previously available using git checkout
, which was the source of some confusion for new Git users and considered detrimental to Git user experience. The main argument for this was that operating on branches is logically different from modifying your working copy, so having a single command for both operations was not a good idea. Interestingly, the git checkout
command was not born overloaded with such heterogeneous functions. Indeed, originally git checkout
could only switch to a branch, optionally creating it if not present when the -b
switch was given. Instead, to copy a file from the index to your working copy, you would use git checkout-index
. At some point though, git checkout
was extended to support both copying a file from the stage/index to the working tree as well as copying a tree-ish. Differentiating the three semantics was –and still is– a matter of juggling with the --
separator and keeping in mind that some argument combinations may alter your index, not only your working copy. For example:
git checkout b # switch to branch b
git checkout -- f # copy f from the stage to the working tree
git checkout b -- g # copy f from branch b
git checkout HEAD f # this will alter your index, too!
The dynamics that led to this kind of design decisions were explained by Git maintainer Junio Hamano:
Another thing is because the system wasn’t really designed, but grew organically. So somebody came up with an idea of doing one thing. "Oh, this is a good idea, a good feature; let’s add it to this command as this option name." And the option name he chooses just gets stuck, but after a few months, somebody else notices, "Oh, this is a similar mode of operation with that existing command."
Thus, to improve this state of things, git switch
and git restore
were born. Using switch
you will be able to write:
git switch my-branch # same as git checkout my-branch
git switch -c my-branch # same as git checkout -b my-branch
git switch -c my-branch HEAD~3 # branch off HEAD~3
git switch --detach HEAD~3 # checkout commit without branching
The git restore
command supports three main options: --source
to specify what you want to copy, and --staged
and --worktree
to specify the destination. This removes all kinds of ambiguity, at the expense of terseness, and allows you to write stuff like:
git restore --source HEAD~3 main.c # --worktree is implied
rm -f hello.c
git restore hello.c
git restore '*.c'
git restore --staged hello.c # same as using git reset
git restore --source=HEAD --staged --worktree hello.c # same as git checkout
A note of caution about the restored source, in case it is not explicitly specified using --source
, since it depends on the destination to restore to:
If not specified, the default restore source for the working tree is the index, and the default restore source for the index is HEAD. When both
--staged
and--worktree
are specified,--source
must also be specified.
Developers' reactions on Hacker News and Reddit have been generally positive, praising the greater intuitiveness of switch
and restore
over the multiple possible uses of checkout
. Anyway, a few developers hinted at restore
having a clumsy and unusual syntax. Specifically, besides the already mentioned glitch with default source resolution, restore
requires three arguments –--source
, --staged
, and --worktree
– for what is likely the most common use case, i.e., reverting a file to its state in a previous commit. Additionally, restore
requires explicitly naming the source revision, while many commands allow you to just give it as an argument.
Switch and restore are not the only new features in Git 2.23, which also modifies the behaviour of many existing commands, including git for-each-ref
, git merge
, git cherry-pick
, while also fixing bugs. Refer to the release notes or to this GitHub summary for full detail.