Git

How to Create a New Branch in Git the Right Way

How to Create a New Branch in Git the Right Way

Run git switch -c feature-login and Git creates the branch and moves you onto it. For most people that’s the whole job.

If you want the branch but don’t want to move yet, git branch feature-login does exactly that. On older Git versions, git checkout -b feature-login handles both steps.

Developers branch to keep a feature or fix away from main until the work is ready to merge. The name itself has to pass the 10 rules in the git-check-ref-format manual, which is why feature login gets rejected and feature/login-form goes through.

How do you create a new branch in Git?

YouTube player

The new branch starts at whatever commit HEAD points to when you run the command. git branch feature-login creates it and leaves you where you are, git checkout -b feature-login creates it and switches, and git switch -c feature-login does the same as checkout -b using the newer command (Git 2.23 and later).

The git-branch manual is explicit that the plain form doesn’t touch the working tree. It also says git switch with -c is the easier route when you want to land on the branch right away.

  1. Confirm your starting point with git status or git branch –show-current.
  2. Commit or stash anything that should stay on the old branch. Uncommitted changes travel with you to the new one.
  3. Create the branch: git switch -c feature-login.
  4. Run git branch to verify. The current branch carries an asterisk.
  5. Commit as usual. New commits land on feature-login only.

Skipping the first step is the usual mistake. Running git status before you branch tells you whether HEAD sits on main or on some leftover topic branch you forgot about.

If the branch already exists, the steps for switching branches in Git are shorter.

What does Git create when you make a branch?

A 41-byte file. According to the Pro Git book (2nd edition), that’s the 40-character SHA-1 of a commit plus a newline.

Nothing else happens. No project files get copied and no history is duplicated, which is why the command finishes instantly even in a repository with years of commits.

The file lives at .git/refs/heads/feature-login and holds the hash of the commit the branch points to. Meanwhile .git/HEAD normally reads ref: refs/heads/main, a symbolic reference to the branch you’re on. After git gc, the same pointer can end up in .git/packed-refs instead of a loose file (git-check-ref-format manual).

Why is GitHub the heart of open source?

Uncover GitHub statistics: developer community growth, repository trends, collaboration patterns, and the platform that powers modern software development.

Explore GitHub Data →

Pro Git describes git branch as running git update-ref with the hash of the commit you are standing on. HEAD is how Git knows which commit that is.

Right after git branch feature-login, git log –oneline –decorate shows both names on one commit: (HEAD -> main, feature-login). Both pointers sit there until one of them moves.

Commits only advance the branch HEAD points to. Every other branch stays put, and that’s what keeps parallel work isolated. For the concept behind it, see the explainer on how a Git branch works.

Should you use git switch -c or git checkout -b?

On Git 2.23 or later, git switch -c. Keep git checkout -b for older installs and existing scripts.

Taskgit checkoutgit switch
Create and switchgit checkout -b feature-logingit switch -c feature-login
Switch to an existing branchgit checkout feature-logingit switch feature-login
Return to the previous branchgit checkout –git switch –
Check out a commitgit checkout a1b2c3dgit switch –detach a1b2c3d

Not sure which you have? Run git –version. Anything from 2.23 up gets git switch -c.

Looking to sharpen your Git skills? Branching, merging, rebasing, and everything else you need - including git rebase, git stash, and commit workflows - is on one page in the Git Cheat Sheet.

Git 2.23 shipped on 2019-08-16 (git-scm.com changelog) and split the overloaded checkout command into git switch for branches and git restore for files.

The sources disagree on how settled that split is. The GitHub Blog’s Git 2.23 highlights labeled both new commands experimental at launch. The Pro Git book now presents git switch as the equivalent for branch work, and the git-switch manual for Git 2.55 (June 2026) documents it as a regular command.

Both statements were accurate on their dates. Today the experimental label no longer describes the command.

One small detail favors git switch. Per its manual, -c is transactional, so if the switch fails the branch is not created. The git-checkout manual says checkout -b fails in that situation too, but it never uses the word transactional or says what happens to the new branch.

The older git checkout command still restores files, which git switch never does. And git switch can’t land on a bare commit without –detach.

How do you create a branch from a specific commit, tag or remote branch?

Put a start point after the branch name, like git branch hotfix-1 a1b2c3d or git switch -c hotfix-1 v2.0. Leave it out and Git uses the current HEAD.

From a commit hash or tag

The git-branch manual accepts a branch name, a commit ID or a tag as the start point.

  • git branch hotfix-1 a1b2c3d
  • git branch release-fix v2.0
  • git switch -c fixup HEAD~3
  • git branch feature-b main

That last line branches from main without checking it out first. A Git tag makes a good start point for a hotfix, since it never moves the way a branch tip does.

From a remote branch

Fetch first, then switch to the name and Git builds the local branch for you. When exactly one remote has a branch called feature-x, git switch feature-x creates a local feature-x that tracks origin/feature-x (git-switch manual, Git 2.55).

The explicit version is git switch -c feature-x –track origin/feature-x, or git branch –track feature-x origin/feature-x if you don’t want to move.

It breaks when two remotes both carry feature-x, because the shortcut can’t choose. Set checkout.defaultRemote to origin, or fall back on the explicit form.

From detached HEAD

Checking out a tag, a commit or a remote branch leaves HEAD pointing at a hash instead of a branch, as Pro Git describes. Commits made there belong to no branch.

That’s a detached HEAD state, and the fix is one command. Run git switch -c rescue-work before you leave and the commits get a name.

What are the rules for naming a branch?

No spaces, ~, ^, :, ?, \*, [ or backslashes. No .. or @{ anywhere in the name, and it can’t end in .lock or a dot. Those limits come straight from the git-check-ref-format manual.

A few names that get rejected:

  • feature login (space)
  • fix..typo (double dot)
  • hotfix:1.2 (colon)
  • release.lock (.lock suffix)
  • -wip (leading dash)

The same manual documents git check-ref-format –branch feature-login as a way to test a name before you use it. The branch check can be stricter than the general reference rules, which is where the leading-dash ban comes from.

Prefixes that group branches

Slashes are legal in branch names, and most teams treat them like folders.

The check-ref-format manual allows a slash for hierarchical grouping, so feature/login-form, bugfix/navbar-overflow and hotfix/1.2.1 sort together in branch lists and on hosting pages. The Git Flow workflow popularized feature/, release/ and hotfix/ as prefixes.

There’s one catch. The slash behaves like a directory separator, so a branch named feature blocks feature/login, and the reverse. Pick your prefix scheme before the first branch exists.

Default branch: main or master

Git and GitHub disagree on the default name, and both positions are current.

The git-init manual lists master as the default and says that changes to main with Git 3.0. The page has been unchanged since Git 2.54 (April 2026). GitHub, on the other hand, has used main for new repositories since 2020-10-01, per the GitHub Changelog.

Your own machine sits somewhere in between. Git 2.28 (2020-07-27) added init.defaultBranch, and if it’s unset, git init still creates master, according to the GitHub Blog’s Git 2.28 highlights.

Set the name once with git config –global init.defaultBranch main, or per repository with git init -b main. The setting only touches new repositories, and git clone keeps following the remote’s HEAD.

In practice, instructions that say “branch from main” fail quietly in a local repository created with master. Run git branch –show-current before pasting commands.

How do you push a new branch to a remote repository?

Run git push -u origin feature-login. It publishes the branch and records origin/feature-login as its upstream in one go.

If you’d rather not type the name twice, git push origin HEAD sends the current branch to a same-named branch on the remote (git-push manual). And git config –global push.autoSetupRemote true makes the first plain git push behave like -u.

The -u flag writes branch.feature-login.remote = origin and branch.feature-login.merge = refs/heads/feature-login into .git/config. After that, argument-less git pull and git push follow the upstream configuration, and git status reports how many commits the branch is ahead or behind.

In these commands, origin is the default name Git gives the remote you cloned from. Any configured remote name works in its place.

After the push, .git/refs/remotes/origin/feature-login stores the last commit you sent (Pro Git). Git treats that file as read-only bookkeeping, so your commits never move it.

Skip -u and a later plain git push can fail, because the new branch has no upstream. With push.default set to simple, the default since Git 2.0, the manual requires an upstream tracking branch with the same name.

Why does creating a branch fail, and how do you fix it?

Usually the name is taken, the name is invalid, the repository has no commit to branch from, local edits block the switch, or the start point doesn’t exist.

MessageCauseFix
a branch named ‘feature-login’ already existsThe name is takenPick another name, switch to the branch, or reset it with git switch -C
‘feature login’ is not a valid branch nameThe name breaks the ref rulesRename it, testing with git check-ref-format –branch
not a valid object name: ‘main’The repository has no commit to branch fromMake a first commit, then branch
error: You have local changes to ‘frotz’; not switching branches.Edits conflict with the target branchCommit, stash, or use git switch -m
the requested upstream branch ‘origin/feature-x’ does not exist–track points at a remote branch you have not fetchedRun git fetch, then retry

Message texts come from the Git source (branch.c) and the git-checkout manual. In the third row, the quoted name is the start point Git could not resolve.

Be careful with git switch -C. It resets the existing branch to the new start point, so only use it when the old tip is expendable.

Creating a branch at HEAD with uncommitted edits never fails, because no files change. The risk starts when the start point differs from HEAD, since git checkout -b fails if checking out that commit would overwrite your uncommitted changes (git-checkout manual).

When that happens, git switch -m topic stashes the conflicting edits, switches, and reapplies them. You can also run git stash before branching and git stash pop afterwards. The details of what git stash does matter here, since untracked files stay behind by default.

If the edits aren’t worth keeping, git switch –discard-changes throws them away for good.

When reapplying an autostash conflicts, Git keeps the stash entry and prints instructions. Resolve the conflicts, then run git stash drop.

Common Questions About Creating a Branch in Git

How do you list all branches in a repository?

Run git branch for local branches, add -r for remote-tracking ones, or -a for both.

git branch -vv goes further and shows each branch’s latest commit along with its upstream name.

Can you rename a branch after creating it?

Yes. git branch -m old-name new-name renames the branch together with its config and reflog, and leaving out the old name renames the current branch.

A branch that is already pushed needs a matching change on the remote, which the guide to renaming a branch in Git walks through.

How do you delete a local branch?

Run git branch -d feature-login. Git refuses unless the branch is fully merged into its upstream, or into HEAD when no upstream exists.

Use -D to force the deletion of unmerged work.

How do you create a branch on GitHub without the command line?

Open the branch dropdown, type a unique name in the “Find or create a branch” field, and click Create branch. You need write access to the repository.

The new branch starts from whichever branch you had selected, and the steps for creating a branch in GitHub cover the issue-page route too.

What is an orphan branch, and how do you create one?

An orphan branch is an unborn branch with no parent commits. Its first commit becomes the root of a history disconnected from every other branch.

git switch –orphan docs-site removes all tracked files from the working tree. git checkout –orphan keeps the start point’s files instead.

Merging and Cleaning Up a Finished Branch

A branch has done its job once its commits land in main and its name is deleted. Merge into main first, then delete the local branch, then delete the remote one.

The remote delete goes last because that copy is the one teammates may still need. The command is git push origin –delete feature-login (git-push manual), and the guide to merging two branches in Git covers the merge itself, conflicts included.

Removing a remote-tracking branch locally is only worth doing once the remote branch is gone, since the next git fetch or git pull recreates it otherwise (git-branch manual).

The git-branch manual for Git 2.56 (2026-09-28) documents –delete-merged <pattern>, which deletes local branches whose configured upstream matches the pattern, but only when the branch tip is already reachable from that upstream. Add –dry-run to preview, and confirm with git branch –help that your installed version includes it.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.