| |

Git 3b 🧩 Basic Branch Commands

⏱️ Estimated read: 10 min | |

3b Basic Branch Commands

Branches are Git’s working unit — you create one for each feature, fix, or experiment, then merge it back when done. The commands that manage branches are the ones you’ll type most often. This chapter covers them systematically: listing, creating, switching, merging, and deleting.

Key point: Most modern Git usage replaces git checkout with git switch for branch changes and keeps git checkout for file restores. switch is clearer and less error-prone — prefer it when you can.


Listing all local branches

The git branch command lists local branches.

git branch

Output:

  bugfix/issue-123
* feature/login
  main

The asterisk marks the current branch.

Useful options:

CommandShows
git branchLocal branches
git branch -aLocal + remote-tracking
git branch -vBranches with last commit
git branch -vvBranches with tracking info
git branch --mergedMerged into current
git branch --no-mergedNot merged into current
git branch --statWith size/statistics

git branch -a is the most useful variant — it shows everything at once:

* main
  feature/login
  remotes/origin/main
  remotes/origin/feature/login

Why so many flags: -a shows remotes, -v shows commit info, --merged/--no-merged tell you what’s safe to delete. Together they give you the full picture of what branches exist and what they contain.


Creating new branches

There are two ways to create a branch: with git branch (creates but doesn’t switch), or with git checkout -b / git switch -c (creates and switches).

Create without switching:

git branch feature/new-ui
git branch develop

You stay on your current branch. The new one points at the same commit.

Create and switch — checkout:

git checkout -b feature/user-profile

Older syntax. Works everywhere.

Create and switch — switch (modern):

git switch -c feature/user-profile

Newer syntax, clearer intent.

Create from a specific commit:

git branch feature/new-ui abc1234
git switch -c feature/new-feature abc1234

The new branch starts at that commit, not at the current position.

Create from a remote branch:

git branch feature/new-ui origin/main
git switch -c feature/new-feature origin/main

This starts the local branch from the remote’s state, not from your local main.

Why the -c shortcut matters: Creating a branch without switching is rarely what you want. -c combines the two steps, which is why it’s the common form.


Switching between branches

Old method — git checkout:

git checkout main
git checkout feature/login
git checkout develop

Works everywhere, but checkout is overloaded — it also restores files.

New method — git switch:

git switch main
git switch feature/login
git switch develop

switch only changes branches. Clearer, safer, less ambiguous.

Why switch is better:

Aspectcheckoutswitch
PurposeBranches and filesBranches only
ClarityAmbiguousExplicit
Error messagesGenericBranch-specific
Accidental branch creationPossibleNo

Switching with options:

# Force switch and reset working directory
git switch --force-create branch-name

# Switch and set upstream tracking
git switch -c branch-name --track origin/branch-name

The --force-create option is destructive — it resets your working directory, discarding uncommitted changes. Use it only when you’re sure.

Why uncommitted changes matter: Git refuses to switch branches if the switch would overwrite uncommitted work. That’s a feature — it protects you. Commit or stash before switching.


Merging branches

git merge combines another branch’s changes into your current branch.

git checkout main
git merge feature/user-profile

You must be on the target branch. git merge feature/user-profile while on main brings the feature branch into main.

Two types of merges:

Fast-forward — happens when the current branch is a direct ancestor of the branch being merged. Git simply moves the branch pointer forward. No merge commit.

Before:
main:    A---B---C
feature:         \
                  D

After git merge feature:
main:    A---B---C---D

Three-way merge — happens when branches have diverged. Git creates a merge commit with two parents.

Before:
main:    A---B---C
feature:         \
                  D---E

After git merge feature:
main:    A---B---C---F
              \     /
               D---E
TypeWhenResult
Fast-forwardNo divergencePointer moves
Three-wayBranches divergedMerge commit

Merge strategies and options:

# Squash all feature commits into one
git merge --squash feature-branch

# Always create a merge commit (no fast-forward)
git merge --no-ff feature-branch

# Use a specific strategy
git merge --strategy-option recursive

Why --no-ff: It preserves the fact that a branch existed. Fast-forward merges lose that context — the history looks flat. --no-ff keeps the branch structure visible.

Why --squash: It collapses all the feature branch’s commits into a single staged change. You then commit once. Useful for cleanup when the feature branch has messy commits.

Why merge types matter: Fast-forward gives clean history; three-way preserves context. Choose based on the workflow — many teams use --no-ff on shared branches and fast-forward on private ones.


Deleting branches

Safe delete — git branch -d:

git branch -d feature/user-profile
git branch -d bugfix/issue-123

Refuses to delete if the branch isn’t fully merged. This prevents losing unmerged work.

Force delete — git branch -D:

git branch -D feature/user-profile
git branch -D bugfix/issue-123

Deletes regardless of merge state. The unmerged commits become unreachable — and eventually garbage-collected.

Why the two flags: -d protects you. -D is an override for cases where you know the branch’s work is either merged elsewhere or intentionally discarded.

Deleting remote branches:

git push origin --delete feature/user-profile

Alternative syntax:

git push origin :feature/user-profile

Both do the same thing — the colon form means “push nothing to this branch, which deletes it.”

Delete multiple remote branches at once:

git push origin --delete feature/branch1 feature/branch2

Why remote branches need a push: A remote branch lives on the server. Deleting it requires telling the server, which is what git push origin --delete does.


Advanced branch operations

Setting upstream tracking:

# Create a branch tracking a remote branch
git switch -c feature/new-feature origin/main

# Set upstream after creating a branch
git branch --set-upstream-to=origin/feature/new-feature

Once upstream is set, git push and git pull work without arguments.

Renaming branches:

# Rename the current branch
git branch -m new-name

# Rename a specific branch
git branch -m old-name new-name

The -m flag moves (renames) the branch. Simple and safe.

Listing with more detail:

# Last commit info
git branch -v

# Tracking info
git branch -vv

# Statistics
git branch --stat

git branch -vv shows tracking relationships — useful when you’re working with remotes.

Why upstream matters: Without tracking, git push needs a remote and branch name every time. With tracking set, git push just works.


Best Practices and Tips

1. Verify before deleting — run git branch -v to check whether a branch is merged. Deleting unmerged work is permanent.

2. Use descriptive names — branch names should say what they contain:

feature/user-login
bugfix/payment-processing
hotfix/critical-security-issue

3. Keep branches small and focused — one feature, one fix per branch. Large branches are hard to review and merge.

4. Regular cleanup — delete merged branches regularly. A repository with 50 stale branches is harder to navigate than one with 5.

5. Prefer git switch over git checkout — clearer intent, better errors, fewer surprises.


Quick Reference

Listing Branches

CommandShows
git branchLocal branches
git branch -aLocal + remote
git branch -vWith last commit
git branch -vvWith tracking
git branch --mergedMerged into current
git branch --no-mergedNot merged
git branch --statWith statistics

Creating Branches

CommandAction
git branch NAMECreate only
git checkout -b NAMECreate + switch
git switch -c NAMECreate + switch (modern)
git branch NAME COMMITFrom specific commit
git switch -c NAME origin/BRANCHFrom remote

Switching Branches

CommandAction
git checkout NAMESwitch (old)
git switch NAMESwitch (modern)
git switch --force-create NAMEForce switch
git switch -c NAME --track origin/NAMETrack remote

Merging Branches

CommandAction
git merge NAMEMerge into current
git merge --no-ff NAMEAlways merge commit
git merge --squash NAMESquash into one commit
git merge --strategy-option recursiveUse strategy

Deleting Branches

CommandAction
git branch -d NAMESafe delete
git branch -D NAMEForce delete
git push origin --delete NAMEDelete remote
git push origin :NAMESame, alternative
git push origin --delete B1 B2Multiple

Advanced

CommandAction
git branch -m NEWRename current
git branch -m OLD NEWRename specific
git branch --set-upstream-to=origin/NAMESet tracking

Merge Types

TypeWhenResult
Fast-forwardLinearNo merge commit
Three-wayDivergedMerge commit
SquashManualSingle commit
No-FFForcedAlways merge commit

Common Pitfalls

PitfallProblemSolution
Uncommitted changesSwitch failsCommit or stash first
Wrong target for mergeWrong branch updatedCheck git branch first
Delete unmerged branchLost workUse -d unless sure
Force switchLoses changesAvoid unless intended
Ambiguous checkoutWrong actionUse switch and restore
No upstream setgit push needs argsgit branch --set-upstream-to
Stale branchesClutterDelete merged ones
Merging without testingBroken mainTest before merge

Real-World Examples

1. List local branches

git branch

Shows branches with * on current.

2. List all branches

git branch -a

Includes remotes.

3. List with tracking

git branch -vv

Shows upstream relationships.

4. Create a branch

git switch -c feature/login

Create and switch.

5. Create without switching

git branch feature/login

Stays on current branch.

6. Switch branches

git switch main

Modern syntax.

7. Merge a branch

git switch main
git merge feature/login

Target the destination, then merge.

8. Merge without fast-forward

git merge --no-ff feature/login

Forces a merge commit.

9. Delete a merged branch

git branch -d feature/login

Safe delete.

10. Delete a remote branch

git push origin --delete feature/login

Removes it from the remote.

11. Rename a branch

git branch -m new-name

Renames the current branch.

12. Set upstream tracking

git branch --set-upstream-to=origin/feature/login

Enables bare git push and git pull.

13. Find merged branches

git branch --merged

Safe to delete.

14. Find unmerged branches

git branch --no-merged

Do not delete yet.

15. Clean up many branches

git branch --merged | grep -v main | xargs git branch -d

Delete all merged branches except main.


Visual: Listing Branches

┌──────────────────────────────────────────────┐
│  git branch                                  │
│                                              │
│    bugfix/issue-123                          │
│  * feature/login       ← current branch      │
│    main                                      │
│                                              │
├──────────────────────────────────────────────┤
│  git branch -a                               │
│                                              │
│  * main                                      │
│    feature/login                             │
│    remotes/origin/main                       │
│    remotes/origin/feature/login              │
│                                              │
└──────────────────────────────────────────────┘

Visual: Create, Switch, Merge

┌──────────────────────────────────────────────┐
│  git switch -c feature/login                 │
│                                              │
│  main ──► A ──► B                            │
│                  \                           │
│                   feature/login (HEAD)       │
│                                              │
├──────────────────────────────────────────────┤
│  Work and commit on feature                  │
│                                              │
│  main ──► A ──► B                            │
│                  \                           │
│                   C ──► D                    │
│                        ▲                     │
│                        HEAD                  │
│                                              │
├──────────────────────────────────────────────┤
│  git switch main && git merge feature/login  │
│                                              │
│  main ──► A ──► B ──► C ──► D                │
│                              ▲               │
│                              HEAD            │
│                                              │
└──────────────────────────────────────────────┘

Visual: Merge Types

┌──────────────────────────────────────────────┐
│  Fast-forward                                │
│                                              │
│  main:    A ──► B                            │
│  feature: A ──► B ──► C ──► D                │
│                                              │
│  After merge:                                │
│  main:    A ──► B ──► C ──► D                │
│                                              │
│  No merge commit — pointer moves forward     │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│  Three-way merge                             │
│                                              │
│  main:    A ──► B ──► E ──► F                │
│  feature: A ──► B ──► C ──► D                │
│                                              │
│  After merge:                                │
│  main:    A ──► B ──► E ──► F ──► M          │
│                      \           /           │
│                       C ──► D ──┘            │
│                                              │
│  M is a merge commit (two parents)           │
│                                              │
└──────────────────────────────────────────────┘

Visual: Delete Safety

┌──────────────────────────────────────────────┐
│  git branch -d feature/login                 │
│                                              │
│  Merged?                                     │
│       │                                      │
│    yes│           no                         │
│       ▼            ▼                         │
│   deleted      refuses                       │
│                                              │
│  Safe — protects unmerged work               │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│  git branch -D feature/login                 │
│                                              │
│  Deletes regardless of merge status          │
│                                              │
│  ⚠️  Unmerged commits become unreachable      │
│                                              │
└──────────────────────────────────────────────┘

Visual: checkout vs switch

┌──────────────────────────────────────────────┐
│  git checkout                                │
│                                              │
│  Branches:  git checkout feature             │
│  Files:     git checkout -- file.txt         │
│  Create:    git checkout -b new              │
│                                              │
│  Overloaded — one command, many meanings     │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│  git switch                                  │
│                                              │
│  Branches:  git switch feature               │
│  Create:    git switch -c new                │
│                                              │
│  Single purpose — switching branches only    │
│                                              │
└──────────────────────────────────────────────┘

Summary

CommandPurpose
git branchList local branches
git branch -aList local + remote
git branch -vWith last commit
git branch -vvWith tracking
git branch NAMECreate without switching
git switch -c NAMECreate and switch
git switch NAMESwitch branches
git merge NAMEMerge into current
git branch -d NAMESafe delete
git branch -D NAMEForce delete
git push origin --delete NAMEDelete remote
git branch -m NEWRename

Key takeaways:

  • git branch lists local branches; -a includes remotes, -v shows commits, -vv shows tracking
  • git branch NAME creates a branch but doesn’t switch; git switch -c NAME does both
  • git switch is the modern replacement for git checkout when changing branches
  • git merge brings another branch into your current one — you must be on the target
  • Fast-forward merges just move the pointer; three-way merges create a merge commit
  • Use --no-ff to force a merge commit and preserve branch context
  • Use --squash to collapse a feature branch into a single commit
  • git branch -d refuses to delete unmerged work; -D forces it
  • Remote branches are deleted with git push origin --delete NAME
  • git branch -m renames branches
  • Set upstream tracking so git push and git pull work without arguments
  • Prefer git switch over git checkout — clearer and safer
  • Keep branches small, focused, and named by purpose
  • Verify before deleting — unmerged branches lose work permanently

Remember: Branches are the working unit of Git. Create one per task, switch between them freely, merge when ready, delete when done. Prefer switch for changing branches and keep checkout for restoring files. Use -d for safe deletes, -D when you know what you’re doing, and --no-ff when you want a merge commit. Master these commands and every Git workflow becomes manageable.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!