Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXVI · Restore and SwitchRestoreSwitch

Switch versus checkout — when each is appropriate, behaviour differences, and the migration story

Intermediate⏱ ~20 mingit

What you'll learn

  • Identify behaviour differences between git switch and git checkout that go beyond a rename
  • Recognise the cases where git checkout is still the right command
  • Choose between git switch and git checkout for a given team context
  • Describe the migration path from checkout to switch and restore
  • Recognise the advisory hints git emits when an old spelling is used

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

The 2.23 split is presented as git switch replacing the branch side of git checkout, but the replacement is not a pure rename. There are behaviour differences that matter for scripts, CI workflows, and onboarding documentation. This lesson maps the differences side by side and explains when each command is the right choice.

The advertised equivalence

For most operations, git switch and git checkout produce identical three-tree states:

# Switch to an existing branch
git switch main
git checkout main

# Create a branch and switch to it
git switch -c feature/x
git checkout -b feature/x

# Detach at a commit
git switch --detach 8a3f9d2
git checkout 8a3f9d2

# Toggle to the previous branch
git switch -
git checkout -

These pairs are exact synonyms. The new command produces the same tree states, the same reflog entries, and the same exit codes as the old command.

flowchart LR
    A["git switch <branch>"] --> X["identical"]
    B["git checkout <branch>"] --> X
    C["git switch -c feature/x"] --> Y["identical"]
    D["git checkout -b feature/x"] --> Y
    E["git switch --detach 8a3f9d2"] --> Z["identical"]
    F["git checkout 8a3f9d2"] --> Z

Behaviour differences that go beyond the rename

Three differences are worth knowing for CI scripts and onboarding documentation:

1. The orphan-branch syntax

git switch --orphan <name> is the canonical way to start a branchless history. The legacy equivalent is git checkout --orphan <name>, which still works. The two commands produce the same tree state; the difference is only in the spelling.

2. The disjoint-checks flag

git switch --disjoint-checks tightens the dirty-tree check to also refuse the switch when an untracked file would be overwritten. git checkout does not have an equivalent flag; the only way to get the strict behaviour from git checkout is to use the --no-overlay or --force flags, neither of which means the same thing.

3. The advisory hints

Starting with Git 2.24, git checkout emits a one-line hint when the new spelling would be more appropriate:

git checkout main
# Switched to branch 'main'
# hint: using 'git switch' to switch branches is more intuitive
# hint: 'git switch main' would do the same thing

The hint is informational only - the command succeeds and the tree state is correct. CI scripts that match on the exact stdout of git checkout may break on the hint lines and should adjust their output handling to ignore lines that begin with hint:.

When checkout is still the right command

git checkout is still the right command in three cases:

1. Legacy scripts that work

A CI pipeline that has run git checkout main in production for five years is doing the right thing. Rewriting it to git switch main is a no-op change that costs reviewer time. The script should be modernised only when it is being modified for another reason.

2. Symmetric read of a file from a ref

git checkout <ref> -- <path> is the legacy spelling for pulling a file from a named tree into the working tree. The new spelling is git restore --source=<ref> <path>. The legacy spelling still works and is one flag shorter, which some engineers find ergonomic. The new spelling is the recommended path.

3. Detached HEAD at a tag with no flag

git checkout v3.2.7 detaches HEAD at the tag without requiring --detach. git switch v3.2.7 would attempt to switch to a branch named v3.2.7 and fail with an error message; the new spelling requires the explicit --detach. The legacy spelling is shorter for this case.

flowchart TD
    A["use case"] --> C{"command shape"}
    C -- "branch switching, new script" --> D["git switch\n(recommended)"]
    C -- "branch switching, old script" --> E["git checkout\n(no rewrite for its own sake)"]
    C -- "file from ref" --> F["git restore --source\n(recommended)"]
    C -- "file from ref, old script" --> G["git checkout --\n(still works)"]
    C -- "detach at tag with no flag" --> H["git checkout <tag>\n(shorter)"]
    C -- "detach at commit, new code" --> I["git switch --detach\n(explicit)"]

The migration story

The migration from checkout to switch and restore is not a forced rewrite. The official documentation describes the new commands as “experimental alternatives to clarify the diverging use-cases”, which is the language of a recommendation, not a deprecation. The migration story has three parts:

1. New work uses the new spellings

Code committed after the team’s adoption date uses git switch for branch operations and git restore for file operations. The transition is enforced by code review, not by tooling - the team agrees on the rule and applies it.

2. Old scripts are modernised when touched

A CI script that is being modified for another reason (unrelated bug fix, dependency upgrade, behaviour change) has its git checkout lines reviewed during the change. If they can be replaced with the new spellings without behaviour change, they are replaced. The review checklist item is “while you are here, are any of the git checkout lines now better expressed as git switch or git restore?”

3. Legacy scripts that work stay

A CI script that has not been touched in three years and still works is not modified. The cost of rewriting a working script exceeds the cost of leaving it alone. The team accepts that some git checkout lines will exist in the codebase for years.

flowchart LR
    A["new code"] -->|adoption| B["git switch / git restore"]
    C["modified code"] -->|"while you are here"| D["review for switch / restore"]
    E["stable code"] -->|"do not touch"| F["git checkout remains"]
    B --> G["codebase mixes old and new"]
    D --> G
    F --> G
    G -->|"as old code is touched"| B

Comparing the legacy and new spellings

The full mapping from the legacy git checkout spellings to the new commands:

LegacyNewBehaviour diff?
git checkout <branch>git switch <branch>None
git checkout -b <name>git switch -c <name>None (default to HEAD; legacy defaults to current HEAD)
git checkout -B <name> [<start>]git switch -C <name> [<start>]None
git checkout <commit>git switch --detach <commit>None
git checkout <tag>git switch --detach <tag>None (but legacy is shorter)
git checkout -git switch -None
git checkout --orphan <name>git switch --orphan <name>None
git checkout -- <path>git restore <path>None
git checkout HEAD -- <path>git restore --staged --worktree <path>None
git checkout <ref> -- <path>git restore --source=<ref> <path>None
git checkout --overlay <ref> -- <path>(no direct equivalent)Yes: --overlay keeps untracked files at the path

The last row is the rare case where the legacy command has a flag with no new equivalent. --overlay controls how untracked files at a target path interact with the restore; the default is to leave them alone, and --no-overlay deletes them. The new git restore does not have an explicit overlay flag - the behaviour matches the legacy default of “leave untracked files alone”.

Production discipline

  1. Adopt the new spellings in new code. Review checklists should explicitly mention git switch and git restore for the corresponding operations.
  2. Modernise git checkout when the surrounding code is being changed for another reason. A pure-rename commit is a low-risk cleanup; a flag-change commit needs explicit justification.
  3. Test CI scripts against Git 2.24+ output. The advisory hints change the stdout of git checkout but not the exit code.
  4. Document the team’s choice. A line in CONTRIBUTING that says “new code uses git switch and git restore; legacy code is modernised when touched” sets the expectation for new contributors.

Cross-course references

  • Ansible for Production Sysadmins - Part XXXVII (RepoArch) documents the migration rule in its CONTRIBUTING file, with the explicit phrasing above.
  • GitOps with Argo CD - Part II (Repos) uses git switch in the controller’s workspace management and parses stdout by exit code only, so the advisory hints do not affect reconciliation.
  • Terraform for Production Sysadmins - Part XV (CIHooks) tests its hook scripts against the hints and asserts the exit code rather than the stdout shape.

Quiz

Knowledge check · 4 questions

  1. Q1. Which scenario is a case where `git checkout` is still the right command over `git switch`?

  2. Q2. Starting with Git 2.24, `git checkout` emits advisory `hint:` lines recommending `git switch` or `git restore`, but the hints do not affect the exit code of the command.

  3. Q3. What is the official migration rule from `git checkout` to `git switch` and `git restore`, and why is the rule not 'rewrite all `git checkout` lines'?

  4. Q4. Audit a CI pipeline that uses `git checkout` heavily, classify each line as 'modernise now', 'modernise when modified', or 'keep as-is', and recommend the migration plan.

    A team's Terraform CI pipeline contains six `git checkout` invocations: (1) `git checkout main` at the start; (2) `git checkout -b ci/$BUILD_ID` after fetching; (3) `git checkout terraform/.terraform.lock.hcl` to discard the lock file before commit; (4) `git checkout -- terraform/.terraform.lock.hcl` in a post-build cleanup; (5) `git checkout HEAD -- terraform/main.tf` to reset after a failed test; (6) `git checkout origin/main -- README.md` to refresh a generated README before commit. The team is starting a one-quarter migration to the new spellings.

Passing score: 75%. Answers are checked in this browser.