Committing straight on main works for a solo throwaway repo. On a shared project it turns every experiment into everyone else’s problem. Branches give you a private line of commits: build the feature, push it, merge when ready, then delete the branch.

Day-one snapshots live in Git Without the Panic. Conflict recovery lives in When Push Fails. This page is the missing loop between them: create, switch, publish, integrate, clean up.

Warm-up: branch lab

Reuse the bare-remote pattern so you can push branches without GitHub. From a scratch folder:

mkdir -p git-branch-lab
cd git-branch-lab
git init --bare remote.git
git clone remote.git app
cd app

echo 'App v1' > README.md
git add README.md
git commit -m "Initial README"
git branch -M main
git push -u origin main

Everything below runs inside app/ unless noted. Delete git-branch-lab when you finish.

Note: Prefer git switch for changing branches. Older docs use git checkout; the switch forms below are clearer for day-to-day branch moves.

Mental model: a branch is a movable label

A branch name is a pointer to a commit. main usually tracks the agreed “shipped” line. A feature branch starts at the same commit, then moves forward as you commit — main stays put until you merge.

List local branches (the current one is marked with *):

git branch
* main

See local and remote-tracking branches:

git branch -a

Note: Stay on main for hotfixes everyone agreed to. Start new work with git switch -c so unfinished commits never land on the shared default by accident.

Hero use case: feature work off main

You need a login page. Do not pile those commits onto main.

Create and switch

Create feature/login from your current commit and switch to it in one step:

git switch -c feature/login
git branch

You should see * feature/login. Make a change and commit on that branch only:

echo 'Login form placeholder' > login.txt
git add login.txt
git commit -m "Add login placeholder"

main still points at the earlier README commit. Confirm with a short graph:

git log --oneline --decorate --graph -n 5

Push the branch with upstream

Publish the branch and set tracking so later pushes are just git push:

git push -u origin feature/login

-u links local feature/login to origin/feature/login. Check remotes:

git branch -vv

Note: Pushing a feature branch does not change main on the remote. Teammates (or you, later) merge when the work is ready — or open a pull request. PR review is a later topic; here we merge locally so the CLI loop is complete.

Merge back into main

Switch home, update main from the remote, then merge the feature:

git switch main
git pull origin main
git merge feature/login
git push origin main

If Git reports conflicts, resolve them the same way as in When Push Fails — edit markers, git add, finish the merge commit, then push.

After a clean merge, main includes the login commit. The feature branch label still exists until you delete it.

Delete the branch locally and remotely

When the work is on main and you no longer need the label:

git branch -d feature/login
git push origin --delete feature/login

-d refuses to delete if the branch is not fully merged (safety). Force with -D only when you intentionally abandon unmerged work.

Note: Deleting a remote branch does not delete commits that already landed on main. It only removes the extra name on the remote.

Switch between branches safely

List branches, then move:

git switch main
git switch feature/login

If Git blocks the switch because you have uncommitted edits that would be overwritten, either commit them on the current branch, or stash (a short companion skill for later), or discard with git restore if the edits are disposable.

Create a branch without switching (rare, but useful):

git branch feature/payments
git switch feature/payments

git switch -c remains the everyday default.

Keep main current while you work

Long-lived feature branches drift. Periodically bring remote main into your feature branch with a merge (simple, safe for shared feature branches):

git switch feature/login
git fetch origin
git merge origin/main

Conflicts here are the same markers you already know. Rebase can replay your commits on top of main instead — powerful, but easy to misuse on a branch others already pulled. Leave rebase for a follow-up page.

Quick reference card

GoalCommand
List local branchesgit branch
List local + remotegit branch -a
Create and switchgit switch -c feature/name
Switch existinggit switch main
Push new branch + trackgit push -u origin feature/name
Update local maingit switch main then git pull origin main
Merge feature into maingit merge feature/name
Delete local (merged)git branch -d feature/name
Delete remote branchgit push origin --delete feature/name
See graphgit log --oneline --decorate --graph -n 10

Practice drills

Inside git-branch-lab/app (recreate the bare remote and clone if needed):

  1. From main, create feature/docs, add a line to README.md, commit, and push with upstream tracking.
  2. Switch to main, merge feature/docs, and push main.
  3. Delete feature/docs locally with -d and delete it on the remote.
  4. Create feature/wip, commit once, switch to main without merging, and confirm git log on main does not show the WIP commit.
  5. Merge origin/main into an active feature branch after adding a new commit on main from another clone (or by switching, committing on main, pushing, then merging into the feature).

Solid answers — clear and portable:

git switch main
git switch -c feature/docs
echo 'Docs link' >> README.md
git add README.md && git commit -m "Add docs link"
git push -u origin feature/docs

git switch main
git pull origin main
git merge feature/docs
git push origin main

git branch -d feature/docs
git push origin --delete feature/docs

git switch -c feature/wip
echo 'wip' > wip.txt
git add wip.txt && git commit -m "WIP"
git switch main
git log --oneline -n 3

# 5: after main advanced on origin
git switch feature/wip
git fetch origin
git merge origin/main

If those five feel routine, you already practice the team habit: work on a named branch, publish it with -u, merge into main when ready, and delete the label. Snapshot basics remain in Git Without the Panic; conflict recovery remains in When Push Fails. Next skills when you need them are rebase (update a feature branch without a merge commit) and pull-request reviews on the host.