Skip to content

switch Git Command Guide

The git switch command switches branches and creates new branches safely. Introduced in Git 2.23, it provides a dedicated command for branch switching operations, separating them from the overloaded git checkout command for better clarity and safety.

Terminal window
git switch [<options>] [--no-guess] <branch>
git switch [<options>] --detach [<start-point>]
git switch [<options>] (-c|-C) <new-branch> [<start-point>]
git switch [<options>] --orphan <new-branch> [<start-point>]
OptionDescription
<branch>Switch to existing branch
--detachSwitch to detached HEAD state
-c <new-branch>Create and switch to new branch
-C <new-branch>Create and switch to new branch (force)
--orphan <new-branch>Create orphan branch
--guessGuess branch name (default)
--no-guessDon’t guess branch name
OptionDescription
--trackSet up tracking for new branch
--no-trackDon’t set up tracking
-t, --trackSet up tracking (same as —track)
--no-trackDon’t set up tracking
OptionDescription
--mergePerform three-way merge if needed
--conflict=<style>Conflict resolution style
-X <option>Pass merge strategy option
--ignore-other-worktreesIgnore other worktrees
--recurse-submodulesRecurse into submodules
OptionDescription
-q, --quietSuppress feedback messages
-v, --verboseVerbose output
--progressForce progress reporting
--no-progressDisable progress reporting
OptionDescription
--force-createForce create branch
--forceForce switch (discard local changes)
--discard-changesDiscard uncommitted changes
--mergeMerge uncommitted changes
--abortAbort switch operation
ParameterDescription
<branch>Name of branch to switch to
<new-branch>Name for new branch to create
<start-point>Commit/branch to start new branch from
Command Comparison:
├── git switch: Dedicated for branch operations
│ ├── Safe branch switching
│ ├── Clear intent for branch operations
│ ├── Better error messages
│ └── Modern Git approach
└── git checkout: Legacy command (overloaded)
├── Branch switching + file operations
├── Can be confusing
├── Still works for file operations
└── Backward compatibility
Branch Switching States:
├── Clean Working Tree: Safe to switch
├── Uncommitted Changes: May need merge or stash
├── Untracked Files: Generally safe to switch
├── Detached HEAD: HEAD points to commit, not branch
├── Orphan Branch: New branch with no history
└── Tracking Branch: Local branch tracking remote
Switch Safety Guidelines:
├── ✓ Clean working tree: Always safe
├── ✓ Untracked files: Safe (not in Git tracking)
├── ⚠ Modified files: May conflict or need merge
├── ⚠ Staged changes: May conflict or need merge
├── ✗ Uncommitted changes in target: May overwrite
└── ✗ Force switch: Discards uncommitted work
Terminal window
# Switch to existing branch
git switch main
git switch develop
# Switch with verbose output
git switch -v feature-branch
# Switch quietly
git switch -q release-branch
Terminal window
# Create and switch to new branch
git switch -c new-feature
# Create from specific commit
git switch -c bug-fix abc123
# Create from another branch
git switch -c feature-ui develop
# Force create (overwrite existing)
git switch -C existing-branch
Terminal window
# Switch to detached HEAD
git switch --detach v1.0
# Switch to specific commit
git switch --detach HEAD~3
# Switch to tag
git switch --detach v2.1.0
Terminal window
# Create orphan branch (no history)
git switch --orphan experimental
# Useful for complete rewrites
# Working tree preserved, history starts fresh
Terminal window
# Automatic merge of uncommitted changes
git switch --merge feature-branch
# Discard uncommitted changes
git switch --discard-changes feature-branch
# Force switch (dangerous)
git switch --force feature-branch
Terminal window
# Create tracking branch
git switch --track origin/feature-remote
# Create tracking with custom name
git switch -c local-feature --track origin/remote-feature
# Switch to tracking branch
git switch main # If main tracks origin/main
Terminal window
# Switch to remote branch (creates local tracking)
git switch remote-branch
# Create local branch from remote
git switch -c local-copy origin/remote-branch
# Update all remote tracking branches
git fetch --all
git switch remote-branch
Terminal window
# Switch with submodule updates
git switch --recurse-submodules feature-branch
# Ignore other worktrees
git switch --ignore-other-worktrees main
Terminal window
# Configure switch behavior
git config switch.defaultBranch main # Default branch name
git config switch.defaultRemote origin # Default remote
git config switch.defaultMergeOptions --no-edit # Default merge options
# Configure tracking behavior
git config branch.autosetupmerge always # Auto setup merge
git config branch.autosetuprebase always # Auto setup rebase
# Configure colors
git config color.branch.current yellow
git config color.branch.local green
git config color.branch.remote red
Terminal window
# Always check status before switching
git status
# Use descriptive branch names
git switch -c feature/user-authentication
# Keep branches focused
# One feature or fix per branch
# Regular cleanup
git branch --merged | grep -v main | xargs git branch -d
# Use tracking branches
git switch --track origin/feature
Terminal window
# Check for uncommitted changes
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "Uncommitted changes detected"
git stash push -m "Auto-stash before switch"
git switch "$branch"
git stash pop
else
git switch "$branch"
fi
# Verify switch success
current_branch=$(git branch --show-current)
if [ "$current_branch" = "$target_branch" ]; then
echo "Successfully switched to $target_branch"
fi
#!/bin/bash
# Feature branch workflow with switch
start_feature() {
local feature_name="$1"
local base_branch="${2:-develop}"
# Switch to base branch
git switch "$base_branch"
git pull origin "$base_branch"
# Create feature branch
git switch -c "feature/$feature_name"
# Push and set upstream
git push -u origin "feature/$feature_name"
echo "Feature branch 'feature/$feature_name' created and ready"
}
# Usage
start_feature "user-dashboard" "develop"
Terminal window
# Emergency hotfix workflow
create_hotfix() {
local issue_number="$1"
local base_branch="${2:-main}"
# Switch to stable branch
git switch "$base_branch"
git pull origin "$base_branch"
# Create hotfix branch
git switch -c "hotfix/issue-$issue_number"
echo "Hotfix branch created. Make your changes and:"
echo "git add . && git commit -m 'Fix issue $issue_number'"
echo "git push origin hotfix/issue-$issue_number"
}
create_hotfix "123" "main"
Terminal window
# Release branch workflow
prepare_release() {
local version="$1"
local release_branch="release/v$version"
# Switch to develop
git switch develop
git pull origin develop
# Create release branch
git switch -c "$release_branch"
# Perform release preparation
echo "Release branch $release_branch ready for testing"
# After testing, merge to main and develop
# git switch main
# git merge --no-ff "$release_branch"
# git switch develop
# git merge --no-ff "$release_branch"
}
prepare_release "3.2.1"
Terminal window
# Stash changes before switching
git stash push -m "Work in progress"
git switch feature-branch
git stash pop
# Merge changes during switch
git switch --merge feature-branch
# Discard changes (dangerous)
git switch --discard-changes feature-branch
Terminal window
# Create branch if it doesn't exist
git switch -c new-branch
# Check available branches
git branch -a
# Fetch remote branches
git fetch --all
git switch remote-branch
Terminal window
# Check if in detached HEAD
git branch --show-current # Empty output = detached
# Create branch from detached HEAD
git switch -c new-branch
# Return to branch
git switch main
Terminal window
# Remote branch not visible
git fetch origin
git switch remote-branch
# Create local tracking branch
git switch -c local-branch origin/remote-branch
# Set upstream manually
git switch local-branch
git branch --set-upstream-to=origin/remote-branch
Terminal window
# Check repository permissions
ls -la .git/
# Handle file permission conflicts
git switch --force feature-branch
# Clean untracked files if needed
git clean -fd
git switch feature-branch
Terminal window
# Switch with submodules
git switch --recurse-submodules feature-branch
# Update submodules after switch
git submodule update --init --recursive
# Handle submodule conflicts
cd submodule/
git switch appropriate-branch
cd ..
git add submodule/
Terminal window
# Speed up switch operations
git switch -q feature-branch # Quiet mode
# Pre-fetch branches
git fetch --all
# Use local operations when possible
git switch local-branch # No network needed
Terminal window
# Reset switch configuration
git config --unset switch.defaultBranch
# Check current configuration
git config --list | grep switch
# Fix tracking issues
git config branch.autosetupmerge true
#!/bin/bash
# Integrated development workflow with switch
dev_workflow_switch() {
echo "=== Development Workflow Switch ==="
# Show available branches
echo "Available branches:"
git branch -v | head -10
# Smart branch switching
switch_smart() {
local target_branch="$1"
# Check if branch exists
if ! git show-ref --verify --quiet "refs/heads/$target_branch"; then
echo "Branch $target_branch doesn't exist locally"
# Try remote branch
if git ls-remote --heads origin "$target_branch" | grep -q .; then
git switch -c "$target_branch" "origin/$target_branch"
return $?
else
echo "Branch $target_branch not found"
return 1
fi
fi
# Check for uncommitted changes
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "Uncommitted changes detected"
read -p "Stash changes? (y/n): " stash_choice
if [[ "$stash_choice" == "y" ]]; then
git stash push -m "Auto-stash before switch to $target_branch"
git switch "$target_branch"
git stash pop
else
git switch --merge "$target_branch"
fi
else
git switch "$target_branch"
fi
echo "Switched to $(git branch --show-current)"
}
# Command dispatcher
case "$1" in
to) switch_smart "$2" ;;
feature) switch_smart "feature/$2" ;;
bugfix) switch_smart "bugfix/$2" ;;
release) switch_smart "release/$2" ;;
*) echo "Usage: $0 {to <branch>|feature <name>|bugfix <name>|release <name>}" ;;
esac
}
dev_workflow_switch "$@"
Terminal window
# Automated branch management
branch_housekeeping() {
echo "=== Branch Housekeeping ==="
# Switch to main branch first
git switch main 2>/dev/null || git switch master 2>/dev/null || {
echo "No main/master branch found"
return 1
}
# Update from remote
git pull origin main 2>/dev/null || true
# Find and clean merged branches
echo "Checking for merged branches..."
merged_branches=$(git branch --merged | grep -v "main\|master\|develop" | grep -v "^\*" | sed 's/^ //')
if [ -n "$merged_branches" ]; then
echo "Merged branches to consider deleting:"
echo "$merged_branches"
echo ""
read -p "Delete merged branches? (y/N): " delete_choice
if [[ "$delete_choice" == "y" ]]; then
echo "$merged_branches" | xargs git branch -d
echo "Merged branches cleaned up"
fi
else
echo "No merged branches to clean up"
fi
# Check for stale branches
echo ""
echo "Checking for stale branches..."
git branch -v | while read -r branch commit rest; do
branch=$(echo "$branch" | sed 's/^* //')
# Skip main branches
[[ "$branch" == "main" || "$branch" == "master" || "$branch" == "develop" ]] && continue
# Check if last commit > 90 days ago
if ! git log -1 --since="90 days ago" "$commit" >/dev/null 2>&1; then
echo "Stale branch: $branch (last commit: $(git log -1 --format='%ci' "$commit"))"
fi
done
echo "Housekeeping complete"
}
branch_housekeeping
Terminal window
# CI/CD branch switching
ci_branch_switch() {
echo "=== CI/CD Branch Switch ==="
local target_branch="${CI_BRANCH:-main}"
local commit_sha="${CI_COMMIT_SHA:-HEAD}"
# Ensure clean working tree
if ! git diff --quiet --exit-code || ! git diff --cached --quiet --exit-code; then
echo "ERROR: Working tree not clean in CI"
git status
exit 1
fi
# Switch to target branch
if git show-ref --verify --quiet "refs/heads/$target_branch"; then
git switch "$target_branch"
else
echo "Creating branch $target_branch from $commit_sha"
git switch -c "$target_branch" "$commit_sha"
fi
# Verify switch
current_branch=$(git branch --show-current)
if [ "$current_branch" != "$target_branch" ]; then
echo "ERROR: Failed to switch to $target_branch"
exit 1
fi
# Update submodules if present
if [ -f .gitmodules ]; then
git submodule update --init --recursive
fi
echo "Successfully switched to $target_branch"
echo "Current commit: $(git rev-parse HEAD)"
}
ci_branch_switch
Terminal window
# Team collaboration workflow
team_collaboration() {
echo "=== Team Collaboration Workflow ==="
# Switch to team branch
switch_team_branch() {
local team_branch="$1"
# Check if team member has local changes
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "Local changes detected - stashing"
git stash push -m "Team switch: $team_branch"
stashed=true
fi
# Switch to team branch
git switch "$team_branch" 2>/dev/null || {
echo "Team branch $team_branch not found locally"
# Try to create from remote
git switch -c "$team_branch" "origin/$team_branch" 2>/dev/null || {
echo "Cannot access team branch $team_branch"
return 1
}
}
# Pull latest changes
git pull origin "$team_branch" || {
echo "Failed to pull latest changes"
return 1
}
# Restore stashed changes if any
if [ "$stashed" = true ]; then
if git stash pop 2>/dev/null; then
echo "Local changes restored"
else
echo "WARNING: Could not restore local changes - manual merge may be needed"
fi
fi
echo "Successfully switched to team branch $team_branch"
}
# Create pair programming branch
create_pair_branch() {
local feature="$1"
local pair_name="$2"
local pair_branch="pair/$feature-$pair_name"
git switch -c "$pair_branch"
git push -u origin "$pair_branch"
echo "Pair programming branch created: $pair_branch"
echo "Both team members can now work on this branch"
}
# Merge team branch back
merge_team_branch() {
local team_branch="$1"
local target_branch="${2:-develop}"
# Switch to target
git switch "$target_branch"
git pull origin "$target_branch"
# Merge team branch
git merge --no-ff "$team_branch" -m "Merge team branch: $team_branch"
if [ $? -eq 0 ]; then
git push origin "$target_branch"
echo "Team branch $team_branch successfully merged"
else
echo "Merge failed - resolve conflicts and try again"
return 1
fi
}
case "$1" in
switch) switch_team_branch "$2" ;;
pair) create_pair_branch "$2" "$3" ;;
merge) merge_team_branch "$2" "$3" ;;
*) echo "Usage: $0 {switch <branch>|pair <feature> <name>|merge <branch> [target]}" ;;
esac
}
team_collaboration "$@"
Terminal window
# Emergency branch switching
emergency_response() {
echo "=== Emergency Response Workflow ==="
# Quick switch to emergency branch
emergency_switch() {
local emergency_branch="emergency-fix"
# Force immediate switch (preserve current work)
git switch -c "$emergency_branch" 2>/dev/null || git switch "$emergency_branch"
echo "Switched to emergency branch: $emergency_branch"
echo "Make critical fixes now"
echo "To return to previous work: git switch -"
}
# Return from emergency
emergency_return() {
# Switch back to previous branch
git switch -
# Check if emergency branch exists
if git show-ref --verify --quiet "refs/heads/emergency-fix"; then
echo "Emergency branch still exists"
echo "Review and merge emergency fixes when ready"
fi
}
# Merge emergency fixes
emergency_merge() {
local target_branch="${1:-main}"
git switch "$target_branch"
git pull origin "$target_branch"
git merge --no-ff emergency-fix -m "Emergency fix: $(git log --oneline -1 emergency-fix)"
if [ $? -eq 0 ]; then
git push origin "$target_branch"
git branch -d emergency-fix
echo "Emergency fixes merged and cleaned up"
else
echo "Emergency merge failed"
return 1
fi
}
case "$1" in
start) emergency_switch ;;
return) emergency_return ;;
merge) emergency_merge "$2" ;;
*) echo "Usage: $0 {start|return|merge [target-branch]}" ;;
esac
}
emergency_response "$@"

What’s the difference between git switch and git checkout?

Section titled “What’s the difference between git switch and git checkout?”

git switch is dedicated to branch operations only, while git checkout handles both branch switching and file restoration. switch provides clearer intent and better error messages.

How do I create a new branch and switch to it?

Section titled “How do I create a new branch and switch to it?”

Use git switch -c to create and switch to a new branch in one command.

Can I switch branches with uncommitted changes?

Section titled “Can I switch branches with uncommitted changes?”

Yes, but only if the changes don’t conflict. Use —merge to merge uncommitted changes, or stash them first.

When HEAD points to a commit instead of a branch. You can make commits but they’ll be lost when switching away unless you create a branch.

How do I switch back to the previous branch?

Section titled “How do I switch back to the previous branch?”

Use git switch - to switch to the previously checked out branch.

Yes, git switch creates a local tracking branch if it doesn’t exist.

What’s the difference between -c and -C?

Section titled “What’s the difference between -c and -C?”

-c creates a new branch, -C creates a new branch even if one with that name already exists (force create).

If conflicts occur during switch —merge, you can abort with git switch —abort (though this may not always be available).

Can I switch branches in a dirty working tree?

Section titled “Can I switch branches in a dirty working tree?”

Yes, but it’s safer to commit, stash, or use —merge. Force switching discards uncommitted changes.

A branch created with —orphan starts with no commit history, useful for complete project rewrites.

Use git branch —show-current or git status to see the current branch.

Use git switch —detach to switch to a specific commit in detached HEAD state.

—guess (default) allows Git to guess branch names from partial matches, like “git switch feat” matching “feature-branch”.

Resolve conflicts manually by editing files, then use git add to stage resolved files. The switch operation will complete automatically.

Use —recurse-submodules to update submodules when switching branches.

What’s the performance impact of switching?

Section titled “What’s the performance impact of switching?”

Generally fast, but can be slower with large working trees or many untracked files.

How do I switch to a branch that doesn’t exist locally?

Section titled “How do I switch to a branch that doesn’t exist locally?”

First fetch from remote, then git switch will create a local tracking branch.

Switching itself is reversible with git switch -, but if you made commits in detached HEAD, they may be lost.

Use git switch —detach to switch to a tag in detached HEAD state.

  1. Branch Navigation: Safely switch between different development branches
  2. Feature Development: Create and switch to feature branches for isolated work
  3. Bug Fixes: Switch to bug fix branches for targeted repairs
  4. Release Management: Switch between release branches for version control
  5. Code Review: Switch to branches under review for testing and validation
  6. Collaborative Development: Coordinate branch switching in team workflows
  7. Emergency Response: Quick switching for critical fixes and hotfixes
  8. Workflow Automation: Scripted branch operations in CI/CD pipelines