New in Git: switch and restore

To my surprise, I recently found out about 2 new additions to the list of high-level commands: git restore and git switch

New in Git: switch and restore

"When I see a door with a push sign, I pull first to avoid conflicts" - anonymous

For those that work with git for some time, it is not often that you get to discover new things about it. That is if you exclude the plumbing commands which probably most of us don't know by heart and most likely that's for the better. To my surprise, I recently found out about 2 new additions to the list of high-level commands:

  • git restore
  • git switch

To understand why they came to be, let's first visit our old friend git checkout.

Checkout this

git checkout is one of the many reasons why newcomers find git confusing. And that is because its effect is context-dependent.
The way most people use it is to switch the active branch in their local repo. More exactly, to switch the branch to which HEAD points. For example, you can switch to the develop branch if you are on the main branch:

git checkout develop

You can also make your HEAD pointer reference a specific commit instead of a branch(reaching the so-called detached HEAD state):

git checkout f8c540805b7e16753c65619ca3d7514178353f39

Where things get tricky is that if you provide a file as an argument instead of a branch or commit, it will discard your local changes to that file and restore it to the branch state.
For example, if you checked out the develop branch and you made some changes to the test.txt file, then you can restore the file as it is in the latest commit of your branch with:

git checkout -- test.txt

A method to the madness

If you first look at these two behaviors you might think that it doesn't make any sense, why have one command do 2 different actions? Well, things are a little more subtle than that. If we look at the git documentation, we can see that the command has an extra argument that is usually omitted:

git checkout <tree-ish> -- <pathspec>

What is <tree-ish> ? It can mean a lot of different things, but most commonly it means a commit hash or a branch name. By default that is taken to be the current branch, but it can be any other branch or commit. So for example if you are in the develop branch and want to change the test.txt file to be the version from the main branch, you can do it like this:

git checkout main -- test.txt

With this in mind, maybe things start to make sense. When you provide just a branch or commit as an argument for git checkout, then it will change all your files to their state in the corresponding revision, but if you also specify a filename, it will only change the state of that file to match the specified revision.

New kids on the block

So even if things may start to make sense after reading the previous paragraphs, we must admit that it is still confusing especially for newcomers. That's why in version 2.23 of git, two new commands have been introduced to replace the old git checkout(it is still available, but people new to git should start with these ones preferably). As you would expect, they basically each implement one of the two behaviors described previously, splitting git checkout in two.

Switch

This one implements the behavior of git checkout when running it only against a branch name. So you can use it to switch between branches or commits.

git switch develop

While with git checkout you can switch to a commit and transition into a detached HEAD state, by default git switch does not allow that. You need to provide the -d flag:

git switch -d f8c540805b7e16753c65619ca3d7514178353f39

Another difference is that with git checkout you can create and switch to the new branch in one command using the -b flag:
git checkout -b new_branch

You can do the same with the new one, but the flag is -c:
git switch -c new_branch

Restore

This one implements the behavior of git checkout when running it against a file. You can restore, as the name suggests, the state of a file to a specified git revision(the current branch by default)

git restore -- test.txt

Conclusion

These methods are still marked experimental, but for all intents and purposes they are here to stay so by all means I encourage everyone to start using them since they will probably make a lot more sense in your head and also it will make git just a little bit less confusing to new users.

More details about the two commands can be found in their git documentation: