git switch -c new-feature creates a branch from your current position and moves you onto it. git checkout -b new-feature does the same thing and is the older spelling. The command is not where people go wrong — the base commit is, and you rarely notice until the pull request shows forty files you did not touch.
Branching in Git is cheap because a branch is just a pointer to a commit. Creating one takes no time and copies nothing.
What costs time is creating it from the wrong place: branching off a stale main, or off another feature branch by accident, and finding out at review time when your diff contains someone else’s work.
Table of contents
- The commands
- Getting the base right
- Fixing a branch created from the wrong base
- Tracking and pushing
- Naming and hygiene
- How this fits the rest of the stack
- FAQ
The commands
git switch -c new-feature # from the current HEAD
git switch -c new-feature main # explicitly from main
git switch -c new-feature origin/main # from the remote's main
git switch -c hotfix v1.2.0 # from a tag
git switch -c investigate a1b2c3d # from a specific commit
git switch and git restore were introduced in Git 2.23 to split up git checkout, which did too many unrelated things — changing branches, restoring files, and creating branches were all the same command, and the flags interacted confusingly.
The equivalences, since you will see both in every tutorial:
git switch -c name=git checkout -b namegit switch name=git checkout namegit switch -=git checkout -(back to the previous branch)
checkout is not deprecated and will keep working. switch is clearer about intent, and worth adopting for that reason alone.
To create a branch without moving onto it:
git branch new-feature main
Getting the base right
This is the actual skill. Before branching, know two things: where you are, and whether that is current.
git status # which branch am I on?
git log --oneline -3 # what is the last commit here?
git fetch origin # update knowledge of the remote
git log --oneline -3 origin/main # what does the remote main look like?
The reliable pattern for starting a feature, which sidesteps the whole problem:
git fetch origin
git switch -c new-feature origin/main
That branches from the remote’s current main regardless of what your local main looks like or which branch you happen to be standing on. It removes two mistakes at once — the stale local main, and branching off whatever you were last working on.
Check what you have actually based on afterwards:
git log --oneline origin/main..HEAD
# empty = your branch is exactly at origin/main
# commits listed = these are on your branch and not on main
If that shows commits you did not write, you branched off someone else’s work. Better to find out now than in review.
Fixing a branch created from the wrong base
Entirely recoverable, and the tool is rebase --onto, which is worth knowing precisely because this situation is common.
Say you branched from feature-a when you meant to branch from main, and have made three commits:
git rebase --onto main feature-a new-feature
Read that as: take the commits on new-feature that are not on feature-a, and replay them onto main. Your three commits move across; feature-a’s commits are left behind.
If you have not committed anything yet, it is simpler — just move the branch pointer:
git switch -c new-feature # oops, wrong base, nothing committed
git reset --hard origin/main # move it to the right base
And if you have already started work on the wrong branch entirely, without creating a new one, your commits can be moved:
git switch -c new-feature # brings the uncommitted work along
# or if already committed on main:
git switch -c new-feature # branch here, keeping the commits
git switch main
git reset --hard origin/main # clean main back up
Create the branch first, then clean up the branch you should not have committed to. Doing it in the other order is how the commits get lost.
Tracking and pushing
A new local branch has no remote counterpart until you push it:
git push -u origin new-feature
-u sets the upstream, so subsequent git push and git pull need no arguments. Without it you get told off every time.
To make that automatic, since it is what you want every time:
git config --global push.autoSetupRemote true
Available since Git 2.37 and one of the better quality-of-life settings.
Two related conveniences worth having:
git branch -vv # branches with their upstreams and ahead/behind counts
git switch - # jump back to the previous branch
git switch - is the branch equivalent of cd -, and it is used constantly once you know it exists.
For checking out a branch that exists on the remote but not locally, recent Git does the right thing automatically:
git fetch origin
git switch someone-elses-branch # creates a local branch tracking origin's
Naming and hygiene
A convention that survives contact with a real team:
feature/user-authentication
fix/login-redirect-loop
chore/upgrade-node-22
hotfix/payment-timeout
Prefixes group branches in listings and make the intent obvious in a pull request title. Include a ticket number if your team uses one — fix/PROJ-482-login-redirect is greppable a year later, which a branch called fix is not.
Practical constraints worth knowing:
- No spaces. Use hyphens.
- Avoid
..,~,^,:and?— Git rejects them because they mean things in revision syntax. - Case sensitivity varies by filesystem.
Feature/Loginandfeature/logincan collide on macOS and Windows. - You cannot have both a branch
featureand a branchfeature/login— the first blocks the second.
Delete branches once merged, locally and remotely:
git branch -d new-feature # refuses if not merged
git push origin --delete new-feature
git fetch --prune # clear out remote-tracking refs that no longer exist
git fetch --prune is the one people skip, which is why git branch -a eventually lists dozens of branches that were deleted on the server months ago.
How this fits the rest of the stack
Branching well is mostly about knowing what your work is based on, because that base determines what your diff contains and what your deploy will actually run. A branch cut from a stale main produces a pull request that is harder to review and a merge that is more likely to conflict.
The same question applies at deploy time: which commit is running right now, and what changed since the last one. RunxBuild builds from a connected GitHub repository and keeps a build log and a rollback target per deploy, so the running version always maps back to a specific commit rather than to whatever was on the server. If you want to see what that service costs alongside a managed database and storage, the RunxBuild hosting calculator lists them separately.
Useful related references:
- How to Get a New Temporary IPv6 Address (and Why Privacy Extensions Exist)
- Python Get Current Directory: os.getcwd Is Not Where Your Script Lives
- Linux Show Current Path: pwd, $PWD, and Why They Sometimes Disagree
- Services on RunxBuild
FAQ
What is the difference between git switch -c and git checkout -b?
Nothing functionally — both create a branch and move onto it. git switch was introduced in Git 2.23 to separate branch operations from file restoration, which checkout had confusingly combined. checkout still works and is not deprecated.
How do I create a branch from main without switching to main first?
git switch -c new-feature main branches from your local main. Better still, git fetch origin && git switch -c new-feature origin/main branches from the remote’s current main, which avoids the stale-local-main problem entirely.
I branched from the wrong branch. How do I fix it?
Use git rebase --onto main wrong-base your-branch, which replays your commits onto the correct base and leaves the wrong branch’s commits behind. If you have not committed anything yet, git reset --hard origin/main is enough.
How do I check what my branch is based on?
git log --oneline origin/main..HEAD lists commits on your branch that are not on main. If that output contains commits you did not write, you branched off someone else’s work rather than off main.
Why does git push complain about no upstream branch?
A new local branch has no remote counterpart until you push it with -u, as in git push -u origin new-feature. To make this automatic for every new branch, set git config --global push.autoSetupRemote true.