Programming and IT

git checkout

Historically `git checkout` does several unrelated things at once: it moves your working copy to another branch, creates a branch and pulls a single file out of a commit. Because it was so overloaded, Git 2.23 added two separate commands — `switch` and `restore`. Here are both sides: the old way and its modern replacement.

Updated
In this article

Three jobs in one command#

The command changes what your working copy is looking at. What happens next depends on what you pass to it.

Pass a branch name, and HEAD moves to that branch while the files in your project folder are rewritten to match its latest commit. Pass a commit hash, and the files are rewritten to match that commit, but there is no branch pointer there, so you end up in a detached state. Pass a file path after two dashes, and the branch does not change at all — instead that file is overwritten with the version from the given commit, and any unsaved edits in it are gone without a warning.

That last case is the only truly dangerous use. Git cannot recover changes that were never committed or staged: they are not stored anywhere. So before rolling a file back, either commit or set your edits aside with git stash.

Syntax#

git checkout main              # go to an existing branch
git checkout -b feature-auth   # create a branch here and go to it
git checkout -b fix origin/main    # create a branch from another tip
git checkout 9c1f8ab           # look at a commit (HEAD becomes detached)
git checkout -- config.yml     # reset the file to HEAD, edits are wiped
git checkout 9c1f8ab -- app/db.py  # take one file from an old commit
git checkout -                 # go back to the previous branch

Since Git 2.23 two narrow commands do the same jobs:

git switch main                # go to a branch
git switch -c feature-auth     # create and go
git switch -                   # back to the previous branch
git restore config.yml         # reset the file to HEAD
git restore --source=9c1f8ab -- app/db.py   # a file from a specific commit
git restore --staged report.md # unstage the file, keep the edits

The split is useful precisely because switch physically cannot touch files and restore cannot change branches. Wiping out your work with one typo becomes much harder.

Example: switching with uncommitted edits#

$ git switch -c feature-search
Switched to a new branch 'feature-search'
$ git status -sb
## feature-search
 M app/views.py

Your edit in app/views.py came with you: switching carries uncommitted changes over to the new branch as long as they do not get in the way. They get in the way when the target branch changes the same file. Then Git refuses:

error: Your local changes to the following files would be overwritten by checkout:
        app/views.py
Please commit your changes or stash them before you switch branches.
Aborting.

This is not a failure but a safeguard: Git did not quietly throw your work away. You have three options — commit, stash with git stash push, or switch with the edits: git switch -m other-branch tries to carry them over with a merge.

Example: pulling a file out of the past#

You need an old version of a config file, but the history must stay untouched:

$ git log --oneline -3 -- deploy/nginx.conf
5d0aa71 new port
c17b3e2 disable gzip
8a94f0c first working config
$ git checkout 8a94f0c -- deploy/nginx.conf
$ git status -s
M  deploy/nginx.conf

The file from commit 8a94f0c is now both in your working copy and in the index (the M in the first column). The branch did not change, other files were not touched. From here it is an ordinary edit: commit it or undo it.

Detached HEAD: what it is and how to get out#

Checking out a commit hash prints a fifteen-line warning whose point is in the first lines:

Note: switching to '9c1f8ab'.
You are in 'detached HEAD' state.

You are looking at a specific commit, and no branch points here. You can look around and run the code freely. There is one danger: commits made in this state are not attached to anything. Once you switch to a branch, the only way back to them is git reflog.

There are two ways out. If you did not commit anything, just go back: git switch -. If you did commit and want to keep the result, pin it with a branch right where you are:

$ git switch -c experiment
Switched to a new branch 'experiment'

Now the commits belong to experiment and will not get lost. More on branches in the article on git branch.

Common mistakes#

Forgetting the two dashes. If a branch called config exists, git checkout config switches to that branch instead of restoring the file. The two dashes tell Git: what follows are paths, not refs. git restore does not have this ambiguity.

Expecting to undo a file restore. You cannot. git checkout -- file and git restore file destroy unsaved edits for good — unlike operations on commits, which the reflog can almost always bring back. The difference between "losing a commit" and "losing unsaved work" is covered in detail in the article on git reset.

The branch is not found. error: pathspec 'feature-x' did not match any file(s) known to git usually means the branch exists only on the server. Run git fetch origin, and then git switch feature-x will automatically create a local branch that tracks the remote one.

Step-by-step plan

  1. See where you aregit status -sb: the current branch and uncommitted files.
  2. Decide what to do with your editsCommit them, stash them, or make sure they will not block the switch.
  3. Switchgit switch <branch> or git checkout <branch>; for a new branch add -c or -b.
  4. Check that you switchedgit branch --show-current should print the name you expect.
  5. Restore a file if neededgit restore <file> or git checkout <commit> -- <file>; remember that unsaved edits in that file are lost.

Start learning this in your own space

The plan goes into your repository: tick off stages, keep notes — the change history shows how far you have come.

Start the plan

Check yourself

1.Which command creates a branch called hotfix and switches to it at once?

2.You edited notes.md and ran git restore notes.md. What happened to your edits?

3.You are on commit 9c1f8ab in detached HEAD and made two commits. How do you keep them?

Sources

Was this helpful?

More articles

Programming and IT git merge Merging brings work from one branch into another. git merge takes the branch you name and joins its history with the current one — sometimes by just moving a pointer, sometimes by creating a separate commit, and sometimes by stopping and asking for help. Programming and IT Git branches A Git branch is a movable pointer to a commit, not a copy of your files. That is why creating one costs nothing, and the git branch command is what you use to list, create, rename and delete branches. Programming and IT git pull git pull brings other people's commits from a remote repository and immediately merges them into your current branch. It is two operations in a row, and almost every problem with pull comes from the second one — how it joins the two histories. Programming and IT How to resolve a merge conflict A conflict is not a breakage, it is a question. Git merges changes automatically as long as they are in different parts of a file; when two branches edit the same lines, only a person can choose the right version. Your job is to read both versions, build working code out of them and tell Git the question is settled. Programming and IT git reset git reset moves the current branch pointer to another commit. How much your work suffers depends on one flag — `--soft` touches nothing but history, `--mixed` clears the index, `--hard` rewrites the files on disk. The third mode is the one Git command that destroys unsaved work for good. Programming and IT Python: reading a file Working with a file takes three steps: open it, read or write, close it. You are better off not closing it by hand — that is what the `with` statement is for. And the parameter people forget most is the encoding: without it, the same code reads a file differently on different machines.

More solutions