Skip to content
LogluneStart free

git checkout: Switch Branches and Restore Files

By Loglune

Checkout moves HEAD to a branch/commit and updates working files, or restores a selected file from another revision.
What each checkout points at
git checkout <branch>
HEAD follows the branch; new commits go on that branch
git checkout <commit>
HEAD points at one past commit (detached HEAD); no branch moves
Loglune X-Loglune-Version
One request evaluates a past flag version; the current version keeps serving

git checkout is the Git command that moves HEAD to a branch or a commit and updates the files in your working directory to match it, or replaces individual files with their version from another commit. Use git checkout <branch> to switch branches, git checkout -b <new-branch> to create a branch and switch to it, git checkout <commit> to look at an old commit in detached HEAD state, and git checkout <commit> -- <file> to bring back one file.

This guide is for developers who need one of those jobs done and want the exact command, including what to do when checkout refuses because of uncommitted changes. Since Git 2.23.0, git switch and git restore split these jobs into two commands, and git checkout still does all of them in Git 2.56.0, so the guide also compares git checkout with git switch, git restore, and git pull. The last section applies the idea of checking out a past commit to feature flags with Loglune, using output recorded on October 6, 2026.

Reproduce a customer’s flag state

Start free

What git checkout does

git checkout has 2 main modes: switching branches with git checkout <branch>, and restoring a different version of a file with git checkout <commit> <filename> or git checkout <filename>.

Command What changes Typical use
git checkout <branch> HEAD points at the branch; working tree and index match it Switch to another branch
git checkout -b <new-branch> [<start-point>] Creates the branch, then switches to it Start a feature or a fix
git checkout <remote-branch-name> Creates a local branch that tracks the remote branch Work on a branch a teammate pushed
git checkout <commit> HEAD points at that commit, not at a branch Inspect or test an old commit
git checkout <commit> -- <file> That file in the working tree and index Bring back one file from another commit
git checkout -- <file> That file, from the index Discard unstaged edits to one file

The command operates on 3 entities: files, commits, and branches. When you name a branch, git checkout also tells Git to record new commits on that branch. When you name a commit or a file, no branch moves.

How to check out a branch in Git

To switch to a branch that already exists, list the branches and check out the one you want:

git branch
git checkout feature-search

git branch marks the current branch with *. Running git status first shows whether you have uncommitted changes, which can block the switch. The section on local changes below covers that case.

To go back to the branch you were on before, use -. - is synonymous to @{-1}, the last branch you checked out:

git checkout main
git checkout -

Checking out main works the same way as any other branch. If your local main is behind the remote, check it out and then update it with git pull.

How to create a new branch and check it out

git checkout -b creates a branch and switches to it in one command. Without a start point, the new branch starts at the current commit:

git checkout -b fix-login

Pass a start point to base the branch on another branch or commit instead of HEAD:

git checkout -b fix-login main
git checkout -b hotfix-2026-10 v2.4.0

The two-command form does the same thing: git branch fix-login creates the branch, and git checkout fix-login switches to it. Use git branch alone when you want the branch to exist without leaving your current one.

git checkout -B <branch> [<start-point>] works like -b, except that if the branch already exists, it resets the branch to the start point instead of failing. That moves an existing branch, so check that no one else’s work depends on it.

How to check out a remote branch

A branch that exists only on the remote has to be fetched before you can check it out. “git checkout remote branch” is not an actual command; you run git fetch and then git checkout.

git fetch origin
git branch -r
git checkout feature-billing

If no local branch is named feature-billing and exactly one remote has a branch with that name, Git treats the command as git checkout -b feature-billing --track origin/feature-billing. The new local branch tracks the remote branch, so git pull and git push work without naming the remote. git branch -vv shows which remote branch each local branch tracks.

To give the local branch a different name, create it explicitly:

git checkout -b billing-review origin/feature-billing

When two remotes have a branch with the same name, Git cannot guess, and you need this explicit form.

How to check out a file from another branch or commit

To replace one file with its version from another branch or commit, name the commit, then --, then the path:

git checkout main -- src/config.ts
git checkout HEAD~2 -- Makefile

This form replaces the files and adds them to the index, so the change is already staged. Commit it like any other change. Your branch does not move.

Without a commit, git checkout -- <file> copies the file from the index and discards your unstaged edits to it. Those edits are not in any commit, so Git cannot bring them back.

What does the – mean in git checkout?

The -- tells Git that everything after it is a path, not a branch or a commit. When you run git checkout <something>, Git guesses whether <something> is a branch, a commit, or a file, and treats it as a branch or commit when the name is ambiguous. If a branch and a file share the name hello.c, git checkout -- hello.c restores the file, and git checkout hello.c switches to the branch.

How to check out a past commit (detached HEAD)

To look at the code as it was at an old commit, find the commit hash with git log and check it out:

git log --oneline
git checkout a1b2c3d

You can also name the commit by a Git tag or a relative name, such as git checkout v2.0 or git checkout main~3. Git updates the working tree to that commit, and HEAD becomes “detached”.

What detached HEAD means

HEAD usually refers to a branch, and the branch refers to a commit. After git checkout <commit>, HEAD refers directly to the commit instead of to a named branch. No branch moved, so main and every other branch stay where they were.

In detached HEAD state you can build, run tests, and even commit. A commit you make here is referenced only by HEAD. The rule is that your development should take place on a branch, and if you are only looking at an old commit, detached HEAD does not matter.

Keep work you made in detached HEAD

If you made commits you want to keep, create a branch before you leave:

git checkout -b experiment

git branch experiment also creates a branch at that commit but leaves HEAD detached, and git tag experiment creates a tag there. If you already switched away, find the commit with git reflog first, then create a branch at it. Without a branch or tag, Git garbage collection eventually deletes those commits.

Return to a branch

To leave detached HEAD, check out a branch:

git checkout main

Checking out an old commit is also how git bisect works: it checks out commits between a good one and a bad one until it finds the first bad commit. To move a branch back to an old commit instead of just looking at it, use git reset, and to undo a commit that is already pushed, use git revert.

When git checkout refuses: uncommitted changes

git checkout keeps your uncommitted changes when you switch, unless a file you changed differs between the current commit and the target branch. Then the switch stops with an error:

error: You have local changes to 'frotz'; not switching branches.

You have 3 ways forward.

  1. Commit the changes on the current branch, then switch.
  2. Stash them with git stash, switch, and run git stash pop where you want them back.
  3. Carry them over with git checkout -m <branch>. -m stashes the local changes, switches, and reapplies them. If they conflict, the stash entry is kept so you can resolve the conflicts or apply it later.

git checkout -f <branch> also switches, but it throws away your local changes, so use it only when you do not need them.

git checkout vs git switch and git restore

Git 2.23.0, released August 16, 2019, added git switch for branches and git restore for files. Both pages on git-scm.com list 2.23.0 as their first version. The split removes the guessing between branch names and file names. git checkout is not obsolete: it is still part of Git, its reference page was last updated in Git 2.55.0, and you can use either set of commands.

Task git checkout git switch / git restore
Switch to a branch git checkout main git switch main
Create and switch git checkout -b fix-login git switch -c fix-login
Check out a past commit git checkout a1b2c3d git switch --detach a1b2c3d
Go to the previous branch git checkout - git switch -
Discard edits to a file git checkout -- app.ts git restore app.ts
Take a file from another commit git checkout main -- app.ts git restore --source main app.ts

Two behaviors differ. git switch refuses a commit unless you pass --detach, so you cannot enter detached HEAD by accident. git restore --source writes the working tree only by default, while git checkout <commit> -- <file> also stages the file; add --staged --worktree to git restore to do both.

git checkout vs git pull

git checkout works with what is already in your local repository. git pull talks to the remote. git pull first runs git fetch and then integrates the fetched branch into the current branch.

git checkout git pull
Contacts the remote No Yes, through git fetch
Changes which branch you are on Yes No
Adds commits to your branch No Yes, from the upstream branch

To start working on a teammate’s branch, you often run both: git fetch, git checkout <branch>, and later git pull on that branch to get new commits.

Checking out a past version of your feature flags

git checkout of a past commit answers “what did the code look like at that commit” without moving any branch. During an incident, the same question often comes up about configuration: what did a feature flag return in an earlier version, while production keeps running the current one. git checkout cannot answer that for feature flags that live outside the repository.

Loglune keeps every published set of flags as an immutable version with a number that increases with each publish. The evaluation endpoint POST /api/flags evaluates the current published version when the request has no X-Loglune-Version header, and evaluates version N when the request carries X-Loglune-Version: N. Both modes use the same evaluation engine, so only the version differs.

Git Loglune
git checkout <branch>: HEAD follows the branch tip A request without X-Loglune-Version evaluates the current published version
git checkout <commit>: HEAD points at one past commit, no branch moves A request with X-Loglune-Version: N evaluates version N, and the current version keeps serving
git checkout -b keep <commit>: keep the old state as a new branch Rolling back to version N publishes a new version with the same contents as N

What the output looks like

We ran the shipped POST /api/flags route on a local test database on October 6, 2026, with an editorial example flag. Version 1 had checkout-v2 with default off, and version 2 switched the default to on. Each request sent the same body:

{
  "context": {
    "key": "user-42",
    "attributes": { "plan": "pro" },
    "evaluatedAt": "2026-08-12T09:00:00.000Z"
  },
  "flag": "checkout-v2"
}

The workspace on a paid plan answered as follows, in the order we sent the requests.

Request Status record.version variationId value
No X-Loglune-Version 200 2 on true
X-Loglune-Version: 1 200 1 off false
X-Loglune-Version: 2 200 2 on true
No X-Loglune-Version, again 200 2 on true
X-Loglune-Version: 3 404 none none none

The pinned request for version 1 returned the old value, and the next request without the header still returned version 2, the way main stays put after you check out an old commit. Version 3 was never published, and the 404 body was:

{
  "error": "not_delivered",
  "hint": "That version is not stored (never published, or older than the plan keeps). Pin another version."
}

A second workspace on the Free plan ran the same two kinds of request. Without the header it returned 403 with "error": "production_eval_unavailable", because the Free plan does not evaluate the current version in production. With X-Loglune-Version: 1 it returned 200 with "variationId": "off" and "version": 1 in the record, because evaluating a pinned version works on every plan.

Each response carries a record with the version, an opaque context key, the results, the evaluation time, and the names of the attributes sent, not their values. The record shows which version gave the answer, the way git log -1 shows which commit you have checked out.

When you know the time of a bug rather than the version number, Loglune can reproduce the flag state at a time T: it rebuilds the flag state at T into a version for reproduction and evaluates it for the user you name, and that reproduction counts as 1 toward the plan’s reproductions. Evaluating a pinned version through POST /api/flags does not count.

Choosing the checkout

Use git checkout <branch> or git switch <branch> to move between branches, git checkout -b to start a new one, git fetch followed by git checkout for a remote branch, git checkout <commit> -- <file> for one file, and git checkout <commit> to look at an old commit without moving any branch. When the old state you need is a feature flag version rather than a commit, Loglune evaluates any published version N for a request with X-Loglune-Version: N while production keeps serving the current version.

Check out a past flag version like git checkout, without moving production

Loglune evaluates published version N of your flags when the request to POST /api/flags carries the header X-Loglune-Version N, and a request without the header keeps evaluating the current published version. Evaluating a pinned version works on every plan and does not count toward billing. The Free plan costs $0 for 1 seat and keeps 7 days of history; evaluating the current version in production needs a paid plan.