Git, CI/CD & GitOpsVIII · BranchingBranching
Branch creation and switching — git switch, git checkout, and the three-tree update
What you'll learn
- Create a branch with git switch -c or git checkout -b and explain the two-phase operation
- Describe what git switch does to HEAD, the index, and the working tree
- Recognise why a clean working tree is required for a safe branch switch and what git status reports otherwise
- Use git switch - to toggle between the previous branch and the current one
- Distinguish a branch switch from a detached-HEAD checkout with git switch --detach
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
Creating a branch and switching to it are two operations that
should feel like one. They are not. The branch is a ref write — a
single file under .git/refs/heads/ is created or updated. The
switch is a three-tree update — HEAD moves, the index is rewritten
from the new branch’s tree, and the working tree files are updated
to match. The distinction is not academic. The first operation is
free; the second operation can refuse to run if your working tree
is dirty. A production engineer who has never been bitten by the
difference has not yet worked on a long-running repository.
The two-phase operation
The most common incantation for starting a new line of work is
git switch -c <name>. This is one command in the shell but two
operations in Git:
- Create the branch ref under
refs/heads/<name>, pointing at the current HEAD. - Move HEAD to point at the new branch, rewrite the index from the new branch’s tree object, and update the working tree.
The -c flag is short for --create. Without it, git switch <name> performs only phase 2 — the branch must already exist.
# Phase 1 + Phase 2: create and switch in one command
git switch -c feature/iam-rotation
# Equivalent legacy spelling (still common in older scripts)
git checkout -b feature/iam-rotation
# Phase 2 only: switch to an existing branch
git switch main
# Equivalent legacy spelling
git checkout main
The two spellings are functionally identical. git switch was
introduced in Git 2.23 specifically to disambiguate “switch to a
branch” from “restore a file from the index”, which git checkout had overloaded into one command. New work should use
git switch; older scripts and muscle memory will keep git checkout alive for years.
sequenceDiagram
participant Dev as Engineer
participant Refs as refs/heads/
participant HEAD as HEAD
participant IDX as Index
participant WT as Working tree
Dev->>Refs: git switch -c feature/x
Note over Refs: write file with current OID
Refs->>HEAD: HEAD -> refs/heads/feature/x
HEAD->>IDX: rewrite index from feature/x tree
IDX->>WT: update working tree to match
flowchart LR
A["HEAD\n(current branch)"] --> B["refs/heads/feature/x\n(new branch ref)"]
B --> C[".git/refs/heads/feature/x\n(40-char SHA)"]
C --> D["working tree\n(feature/x files)"]
What happens to the three trees
When git switch (or git checkout) runs successfully, three
things happen in order:
- HEAD is moved. HEAD is a file under
.git/whose contents are the textual name of the current branch (ref: refs/heads/mainfollowed by a newline). HEAD is rewritten to point at the new branch. The branch ref itself is unchanged — you have not committed anything, so the tip stays where it was. - The index is rewritten. The binary manifest at
.git/indexis replaced with a fresh serialisation of the tree object that the new branch points at. Every path gets a new blob OID; the cache of stat() information is reset. This is fast because Git reads the tree object directly; it does not scan the working tree. - The working tree is updated. For each path whose blob OID changed between the old HEAD’s tree and the new branch’s tree, Git writes the new bytes to disk. Unchanged paths are not touched. A path that is clean in the index but dirty in the working tree (edited but not staged) blocks the switch — Git refuses, because writing the new bytes would silently destroy the uncommitted edits.
git switch main
# Switched to branch 'main'
git switch feature/iam-rotation
# Switched to branch 'feature/iam-rotation'
The two-line “Switched to branch ’…’” output is the only thing the user sees; behind the scenes, three trees have been rewired and zero or more working-tree files have been rewritten.
Why a clean working tree is required
The most common reason a branch switch fails is an unclean working
tree. git status exposes this; git switch enforces it:
git status
# On branch feature/iam-rotation
# Changes not staged for commit:
# modified: terraform/main.tf
# Untracked files:
# scripts/scratch.sh
git switch main
# error: Your local changes to the following files would be overwritten by checkout:
# terraform/main.tf
# Please commit your changes or stash them before you switch branches.
# Aborting
The error message names exactly the path that would be clobbered. The reasoning is straightforward: switching branches is going to write the bytes that the new branch’s tree contains at that path. If your working tree has different bytes there, the switch would silently destroy your edits. Git refuses to do that and asks you to commit, stash, or discard the local changes first.
# Three options when the switch is blocked by a dirty working tree
# 1. Commit the change on the current branch first
git add terraform/main.tf
git commit -m "rotate iam keys on feature branch"
# 2. Stash the change, switch, then unstash
git stash push -m "wip on rotation"
git switch main
git stash pop
# 3. Discard the change (use only when the work is genuinely throwaway)
git restore terraform/main.tf
Switching to the previous branch
git switch - is a shortcut for “go back to wherever I was
before the last switch”. The previous branch is recorded in the
reflog as @{-1}, and - is the readable spelling.
git switch feature/iam-rotation
# Switched to branch 'feature/iam-rotation'
git switch main
# Switched to branch 'main'
git switch -
# Switched to branch 'feature/iam-rotation'
This is the right command for “I just peeked at main, give me back my feature branch”. It is also the safest spelling because it never names the destination explicitly — you cannot typo a branch name that you do not have to type.
Detached HEAD: switching to a commit, not a branch
git switch --detach <commit> checks out a commit directly,
bypassing the branch machinery. HEAD is set to the commit’s OID,
not to a branch name; the working tree and index are still
updated, but no branch has moved.
git switch --detach 8a3f9d2
# HEAD is now at 8a3f9d2 rotate iam keys
The legacy spelling is git checkout 8a3f9d2. A detached HEAD is
the right state for inspecting an old commit (bisecting,
reviewing a tag, recovering a lost branch tip) and the wrong state
for doing work — commits made on a detached HEAD are only
reachable through the reflog and become orphaned when the
reflog expires. The lesson git-cicd-gitops-v-04-detached-head-state
covers this in depth.
Production discipline
- Use
git switch -cfor new work; usegit switch -for “go back”. The two-flag spellings match the common cases and remove the temptation to type a branch name you might typo. - Verify HEAD after every programmatic switch. A CI script
that calls
git switchshould capture the exit code and re-readgit rev-parse --abbrev-ref HEADbefore doing anything that depends on being on the right branch. - Commit or stash before switching. A working tree with uncommitted edits will block the switch. Treat the block as a reminder, not an obstacle: the uncommitted edits belong to the current branch, and the switch is asking you to make that explicit.
- Never
git checkouta remote-tracking ref and start committing.git checkout origin/mainputs you in detached HEAD at the remote’s tip. Always create a local branch first:git switch -c main origin/main(orgit switch mainif a local branch already exists).
Cross-course references
- Ansible for Production Sysadmins - Part XXXVIII (Review)
uses
git switch -c feature/xas the standard opening of a change branch. The dirty-working-tree check is what prevents the common “committed my fix on the wrong branch” mistake. - GitOps with Argo CD - Part IV (AppSources) discusses
branch-per-environment and uses
git switchfor the rare manual operations against the config repo. The discipline of “verify HEAD after every switch” applies directly. - Terraform for Production Sysadmins - Part XII (State) draws the analogy between a branch switch and a Terraform workspace switch: both move the user to a different configuration with a strict consistency check, and both refuse to run when the local state is dirty.
Quiz
Knowledge check · 4 questions
Q1. What does `git switch -c feature/iam-rotation` actually do, in two phases?
Q2. When `git switch main` fails with 'Your local changes to the following files would be overwritten', HEAD has already been moved to main and the index has been partially rewritten.
Q3. What does `git switch -` do, and why is it the safest spelling for toggling between two branches?
Q4. Diagnose a 'committed on the wrong branch' incident and recommend a fix.
An engineer edits terraform/main.tf on a feature branch, runs `git switch main` to pull a recent change from upstream, sees the switch succeed, edits the same file, and commits. The commit lands on main, not on the feature branch. The engineer insists they ran `git switch feature/iam-rotation` first. The team's branch protection requires a PR for main, but the commit was direct-pushed because the engineer is a maintainer.
Passing score: 75%. Answers are checked in this browser.