Creating a branch is one of the most common things you do in Git, and there are three commands that can do it — `git switch`, `git checkout`, and `git branch`. They were added at different times, work slightly differently, and produce subtly different results.

This tutorial walks through each command, the conventions that make branches easy to live with at scale, and how to handle branches that already exist on a remote.

## The three commands at a glance

| Command | Creates branch? | Switches to it? | Notes |
|---|---|---|---|
| `git switch -c <name>` | ✓ | ✓ | Modern (Git 2.23+). The recommended default. |
| `git checkout -b <name>` | ✓ | ✓ | Older but still everywhere. Identical effect to `switch -c`. |
| `git branch <name>` | ✓ | ✗ | Creates the branch but leaves you where you are. Useful when you want to create from somewhere without leaving your current spot. |

If you only remember one of them, remember `git switch -c`. Git's own documentation now treats `switch` as the preferred command and reserves `checkout` for restoring files. They produce the same result here — the difference is intent.

## `git switch -c` — the modern way

```bash
git switch -c feature/shopping-cart
```

```
Switched to a new branch 'feature/shopping-cart'
```

`-c` is short for `--create`. The new branch is based on your current `HEAD`, so whatever commits you have on the branch you were on are now the starting point of `feature/shopping-cart`.

To verify:

```bash
git status
```

```
On branch feature/shopping-cart
nothing to commit, working tree clean
```

You're now on the new branch, ready to commit.

## `git checkout -b` — the older equivalent

```bash
git checkout -b feature/shopping-cart
```

```
Switched to a new branch 'feature/shopping-cart'
```

Output and effect are identical to `git switch -c`. The reason `git switch` exists at all is that `git checkout` has historically meant too many things — switch branches, restore files, detach HEAD — so Git split the file-restoration side off into [`git restore`](/git/reverting-commits) and the branch-switching side off into `git switch`.

`git checkout -b` is going to keep working for the foreseeable future, but new tutorials and IDE integrations are increasingly using `switch`.

## `git branch` — create without switching

```bash
git branch feature/shopping-cart
```

This creates the branch but leaves you on the branch you were already on. No output means it worked. To switch later:

```bash
git switch feature/shopping-cart
```

The two-step approach is useful when you're inside a long-running operation (a rebase, a bisect) and you want to bookmark the current point as a branch without leaving the operation.

## Creating from a specific starting point

By default, the new branch starts from your current `HEAD`. You can branch from anywhere by passing a starting point as the last argument.

From another branch:

```bash
git switch -c release/v2 main
```

From a specific commit:

```bash
git switch -c hotfix/login 4f1c2a8
```

From a tag:

```bash
git switch -c support/v1.4 v1.4.0
```

This is how you would, for example, start a hotfix branch from the exact commit that's running in production rather than from the tip of `main` (which may have moved on since the release).

## Branch naming conventions

Git accepts almost any name (no spaces, no `..`, no colons, no `~`, no `^`), but the conventions that actually matter are organizational. A few prefixes pay off:

| Prefix | Use for |
|---|---|
| `feature/` | New functionality (`feature/shopping-cart`) |
| `bugfix/` | Bug fixes that aren't urgent (`bugfix/login-typo`) |
| `hotfix/` | Urgent production fixes (`hotfix/payment-race`) |
| `release/` | Release-stabilisation branches (`release/v2.1`) |
| `chore/` | Maintenance, dependencies, refactors (`chore/upgrade-rails`) |

Why bother? Two reasons:

1. **Sorting.** `git branch -a` lists branches alphabetically. With prefixes, all the features cluster together and you can scan a long list at a glance.
2. **Automation.** Most deployment tools — including [DeployHQ](/git/deploy-websites-with-git) — can match branch patterns. A rule like "deploy every push to `release/*` to staging" only works if your branches are named consistently.

Use forward slashes between segments. Git treats the slash as a folder separator inside `.git/refs/heads/`, so `feature/shopping-cart` is genuinely organised in the file system, not just visually.

**Note:** Don't name a branch the same as an existing tag. Git allows it, but commands like `git checkout v1.0` become ambiguous, and you'll spend an hour figuring out why nothing behaves as expected.

## Working with remote branches

The branches you create locally exist only on your machine until you push them.

### Push a new branch to the remote

```bash
git push -u origin feature/shopping-cart
```

`-u` (short for `--set-upstream`) tells Git to remember that your local branch tracks the remote one. After this, plain `git push` and `git pull` work without arguments because Git knows where to send and fetch from.

```
* [new branch]    feature/shopping-cart -> feature/shopping-cart
Branch feature/shopping-cart set up to track remote branch feature/shopping-cart from origin
```

### Create a local branch that tracks an existing remote branch

A teammate has pushed a branch you want to work on. After fetching:

```bash
git fetch origin
git switch feature/shopping-cart
```

If a local branch by that name doesn't exist yet but a remote one does, Git creates the local branch automatically and sets up tracking — this is the most ergonomic way to "check out a remote branch".

For explicit control:

```bash
git switch -c feature/shopping-cart origin/feature/shopping-cart
```

Or, with the older command:

```bash
git checkout --track origin/feature/shopping-cart
```

### Clone a single branch from a remote

If you want to grab only a specific branch when cloning a repository — useful for very large repos or for CI jobs — pass `-b`:

```bash
git clone -b release/v2 --single-branch git@github.com:example/site.git
```

`--single-branch` tells Git not to fetch every other branch's history, which can dramatically reduce clone time on large projects. See [cloning an existing repository](/git/cloning-an-existing-repository) for the wider cloning options.

## Listing, renaming, and recovering branches

A quick run-through of the operations that go hand in hand with creation.

### List branches

```bash
git branch          # local branches only
git branch -r       # remote-tracking branches only
git branch -a       # both
git branch --merged # branches fully merged into HEAD (safe to delete)
```

The current branch is marked with a `*`.

### Rename a branch

```bash
git branch -m old-name new-name
```

If you're on the branch you want to rename, you can omit `old-name`:

```bash
git branch -m new-name
```

Renaming is local. To propagate to the remote, you delete the old remote branch and push the renamed one:

```bash
git push origin --delete old-name
git push -u origin new-name
```

See [the rename-a-branch FAQ](/git/faqs/rename-a-branch-in-git) for the edge cases.

### Recover a "lost" branch

If you deleted a branch and want it back, the reflog usually has you covered. Find the last commit on the branch:

```bash
git reflog
```

Look for the commit you remember being at the tip of the branch, then re-create:

```bash
git branch recovered-branch <hash>
```

The branch is back, pointing at the commit it was at when you deleted it. Git's reflog keeps entries for about 90 days by default, so most accidents are recoverable.

## Practical recipes

**"I want to start a new feature without leaving `main`" —** `git switch -c feature/<name>`. You're on the new branch and `main` is untouched on disk.

**"I need to start a hotfix from the exact commit in production, not the tip of `main`" —** `git switch -c hotfix/<name> <production-commit-hash>`. See also [cherry-picking commits](/git/cherry-picking-commits) for moving the fix back to other branches once it's tested.

**"My teammate pushed a branch and I want to work on it" —** `git fetch origin`, then `git switch <branch-name>`. Git creates the local branch and sets up tracking automatically.

**"I want to clone only one branch from a huge repo" —** `git clone -b <branch> --single-branch <url>`.

**"I created a branch with a typo in the name" —** `git branch -m <wrong-name> <right-name>` while you're on a different branch, or just `git branch -m <right-name>` while you're on it.

**"I want to bookmark the current commit as a branch without switching" —** `git branch <name>`. Useful before a `git reset` or a `git rebase` to keep an escape hatch.

These all work the same way against [GitHub](/blog/how-to-set-up-git-pull-deployments-with-deployhq), GitLab, and Bitbucket — branch creation is a local operation; `git push -u origin <branch>` is what propagates it to the remote.

## Quick reference

| Goal | Command |
|---|---|
| Create and switch (modern) | `git switch -c <name>` |
| Create and switch (older) | `git checkout -b <name>` |
| Create without switching | `git branch <name>` |
| Create from another branch | `git switch -c <new> <source-branch>` |
| Create from a commit hash | `git switch -c <new> <hash>` |
| Create from a tag | `git switch -c <new> <tag>` |
| Push and set upstream | `git push -u origin <name>` |
| Check out an existing remote branch | `git switch <name>` (after `git fetch`) |
| Clone a single branch | `git clone -b <name> --single-branch <url>` |
| List local branches | `git branch` |
| List all branches (local + remote) | `git branch -a` |
| Rename a branch | `git branch -m <old> <new>` |
| Recover a deleted branch | `git branch <name> <hash>` (from `git reflog`) |

## Next steps

- [Branching and merging](/git/branching-and-merging) — what to do with branches once you have them
- [Publishing local changes](/git/publishing-local-changes) — push your new branch to the remote
- [Cloning an existing repository](/git/cloning-an-existing-repository) — branch options when starting from scratch

---

A consistent branch-naming scheme pays off the moment you connect deployments to Git. [DeployHQ](https://www.deployhq.com/features/automatic-deployments) can match branch patterns like `release/*` or `main` and deploy automatically every time you push — so a new `feature/` branch can become a preview environment, and a merged `release/` branch can ship to production without anyone touching a control panel. [Start deploying free](https://www.deployhq.com/signup).
