If you clicked on this, chances are you've typed git commit -m "fixed stuff" more than once, pushed straight to main, and quietly hoped nothing broke. That's a common way to start with Git, and it works fine right up until two things need to happen at once.
This is a plain walkthrough of Git branching for people who don't want a tutorial that assumes they already know terms like "detached HEAD." (That message still confuses plenty of experienced developers too. We'll decode it properly later in this guide.) It assumes you know roughly what Git does. If you're still fuzzy on how Git and GitHub relate to each other, our Git vs GitHub post covers that first.
By the end, you'll understand not just the commands but why branching exists, in a way that's easy to remember. Every command below is real, so follow along in a scratch repository and you'll see the same results.
The Problem Branching Actually Solves
Let's start with the real problem, since jumping straight into commands without explaining why you'd use them is part of why branching feels confusing at the start.
Imagine you're working on a small app. It works, it's live, and now you want to add a dark mode toggle. Halfway through, you realize it's a three-day job. Meanwhile, a client reports a bug that needs fixing today.
If you're working directly on your main codebase (what Git calls the "main" or "master" branch), you're stuck. The dark mode code is half-written and broken, so you can't cleanly pause and fix the bug. Everything is tangled together in the same timeline.
This is the exact situation branching was built for: letting you work on multiple things at once without one messing up the other.
A branch is a separate line of development where you can experiment, build, and even break things without touching the stable version everyone else relies on. Once your work is solid, you merge it back in. If it doesn't pan out, you delete the branch and nothing was ever at risk.
Think of your main branch as the published version of a book. A branch is a draft where you scribble notes, cross things out, and rewrite chapters. Nobody sees the mess. They only see the final version once you're happy with it and it gets folded back into the book.
Why Not Just Save Multiple Copies of Your Folder Instead?
Fair question, and it's a common instinct before learning Git. You end up with folders like project-final, project-final-v2, and project-final-ACTUAL-final. It works for a day or two, then turns into a mess because it's hard to track what changed where.
Branches solve this because they're lightweight and live inside the same project. Nothing is being duplicated. You're just telling Git "remember this point, and let me build a separate timeline from here." Switching between timelines takes one command. Comparing them takes one command. Merging them usually takes one command too.
Let's Get Our Hands Dirty: Creating Your First Branch
Enough theory. To follow along, create a small practice repository, or use any repository you don't mind experimenting in. A branch needs a commit to point at, so we'll make one first:
mkdir branch-demo
cd branch-demo
git init
echo "body { background-color: white; }" > style.css
git add style.css
git commit -m "Add base styles"
Now check which branch you're on:
git branch
You'll see an asterisk marking your current branch:
* main
Older Git versions may call it master instead. It's the same thing with a different name (see the FAQ below).
Now create a new branch for that dark mode feature:
git branch feature/dark-mode
The slash is a naming convention we'll unpack later in this guide. To Git, feature/dark-mode is simply the branch's name. Run git branch again:
feature/dark-mode
* main
Both branches are listed, but the asterisk is still next to main. Creating a branch doesn't automatically move you onto it. You have to switch:
git switch feature/dark-mode
Switched to branch 'feature/dark-mode'
git checkout feature/dark-mode instead. That still works, but checkout is an older command that does too many different things at once (switching branches, restoring files, and more), which is exactly what confuses beginners. git switch was introduced in Git 2.23 specifically to make branch switching its own clear command. If git switch says the command doesn't exist, run git --version and update Git.
To create and switch in one move, which covers most real-world cases, use this instead of the two steps above (the branch already exists now, so you'd only run one or the other):
git switch -c feature/dark-mode
The -c flag means "create." Now make a change and commit it like normal:
echo "body { background-color: #1a1a1a; color: #ffffff; }" > style.css
git add style.css
git commit -m "Add dark mode styles"
Here's the part that trips people up the first time. Switch back to main and look at the file:
git switch main
cat style.css
body { background-color: white; }
Your dark mode changes are gone. Not deleted, just not there on this branch. Switch back and they reappear:
git switch feature/dark-mode
cat style.css
body { background-color: #1a1a1a; color: #ffffff; }
That "vanishing and reappearing" moment is usually when branching finally clicks. You're not editing one timeline anymore. You're hopping between separate ones, and Git keeps each one perfectly intact.
To see the whole picture, ask Git to draw it:
git log --oneline --graph --all
* 3f2a9c1 (HEAD -> feature/dark-mode) Add dark mode styles
* b7d41e0 (main) Add base styles
Your commit hashes will differ, but the shape will match. Each line is a commit. The labels in parentheses are branch names pointing at commits, and HEAD marks where you are right now.
A Live Demo: How Branches Relate to Each Other
Reading about commits and timelines is one thing. Seeing it move is another. Use the two buttons below to move HEAD between the branches and watch the working file change, exactly like it does in your terminal.
HEAD → main
HEAD → feature/dark-mode
body { background-color: white; }body { background-color: #1a1a1a; color: #ffffff; }That's the whole idea. Same project, same file, completely different content depending on which branch is checked out. Notice that C1 and C2 are shared by both branches. Only C3 belongs to the feature.
What Is HEAD? (And Why "Detached HEAD" Sounds Scarier Than It Is)
HEAD is Git's "you are here" marker. Normally it points at a branch, and that branch points at a commit. When you commit, the branch moves forward and HEAD comes along with it.
A detached HEAD happens when HEAD points straight at a commit instead of a branch. It usually happens when you check out an old commit or a tag, or run git switch --detach. Git prints a long warning, but nothing is broken. You're just standing on a commit with no branch name attached, so any new commits you make there aren't reachable from a branch and can get lost.
If you made work you want to keep, give it a branch right where you stand:
git switch -c feature/rescued-work
If you were only looking around, leave with git switch - or git switch main, and nothing is lost.
Now Comes the Fun Part: Merging Your Work Back
Once your dark mode feature is done and tested, you want it back in main so it becomes part of the official project. Switch to the branch you want to merge into first:
git switch main
git merge feature/dark-mode
Two Kinds of Successful Merge
What Git does next depends on whether main moved while you were working.
Fast-forward. If main has no new commits of its own since you branched, Git simply moves main's pointer forward to include your changes. You'll see something like this:
Fast-forward
style.css | 1 +
1 file changed, 1 insertion(+)
Before: C1 --- C2 --- C3 feature/dark-mode
|
main
After: C1 --- C2 --- C3 feature/dark-mode, main
Merge commit. If main did get new commits while you were working, but they touched different lines than yours, Git combines both histories automatically and records a new "merge commit" to tie them together:
Merge made by the 'ort' strategy.
style.css | 1 +
1 file changed, 1 insertion(+)
C1 --- C2 --- C4 ---- M main
\ /
`-- C3 ---' feature/dark-mode
Git may open a text editor to confirm the merge commit's message. Save and close it to continue. (Older Git versions say "recursive" instead of "ort," which is only the name of the merging algorithm.) Either way, no conflict and no drama.
git branch -d feature/dark-mode. Deleting a branch doesn't delete your commits. They're already living permanently inside main now. You're just cleaning up the label. If a branch isn't merged yet, -d refuses to delete it. The capital -D forces it, so only use that when you truly want to throw the work away.
What If Two Branches Changed the Same Lines?
This is the scenario people fear most: the dreaded "merge conflict." It's less scary than its reputation suggests, once you've worked through one or two.
Say both main and your feature branch modified the same line in style.css. When you run git merge, Git can't decide which version is correct, so it stops and tells you:
Auto-merging style.css
CONFLICT (content): Merge conflict in style.css
Automatic merge failed; fix conflicts and then commit the result.
Run git status and Git lists the conflicted file under "Unmerged paths." Open it and you'll see something like this:
<<<<<<< HEAD
background-color: white;
=======
background-color: #1a1a1a;
>>>>>>> feature/dark-mode
Everything between <<<<<<< HEAD and ======= is what's currently on your branch (main, in this case). Everything between ======= and the branch name is what's coming in from the other branch. To resolve it:
- Edit the file to keep whichever version makes sense, or blend both.
- Delete the three marker lines (
<<<<<<<,=======, and>>>>>>>). - Save the file, then stage it and commit:
git add style.css
git commit -m "Resolve merge conflict in style.css"
That's it. There's no special conflict-resolution command. It's literally editing a text file to remove ambiguity, then committing like normal.
git merge --abort while the merge is still in progress and Git puts everything back the way it was before you started. You can always try again.
One thing worth knowing early: a merge conflict isn't Git telling you something went wrong. It's Git refusing to guess on your behalf when two people had different ideas about the same line. That's the safer outcome, since the alternative would be silently picking one version and hoping it was right.
You'll also hear about git rebase, another way to combine branches that rewrites commits to keep history in a straight line. It's powerful, but you don't need it to be productive with branches. Save it for later, and until then, don't rebase branches other people are using.
Sharing a Branch: Push, Then Pull Request
So far, everything has happened on your own machine. To share a branch with teammates (or back it up), push it to a remote:
git push -u origin feature/dark-mode
The -u flag links your local branch to its remote counterpart, so later git push and git pull commands need no extra arguments. On GitHub you'll then usually open a pull request (GitLab calls it a merge request): a request for others to review your branch and merge it into main. That review step belongs to the hosting platform, not to Git itself, and our Git vs GitHub post explains exactly where the line falls.
Two commands help when remotes are involved. git branch -a lists local branches and remote ones (shown as remotes/origin/...). And once a branch is merged, you can remove its remote copy with git push origin --delete feature/dark-mode. Many platforms also offer a "delete branch" button right after merging a pull request.
A Naming Convention That'll Save You Real Headaches
Here's something that isn't in most beginner guides but genuinely matters once you're working on real projects, especially with other people. Vague branch names like test, new-stuff, or fix become useless the moment you have more than three or four branches open. Nobody, including future you, can tell what they contain without opening each one.
A prefix-based pattern is common in team projects and tends to hold up well as a project grows. It matters even more once you're collaborating on a platform like GitHub or GitLab (if you're still deciding between the two, we compared them in GitLab vs GitHub in 2026):
| Prefix | Use case | Example |
|---|---|---|
feature/ |
New functionality | feature/dark-mode |
fix/ |
Bug fixes | fix/login-crash |
chore/ |
Maintenance, no user-facing change | chore/update-dependencies |
hotfix/ |
Urgent production fix | hotfix/payment-bug |
This small habit means anyone, including you six months from now, can glance at a branch list and immediately understand what's going on without opening a single file. It also pays off the moment a second person joins the project. A shared naming pattern is often the difference between a branch list that explains itself and one that needs a group chat to decode.
A few practical rules go along with it:
- Stick to lowercase letters, numbers, hyphens, and slashes. Git rejects branch names with spaces and with characters like
~,^,:,?,*, and[. - Keep names short but descriptive.
fix/login-crashbeats bothfixandfix/the-bug-where-the-login-page-crashes-on-mobile. - Add a ticket number if your team uses one, such as
fix/1234-login-crash. - Don't reuse a prefix as a branch name. Git stores branches like file paths, so once a branch called
featureexists, you can't createfeature/dark-mode. Git will refuse with an error about a ref that already exists.
Commands Most Beginners Never Learn, But Should
Most beginner guides stop at branch, switch, and merge. Three more commands change how you work once you know they exist.
1. git switch -: Jump Back to Your Previous Branch
git switch -
Just like cd - in your terminal takes you back to the previous directory, git switch - takes you back to whichever branch you were on before your last switch. If you're bouncing between main and a feature branch constantly, which happens a lot when reviewing code or checking something quickly, this single command saves you from typing the full branch name over and over.
2. git log --oneline --graph --all: See Your Branches
You've already met this one. It draws every branch and commit as a tree, right in the terminal. When you're unsure where things stand, run it before doing anything else.
3. git branch --merged: Find Branches Safe to Delete
git switch main
git branch --merged
This lists branches whose work is already included in your current branch, which makes them safe candidates for git branch -d. It's the fastest way to clean up after a busy week.
These are small things, but knowing them is what separates someone who just knows Git commands from someone who moves through Git quickly without thinking about it.
Putting It Together: A Typical Feature-Branch Workflow
Here's how the pieces fit into the routine most teams follow:
- Start from the latest main:
git switch main, thengit pull. - Create a branch for the work:
git switch -c feature/short-description. - Commit small, focused changes as you go.
- Share it when it's ready:
git push -u origin feature/short-description. - Open a pull request (or merge request) and respond to review comments.
- After it's merged, clean up:
git switch main,git pull, thengit branch -d feature/short-description.
Common Mistakes Beginners Make With Branching
A few things reliably trip people up early on. Here's what's going on and how to recover.
Forgetting Which Branch You're On
You make changes, commit them, and then realize you were on main the whole time instead of your feature branch. This happens to nearly everyone at some point. The fix depends on whether you've already committed.
If you haven't committed yet, you can just create the branch now, and your uncommitted changes will move with you:
git switch -c feature/oops-forgot-branch
If you already committed, you can move that last commit to a new branch and rewind main by one commit:
git branch feature/oops-forgot-branch
git reset --hard HEAD~1
The first line creates a new branch at your current commit, so the work is safe. The second moves main back one commit. Use HEAD~2 if you made two commits, and so on. Only do this if you haven't pushed main yet.
git reset --hard when you're absolutely sure what you're discarding. It rewrites your current branch to an earlier point, and it also throws away any uncommitted changes in your working files, which are gone for good. Run git status and git log first if you're unsure.
If you've made a bigger mess than this, like committing something you didn't mean to or needing to undo several steps at once, our full guide on How to Undo Almost Any Git Mistake covers more of these situations.
Branching Off an Outdated Main
If your main branch has moved forward since you last pulled, but you branch off your old local copy, you'll end up merging in stale code later and creating unnecessary conflicts. Before starting new work, it's worth running:
git switch main
git pull
git switch -c feature/new-thing
Three extra seconds now can save an annoying conflict cleanup later.
Merging in the Wrong Direction
git merge always brings the named branch into the branch you're currently on. Running git merge main while on your feature branch pulls main's changes into the feature, which is handy for catching up but doesn't ship anything. To ship the feature, switch to main first and run git merge feature/dark-mode. When in doubt, glance at the asterisk in git branch before merging.
Quick Reference: Commands Covered in This Guide
| Command | What it does |
|---|---|
git branch |
Lists local branches |
git branch branch-name |
Creates a branch without switching to it |
git switch branch-name |
Moves to an existing branch |
git switch -c branch-name |
Creates a branch and switches to it in one step |
git switch - |
Jumps back to your previous branch |
git merge branch-name |
Merges the named branch into your current branch |
git merge --abort |
Cancels an in-progress merge and restores the previous state |
git branch -d branch-name |
Deletes a branch that's already merged |
git branch -D branch-name |
Force-deletes a branch, merged or not |
git branch -m old-name new-name |
Renames a branch |
git branch --merged |
Lists branches already merged into the current one |
git branch -a |
Lists local and remote branches |
git log --oneline --graph --all |
Draws all branches and commits as a tree |
git push -u origin branch-name |
Pushes a branch and links it to its remote counterpart |
Frequently Asked Questions
Do I need to create a branch for every tiny change?
Not necessarily. For a solo weekend project, working directly on main is fine. Branching becomes useful once you're juggling multiple features, working with a team, or need your main code to always stay deployable. As a rule of thumb: if a change takes more than one commit, or touches something risky, branch it.
What's the difference between "main" and "master"?
No functional difference. They're just names. Git used to default to master as the primary branch name, and most tools and hosting platforms now default to main instead. You can rename yours anytime with git branch -m master main. If the branch already exists on a remote, push the new name and update the remote's default branch too. To make new repositories start with main, run git config --global init.defaultBranch main.
Can I switch branches if I have uncommitted changes?
Often, yes. Your uncommitted changes come along to the new branch as long as they don't conflict with files that differ between the two branches. If they do, Git stops you and suggests committing first or using git stash, which temporarily shelves your changes so you can bring them back later with git stash pop.
What happens to a branch after I merge and delete it?
Nothing bad. Your commits are already merged into the target branch permanently, so deleting the branch just removes the label pointing to them. The commit history stays intact and visible in git log.
Is it bad to have a lot of branches open at once?
Not inherently, but it gets messy fast without a naming convention (see the naming section above) and occasional cleanup. A good habit is deleting branches right after they're merged, so your branch list only shows work that's actually in progress. git branch --merged makes that cleanup quick.
Should I use a GUI tool instead of the terminal for branching?
Either works fine, and tools like GitHub Desktop or the Git panel in VS Code can make branching feel more visual. That said, learning the terminal commands first genuinely helps, because GUI tools eventually hit edge cases (like conflicts) where understanding what's happening underneath makes troubleshooting much faster.
What's the difference between merge and rebase?
Merging combines two branches and preserves the full history, sometimes with an extra merge commit. Rebasing replays your commits on top of another branch to create a straight, linear history, but it rewrites those commits in the process. As a beginner, stick with merge, and avoid rebasing any branch that other people are already using.
How do I rename a branch?
Run git branch -m old-name new-name. To rename the branch you're currently on, you can leave out the old name: git branch -m new-name. If you've already pushed the branch, you'll also need to push the new name and delete the old one on the remote.
The Part That Doesn't Show Up in the Command List
Every command above is easy to look up again later. What's harder to learn is knowing when to use them, and that only comes from one place: making a branch before you're sure you need one.
Here's a simple way to think about it: a branch costs you almost nothing to create and almost nothing to throw away. That's the whole point. Most tools ask you to commit to an idea before you know if it's good. Branching lets you test the idea first, for free, and only commit once you're sure.
So the fastest way to actually learn this isn't rereading the commands. It's opening a terminal, running git switch -c whatever-you-want on a real project, and trying something you're not sure will work. If it works, merge it. If it doesn't, delete it. Either way, you'll understand branching better after those five minutes than after another read-through of this page, because you'll have felt the real reason branching exists: the freedom to be wrong without it costing you anything.