Beginner 5 min · March 29, 2026

Git Checkout -b — Direct Push to Main Broke Payments

A direct push to main at 4:47pm caused NullPointerException in payments.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Creates a 41-byte pointer file in .git/refs/heads/ pointing at current commit
  • Moves HEAD to the new branch
  • Working directory stays identical — no files copied
  • -b flag — create branch if it doesn't exist, then switch
  • git switch -c — modern replacement (Git 2.23+), same outcome, safer scope
  • Always git pull origin main before branching — stale base = merge conflict time bomb
Plain-English First

Imagine you're editing a legal contract in Microsoft Word. Before you start making risky changes, you hit 'Save As' and create a copy called 'contract-draft-v2'. Now you can hack away on the copy without touching the original. Git checkout -b does exactly that — except parallel universe of your codebase in milliseconds. The original stays untouched. You work in your universe. When you're happy, you merge the universes back together. That's it.

git checkout -b creates a branch and switches to it in one command. It solves the core problem of parallel development: isolating your changes from everyone else's work without asking permission or copying files.

A branch is a 41-byte pointer to a commit — not a folder copy. Creating one is free and instant regardless of repository size. This mental model explains why branching feels instant, why switching is fast, and why merging is possible.

Common misconceptions: that branches duplicate files (they don't — they're pointers), that checkout -b is the only way to create branches (switch -c is the modern replacement), instead of a clunky file copy, it creates a lightweight and that you can safely branch without pulling first (you can't — stale base creates merge conflicts). The most dangerous mistake is committing to main without realizing you never created a branch.

What Git Is Actually Doing When You Run checkout -b

Before you touch the command, you need a mental model of what a branch actually is — because if you think it's a folder copy, you'll be confused forever.

A branch in Git is nothing more than a named pointer to a commit. That's it. A tiny text file containing a 40-character hash. When you run git checkout -b feature/user-authentication, Git does two things: it creates that pointer file in .git/refs/heads/ pointing at your current commit, then it moves a special pointer called HEAD to point at your new branch. HEAD is Git's way of saying 'this is where I am right now.' No files are copied. No new directory is created. The whole operation takes milliseconds regardless of how large your codebase is.

Why does this matter? Because it explains everything about how branches behave. Switching branches feels instant because Git is just updating two pointers and swapping out the files that differ between commits. Creating a branch is free because it's literally creating a 41-byte file. And merging is possible because Git can trace the commit history through those pointers and figure out exactly what changed on each branch.

Without this mental model, branch operations feel like magic. With it, they feel inevitable.

io/thecodeforge/git/BranchInternals.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# io.thecodeforge — Git Branch Internals

# Check where HEAD currently points before creating a branch
# This shows you your current branch — in this case, main
git branch
# Output: * main

# Create and switch to a new branch in a single command
# The -b flag means 'create this branch if it doesn't exist, then switch to it'
git checkout -b feature/user-authentication

# Now let's look at what Git actually created on disk
# Git stores branch pointers as plain text files under .git/refs/heads/
cat .git/refs/heads/feature/user-authentication
# Output: a3f8c1d2e4b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0
# That 40-char hash IS the branch — it's just a pointer to a commit

# HEAD now points to the new branch, not main
cat .git/HEAD
# Output: ref: refs/heads/feature/user-authentication

# Confirm the switch worked — asterisk shows your active branch
git branch
# Output:
#   main
# * feature/user-authentication

# The new branch starts at exactly the same commit as main
# No files changed, nothing was copied — just a new pointer was created
git log --oneline -1
# Output: a3f8c1d (HEAD -> feature/user-authentication, main) Initial commit
Output
Switched to a new branch 'feature/user-authentication'
ref: refs/heads/feature/user-authentication
main
* feature/user-authentication
a3f8c1d (HEAD -> feature/user-authentication, main) Initial commit
Branches Are Pointers, Not Copies
  • Creating a branch: one 41-byte file in .git/refs/heads/
  • Switching branches: Git updates the working directory to match the target commit
  • Deleting a branch: removes the pointer file. The commits remain until garbage-collected.
  • HEAD is a special pointer that tracks which branch you're currently on
Production Insight
Understanding that branches are pointers explains why creating hundreds of branches has zero performance impact on Git operations. The overhead comes from the commits themselves (file content), not the branch pointers. This also explains why deleting a branch doesn't delete the commits — the pointer is removed, but the commit objects remain in the object database until garbage collection runs (default: 90 days). This is why git reflog and git branch --contains can find 'deleted' branches for weeks after deletion.
Key Takeaway
A branch is a 41-byte pointer to a commit — not a file copy. Creating one is free and instant. Switching branches updates the working directory to match the target commit. Understanding this mental model makes all branch operations predictable.

The Right Way to Create Branches Before You Touch a Single File

Here's the discipline that separates controlled development from chaos: you create the branch before you write any code. Not after. Not halfway through. Before.

I've seen developers write two hours of code on main, then try to retroactively move it to a branch. It works — git stash and git checkout -b can rescue you — but it's a mess you're creating for yourself unnecessarily. The correct reflex is: new task, new branch, then code.

The branch name matters more than most tutorials admit. A branch named 'fix' or 'test2' is a gift to your future self that keeps giving pain. Use a consistent naming convention your whole team agrees on. The most common patterns are feature/short-description, bugfix/issue-number-description, and hotfix/critical-thing. The forward slash creates a visual namespace without any technical special meaning in Git itself — it just groups branches when you list them.

Also: always know which branch you're branching from. Running git checkout -b without checking where you are is how you branch off someone else's half-finished feature branch by accident. I've seen this cause a 45-minute merge headache during a release. Always confirm with git branch or git status first.

io/thecodeforge/git/BranchWorkflow.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# io.thecodeforge — Git Branch Workflow

# STEP 1: Always confirm where you are before creating a branch
# Never skip this — branching from the wrong base is a silent killer
git status
# Output: On branch main — good, we're on the right base

# STEP 2: Pull the latest changes so your branch starts up-to-date
# Branching from a stale main means a painful merge later
git pull origin main

# STEP 3: Create and switch to your feature branch
# Convention: feature/<ticket-number>-<short-description>
# This ties the branch to a ticket in Jira/Linear/GitHub Issues
git checkout -b feature/PAY-421-stripe-webhook-handler
# Output: Switched to a new branch 'feature/PAY-421-stripe-webhook-handler'

# STEP 4: Verify the switch — never assume
git branch
# Output:
#   main
# * feature/PAY-421-stripe-webhook-handler

# STEP 5: Do your work — add files, make commits
# Your changes are now completely isolated from main
touch stripe_webhook_handler.py
git add stripe_webhook_handler.py
git commit -m "PAY-421: scaffold Stripe webhook handler"

# STEP 6: Push the branch to remote so others can see it and CI can run
# -u sets the upstream tracking, so future 'git push' needs no arguments
git push -u origin feature/PAY-421-stripe-webhook-handler
# Output: Branch 'feature/PAY-421-stripe-webhook-handler' set up to track remote branch
Output
On branch main
Your branch is up to date with 'origin/main'.
Already up to date.
Switched to a new branch 'feature/PAY-421-stripe-webhook-handler'
main
* feature/PAY-421-stripe-webhook-handler
[feature/PAY-421-stripe-webhook-handler (root-commit) 7d3a1b2] PAY-421: scaffold Stripe webhook handler
Branch 'feature/PAY-421-stripe-webhook-handler' set up to track 'origin/feature/PAY-421-stripe-webhook-handler'.
Production Trap: Stale Base Creates Merge Conflict Time Bombs
  • Stale base = branch starts from an old commit that's behind main
  • Every file your teammates touched since that commit becomes a potential conflict
  • The fix costs 10 seconds: git pull origin main before git checkout -b
  • The pain costs hours: resolving conflicts on a 40-commit-behind PR
Production Insight
Branch naming conventions drive CI/CD automation. Most CI systems use glob patterns to trigger builds (feature/ triggers PR validation, release/ triggers staging deploy). Branch protection rules use patterns to enforce policies. Inconsistent naming (feat/login vs feature/login vs login-feature) breaks these automations silently. Teams should enforce naming conventions via pre-push hooks or CI linting, not just documentation. The ticket number in the branch name (PAY-421) enables automated changelog generation and traceability from branch to Jira/Linear ticket.
Key Takeaway
Create the branch before writing code — not after. Always pull main before branching to avoid stale-base merge conflicts. Branch naming conventions (feature/TICKET-description) drive CI automation and traceability. Verify your branch with git branch before every commit.

checkout -b vs git switch -c: The Command That's Replacing It

Here's something most beginner tutorials skip entirely: git checkout is an overloaded command that does too many unrelated things. It checks out branches, checks out individual files, detaches HEAD, restores file content — it's a Swiss Army knife that's genuinely confusing. Git 2.23 (released 2019) introduced git switch specifically to handle branch operations cleanly, separating concerns properly.

git switch -c feature/my-branch does exactly what git checkout -b does. The -c flag means 'create'. There's also git restore for the file-restoration job that checkout used to do. The Git team is not deprecating checkout, so nothing will break. But if you're learning Git today, you should know that git switch -c is the more intentional choice for branch creation — it can't accidentally clobber your file changes because it doesn't know how.

That said, you'll see git checkout -b in virtually every existing tutorial, script, CI pipeline, and StackOverflow answer for the next decade. You need to know both. Use git switch -c in your own new work. Recognise git checkout -b everywhere else. They're identical in outcome for the branch creation use case — the difference is purely about command clarity and intent.

io/thecodeforge/git/CheckoutVsSwitch.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# io.thecodeforge — Checkout vs Switch

# THE OLD WAY — git checkout -b (still works, still everywhere)
# Creates branch 'feature/order-refund-api' and switches to it
git checkout -b feature/order-refund-api
# Output: Switched to a new branch 'feature/order-refund-api'

# Go back to main to demonstrate the equivalent modern command
git checkout main

# THE NEW WAY — git switch -c (available since Git 2.23)
# Identical outcome, but the command can ONLY deal with branches
# It cannot accidentally restore file content — safer mental model
git switch -c feature/order-refund-api-v2
# Output: Switched to a new branch 'feature/order-refund-api-v2'

# SWITCHING between existing branches — no flag needed
git switch main
# Output: Switched to branch 'main'

# BRANCH FROM A SPECIFIC COMMIT — not from HEAD
# Useful when you need to hotfix an older release tag
# Both commands support this equally
git checkout -b hotfix/v2.4.1-null-pointer-crash v2.4.1
# Creates a new branch starting at the v2.4.1 tag, not current HEAD
# Output: Switched to a new branch 'hotfix/v2.4.1-null-pointer-crash'

# The equivalent with git switch:
git switch -c hotfix/v2.4.1-null-pointer-crash-alt v2.4.1
# Output: Switched to a new branch 'hotfix/v2.4.1-null-pointer-crash-alt'
Output
Switched to a new branch 'feature/order-refund-api'
Switched to a new branch 'feature/order-refund-api-v2'
Switched to branch 'main'
Switched to a new branch 'hotfix/v2.4.1-null-pointer-crash'
Switched to a new branch 'hotfix/v2.4.1-null-pointer-crash-alt'
Senior Shortcut: Check Git Version Before Using switch
  • Git 2.23+ required for switch — released August 2019
  • Corporate servers, Docker base images, and CI runners often lag behind
  • checkout -b is safe in all Git versions — maximum compatibility
  • switch -c is safer for local work — it can't accidentally restore files
Production Insight
The overloaded nature of git checkout is a real source of production bugs. Running git checkout filename silently restores a file to its last committed state, discarding hours of work. Running git checkout <commit-hash> detaches HEAD, which confuses developers who don't understand detached HEAD state. git switch eliminates the first risk entirely — it only deals with branches. git restore handles file restoration with explicit intent. The separation of concerns reduces accidental data loss. In CI/CD scripts, always use git checkout -b for maximum compatibility — you don't control the Git version on every runner image.
Key Takeaway
git switch -c is the modern replacement for git checkout -b — same outcome, safer scope. switch only handles branches; it can't accidentally restore files or detach HEAD. Use checkout -b in CI scripts for compatibility. Use switch -c for local work where intent clarity matters.

Recovering From the Branch Mistakes That Actually Happen

You will mess up a branch at some point. Everyone does. The goal is to know the recovery path before panic sets in at 11pm.

The most common disaster is realising you've been committing to main instead of a feature branch. This happens more than people admit — you start coding, get into flow, make three commits, then look up and see you're on main. Don't push. Here's the exact recovery: create a new branch from your current position with git checkout -b feature/the-thing-i-was-building, which captures all your commits. Then reset main back to where it was with git checkout main followed by git reset --hard origin/main. Your commits now live only on the feature branch, and main is clean again.

The second common mess is naming a branch wrong. You can rename your current branch with git branch -m new-name — the -m flag moves/renames it. If you've already pushed the old name to remote, you'll need to push the new name with git push origin -u new-name and delete the old remote branch with git push origin --delete old-name. It's annoying but fully recoverable.

Branching from the wrong base — like branching from someone else's feature branch instead of main — requires a git rebase --onto to replay your commits onto the correct base. That's a topic on its own, but know the symptom: your PR contains commits you didn't write.

io/thecodeforge/git/BranchRecovery.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# io.thecodeforge — Git Branch Recovery

# SCENARIO: You've made commits directly on main by accident
# DO NOT push — check your log first to confirm the damage
git log --oneline -5
# Output:
# 9c4f2a1 (HEAD -> main) Add Stripe webhook signature validation
# 7b3e1d8 Add Stripe webhook endpoint
# a3f8c1d (origin/main) Initial project scaffold  <-- this is where main should be

# RECOVERY STEP 1: Create a new branch at your current position
# This saves all your accidental main commits
git checkout -b feature/PAY-421-stripe-webhook-handler
# Output: Switched to a new branch 'feature/PAY-421-stripe-webhook-handler'
# Your commits are now safely on the feature branch

# RECOVERY STEP 2: Go back to main
git checkout main

# RECOVERY STEP 3: Reset main to match origin — erases your local commits from main
# --hard means working directory is also reset — your files revert to origin/main state
# This is safe because your commits still exist on the feature branch
git reset --hard origin/main
# Output: HEAD is now at a3f8c1d Initial project scaffold

# Confirm main is clean
git log --oneline -3
# Output: a3f8c1d (HEAD -> main, origin/main) Initial project scaffold

# SEPARATE SCENARIO: Renaming a badly named branch
# You're on the branch you want to rename
git checkout -b fixstuff  # bad name — too vague
git branch -m bugfix/AUTH-88-jwt-expiry-missing  # rename it
# Output: (no output — success is silent in git branch -m)
git branch
# Output:
#   main
# * bugfix/AUTH-88-jwt-expiry-missing
Output
Switched to a new branch 'feature/PAY-421-stripe-webhook-handler'
HEAD is now at a3f8c1d Initial project scaffold
a3f8c1d (HEAD -> main, origin/main) Initial project scaffold
main
* bugfix/AUTH-88-jwt-expiry-missing
Never Do This: Reset Main After Pushing
  • git reset --hard origin/main is safe ONLY if you haven't pushed
  • If you pushed: use git revert to create an undo commit (preserves history)
  • Force-pushing a reset to a shared branch breaks every teammate's local history
  • The rule: reset for local-only mistakes, revert for published mistakes
Production Insight
The checkout -b + reset --hard origin/main recovery pattern is the most common branch rescue in production. The critical constraint: it only works if you haven't pushed. Once pushed, the accidental commits are part of the remote history that teammates may have pulled. At that point, git revert creates a new commit that undoes the changes without rewriting history. The distinction between reset (rewrites history) and revert (preserves history) is fundamental to safe collaboration. Teams should have pre-push hooks that warn when pushing directly to main.
Key Takeaway
The most common branch mistake is committing to main by accident. Recovery: checkout -b to save work, then reset --hard origin/main to clean main. This only works if you haven't pushed. If you pushed, use git revert instead. Never force-push a reset to a shared branch.

Branching From Tags and Commits: Hotfix Patterns

Not all branches start from HEAD. Production hotfixes often need to branch from a specific release tag — you're patching v2.4.1 while v2.5.0 development continues on main. Both git checkout -b and git switch -c accept a starting point as the final argument.

The syntax: git checkout -b hotfix/name v2.4.1 creates a new branch starting at the commit tagged v2.4.1, not your current HEAD. This is critical for release management — you don't want unreleased feature code from main leaking into your hotfix.

After fixing and merging the hotfix, you need to ensure the fix reaches main too. In GitFlow, this means merging the hotfix branch into both main (for the patched release) and develop (so the fix isn't lost in the next release). In GitHub Flow, the hotfix merges to main and you rely on the next deploy to include it.

io/thecodeforge/git/HotfixBranching.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# io.thecodeforge — Hotfix Branching from Tags

# SCENARIO: Production is running v2.4.1. A null pointer crash is reported.
# Main has moved on to v2.5.0-rc1 with unreleased features.
# You need to patch v2.4.1 without pulling in v2.5.0 changes.

# Step 1: Verify the tag exists
git tag -l 'v2.4*'
# Output: v2.4.0, v2.4.1

# Step 2: Create a branch starting from the v2.4.1 tag
git checkout -b hotfix/v2.4.2-null-pointer-crash v2.4.1
# Output: Switched to a new branch 'hotfix/v2.4.2-null-pointer-crash'
# Note: you're now on a branch that has ONLY v2.4.1 code.
# No v2.5.0 features are present.

# Step 3: Verify you're at the right commit
git log --oneline -3
# Output shows only commits up to v2.4.1 tag

# Step 4: Fix the bug
echo '// null check added' >> src/main/java/io/thecodeforge/payment/PaymentService.java
git add src/main/java/io/thecodeforge/payment/PaymentService.java
git commit -m "HOTFIX: add null check for PaymentService.processPayment()"

# Step 5: Tag the hotfix release
git tag -a v2.4.2 -m "Hotfix v2.4.2: null pointer crash in PaymentService"

# Step 6: Merge into main (for deployment) and into develop (for future releases)
git checkout main
git merge --no-ff hotfix/v2.4.2-null-pointer-crash

git checkout develop
git merge --no-ff hotfix/v2.4.2-null-pointer-crash

# Step 7: Push everything
git push origin main develop v2.4.2

git push origin --delete hotfix/v2.4.2-null-pointer-crash
Output
Switched to a new branch 'hotfix/v2.4.2-null-pointer-crash'
v2.4.0
v2.4.1
HOTFIX: add null check for PaymentService.processPayment()
Merge made by the 'ort' strategy.
Tags Are Immutable Release Points
  • Tags are immutable — they always point to the same commit
  • Branching from a tag: your branch has only the code up to that tag
  • No unreleased features from main leak into the hotfix
  • After the hotfix: merge into both main (deploy) and develop (preserve for future)
Production Insight
Branching from tags is essential for release management in teams with multiple supported versions. Without it, hotfixes on main would require either cherry-picking (error-prone) or releasing from a feature-complete main (which may include unreleased features). The tag-based branching pattern ensures the hotfix contains only the fix — nothing else. The merge-back step (into both main and develop) is where bugs are introduced: forgetting to merge into develop means the fix is lost in the next release. CI pipelines should verify that hotfix branches are merged into both targets.
Key Takeaway
Branch from tags for production hotfixes: git checkout -b hotfix/name v2.4.1. This creates a branch with only the tagged release's code — no unreleased features from main. After the fix, merge into both main (for deployment) and develop (to preserve the fix for future releases). Tags are immutable release points; branches from tags create parallel patch timelines.
● Production incidentPOST-MORTEMseverity: high

Friday Afternoon Push to Main: Half-Built Feature Breaks Payments

Symptom
CI pipeline on main showed red tests. The payments service deployed automatically from main and started returning 500 errors for checkout requests. Monitoring dashboard showed a spike in failed transactions starting at 4:47pm.
Assumption
The team initially assumed a deployment infrastructure issue or a dependency update that broke compatibility. The rollback was initiated based on the deployment timestamp correlation.
Root cause
1. The developer started working on a new feature (partial refund logic) without creating a branch. 2. Three commits were made directly to main over two hours. 3. The commits included incomplete refund logic with hardcoded test values and missing null checks. 4. The developer pushed to main at 4:45pm. CI ran, tests failed, but the auto-deploy pipeline was configured to deploy on push to main regardless of test status (a separate process failure). 5. The half-built refund logic was deployed to production at 4:47pm. 6. The missing null check caused NullPointerException on every checkout request that didn't have a refund context (which was all of them — the feature wasn't complete).
Fix
1. Immediate rollback: kubectl rollout undo deployment/payments-service -n production to restore the previous version. 2. Recovery: Created a branch from the current HEAD to save the developer's work: git checkout -b feature/partial-refund-logic. 3. Reset main: git checkout main && git reset --hard origin/main to remove the three commits from main. 4. Fixed the auto-deploy pipeline: added branch protection rules requiring PR reviews and CI pass before merge to main. 5. The developer's work continued on the feature branch, completed with proper tests, and was merged via PR the following week.
Key lesson
  • Always create a branch before writing code. Not after. Not halfway through. Before.
  • Branch protection rules on main (required PR reviews, required CI pass) would have prevented this entirely. The fix wasn't just technical — it was a process gap.
  • Auto-deploy pipelines that deploy on push to main regardless of test status are dangerous. Test failures should block deployment.
  • The git checkout -b + git reset --hard origin/main recovery pattern only works if you haven't pushed yet. Once pushed, you need revert commits, not resets.
Production debug guideSystematic recovery paths for common branch mistakes.5 entries
Symptom · 01
You committed to main instead of a feature branch and haven't pushed yet
Fix
1. Do NOT push. Run git log --oneline -5 to confirm which commits are on main. 2. Create a new branch at your current position: git checkout -b feature/rescue-branch. 3. Switch back to main: git checkout main. 4. Reset main to match remote: git reset --hard origin/main. 5. Switch to the rescue branch and continue working. Push the branch when ready.
Symptom · 02
You committed to main and already pushed — protected branch rejection or CI failure
Fix
1. Check if the push was rejected by branch protection. If yes, create a branch from your current position and push that instead. 2. If the push succeeded (no branch protection): DO NOT force-push to undo. Create a revert commit: git revert HEAD~N..HEAD where N is the number of accidental commits. 3. Create a branch from before the revert to preserve your work: git checkout -b feature/rescue <pre-revert-sha>. 4. Notify the team that main had direct commits that were reverted.
Symptom · 03
'fatal: A branch named X already exists' when running checkout -b
Fix
1. The branch already exists. Run git branch to see all local branches. 2. If you want to switch to the existing branch: git checkout X (no -b flag). 3. If you want a new branch with a different name: git checkout -b X-v2. 4. If the existing branch is stale and you want to replace it: git branch -D X && git checkout -b X.
Symptom · 04
Your PR contains commits you didn't write — branched from wrong base
Fix
1. Run git log --oneline origin/main..HEAD to see which commits are on your branch. 2. Identify the commit where your work starts (the first commit you wrote). 3. Run git merge-base HEAD origin/main to see where the branch diverged from main. 4. If it diverged from a teammate's branch instead of main: use git rebase --onto origin/main <wrong-base> HEAD to replay your commits onto main. 5. Force-push the corrected branch: git push --force-with-lease.
Symptom · 05
Switching branches fails with 'Your local changes would be overwritten'
Fix
1. You have uncommitted changes that conflict with the target branch. 2. Option A: Commit the changes: git add -A && git commit -m 'WIP: save work in progress'. 3. Option B: Stash the changes: git stash push -m 'work in progress before branch switch'. 4. Then switch branches: git checkout <target-branch> or git switch <target-branch>. 5. To restore stashed work later: git stash pop.
★ Git Branch Triage Cheat SheetFast recovery for branch creation and switching failures.
Accidentally committed to main, haven't pushed
Immediate action
Save work to a new branch, then reset main.
Commands
git checkout -b feature/rescue-branch
git checkout main && git reset --hard origin/main
Fix now
Your commits now live only on the rescue branch. Main is clean.
Accidentally committed to main, already pushed+
Immediate action
Do NOT force-push. Use revert commits.
Commands
git log --oneline -5 (identify accidental commits)
git revert HEAD~N..HEAD (undo on main, preserve history)
Fix now
Create a branch from before the revert to preserve your work. Notify the team.
'fatal: A branch named X already exists'+
Immediate action
Switch to existing branch instead of creating.
Commands
git branch (list all branches to confirm it exists)
git checkout X (switch without -b flag)
Fix now
If you need a fresh branch: git branch -D X && git checkout -b X
PR shows commits from a teammate you didn't write+
Immediate action
You branched from the wrong base. Rebase onto main.
Commands
git merge-base HEAD origin/main (find divergence point)
git rebase --onto origin/main <wrong-base> HEAD
Fix now
Force-push the corrected branch: git push --force-with-lease
'error: Your local changes would be overwritten by checkout'+
Immediate action
Save uncommitted work before switching.
Commands
git stash push -m 'WIP before branch switch'
git checkout <target-branch>
Fix now
Restore later with: git stash pop
Aspectgit checkout -bgit switch -c
Git version requiredAny version (pre-2.0 safe)Git 2.23+ only (2019)
Primary purposeOverloaded — branches AND file restorationBranches only — single responsibility
Can accidentally overwrite filesYes — git checkout filename restores files silentlyNo — file restoration is a separate command (git restore)
Syntax to branch from a tag/commitgit checkout -b hotfix/name v2.4.1git switch -c hotfix/name v2.4.1
Use in CI/CD scriptsSafe — maximum compatibility across all Git versionsRisky — old runner images may not have Git 2.23
Recommended for new local workFine, but legacyYes — intent is clearer, harder to misuse
Industry prevalence in docs/tutorialsDominant — 95%+ of existing guidesGrowing — newer docs prefer it
Detached HEAD mode supportYes — git checkout <commit-hash>Yes — git switch --detach <commit-hash>

Key takeaways

1
A Git branch is a 41-byte pointer file
not a copy of your code. Creating one is free and takes milliseconds regardless of repo size. There's zero reason not to branch for every piece of work.
2
Always pull before you branch. A branch created from a stale base is a merge conflict time bomb
it costs 10 seconds to prevent and potentially hours to untangle after the fact.
3
git checkout -b and git switch -c produce identical results for branch creation. Use checkout -b in scripts that need to run on any Git version. Use switch -c for new local work where intent clarity matters.
4
The scariest git checkout -b mistake isn't creating the branch wrong
it's not realising you're still on main. Make 'git branch' or 'git status' a muscle-memory reflex before every commit. Your teammates will thank you.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

FAQ · 4 QUESTIONS

Frequently Asked Questions

01
What does git checkout -b actually do — is it two commands in one?
02
What's the difference between git checkout -b and git checkout for switching branches?
03
How do I create a branch from a specific commit or tag instead of from HEAD?
04
Can git checkout -b cause data loss, and how do you protect against it?
🔥

That's Git. Mark it forged?

5 min read · try the examples if you haven't

Previous
Git Pull: Fetch and Merge Remote Changes
12 / 19 · Git
Next
Git Delete Local and Remote Branch