64 64. Branches and Merging
So far, every commit you made went onto one straight line of history. Branches let you work on a new idea in a separate line of commits without disturbing the working version of your project, and merging brings the finished work back. Branches are the foundation of everything else in this part of the book: teamwork, pull requests, and running several AI agents at once.
64.1 What is a branch?
Each commit remembers the commit that came before it, so your commits form a chain. A branch is simply a movable label that points at one commit: the newest commit of that line of work. When you commit on a branch, Git moves the label forward to the new commit automatically.
The branch you have used all along is called main. Nothing makes it special except a convention: main holds the working, “official” version of the project.
A---B---C <- main
(Each letter is a commit; A is the oldest.)
Git also keeps a special label called HEAD, which means “the branch you are on right now”. When Git’s output says HEAD -> main, read it as “you are on main”.
Creating a branch is instant. Git does not copy your files; it only writes down a new label.
64.2 Why use branches?
- Try ideas safely. If an experiment fails, delete its branch;
mainwas never touched. - Keep
mainworking. Half-finished work lives on its own branch until it is ready. - Work on several things at once. Use one branch per feature or bug fix and switch between them whenever you like.
- Work with others (people or AI agents). Each one works on their own branch, and the branches are combined later (see the next pages).
64.3 Creating and listing branches
The examples continue Maria’s git-tutorial-project from the earlier Git pages (it is already on GitHub, see Putting Your Repository on GitHub). Her README says the contact page is next, so she’ll build it on a branch. Your commit IDs will be different, and Git’s messages can vary slightly between versions.
git branch lists your branches. The * marks the one you are on:
git branchOutput:
* main
To create a new branch and switch to it, use git switch -c (-c means “create”):
git switch -c add-contact-page
git branchOutput:
Switched to a new branch 'add-contact-page'
* add-contact-page
main
The new branch starts at the commit you were on, so for now both labels point to the same commit:
A---B <- main, add-contact-page (HEAD)
Branch names cannot contain spaces. Use short, lowercase words joined with hyphens that describe the work, such as add-contact-page or fix-login-bug.
64.4 Committing on a branch
New commits always go onto the branch you are on. Maria creates a new file, src/contact.html:
<!DOCTYPE html>
<html>
<head>
<title>Contact</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<h1>Contact Me</h1>
<p>Email: maria@example.com</p>
</body>
</html>and commits it:
git add src/contact.html
git commit -m "Add contact page"Output:
[add-contact-page e5b1cce] Add contact page
1 file changed, 11 insertions(+)
create mode 100644 src/contact.html
Notice the branch name in square brackets. Next, in src/index.html she replaces the line <p>Contact: maria@example.com</p> with a link, <p><a href="contact.html">Contact me</a></p>, and commits again with the message “Link to contact page from home page”. Now add-contact-page has moved ahead while main stayed where it was:
A---B <- main
\
'---C---D <- add-contact-page (HEAD)
64.5 Switching branches changes your files
Watch what happens to the src folder when Maria switches back to main:
git switch main
ls srcOutput:
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
index.html
contact.html is gone! It isn’t lost: it lives in the add-contact-page branch. When you switch branches, Git rewrites the files in your project folder to match that branch. Run git switch add-contact-page and contact.html comes back. (If a file is open in your editor, the editor shows the new version.)
The line Your branch is up to date with 'origin/main' compares your main with origin/main, Git’s record of where main was on GitHub the last time you pushed or pulled.
If you have uncommitted changes that switching would overwrite, Git refuses to switch:
error: Your local changes to the following files would be overwritten by checkout:
src/index.html
Please commit your changes or stash them before you switch branches.
Aborting
Uncommitted changes that don’t clash are carried along to the other branch, which is confusing. The simple habit: run git status and commit before you switch.
64.6 Merging a branch
To merge, first switch to the branch that should receive the work, then name the branch whose work you want to bring in. Maria is back on add-contact-page, so:
git switch main
git merge add-contact-pageOutput:
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
Updating c0d14e5..14b4ab2
Fast-forward
src/contact.html | 11 +++++++++++
src/index.html | 2 +-
2 files changed, 12 insertions(+), 1 deletion(-)
create mode 100644 src/contact.html
64.6.1 Fast-forward merge
main had no new commits since the branch started, so Git did the simplest possible thing: it slid the main label forward to D. This is called a fast-forward merge. No new commit is created.
A---B---C---D <- main (HEAD), add-contact-page
64.6.2 Merge commit
Often both branches move. Maria creates a branch style-links, adds a rule to css/style.css that colors links dark blue, and commits (E). Then she switches back to main, updates the status line in README.md, and commits there too (F):
A---B---C---D-----F <- main (HEAD)
\
'---E <- style-links
Now Git can’t just slide a label forward. Instead, it creates a new commit:
git merge style-linksGit first opens your text editor with a ready-made message, Merge branch 'style-links'. Accept it by saving and closing the editor (in nano: Ctrl+O, Enter, Ctrl+X, as when you pulled on Cloning and Pulling). If you see a screen full of ~ symbols, Git opened the Vim editor: type :wq and press Enter. To skip the editor entirely, run git merge --no-edit style-links instead.
Output:
Merge made by the 'ort' strategy.
css/style.css | 4 ++++
1 file changed, 4 insertions(+)
(ort is just the name of Git’s merging method.) The new commit M is a merge commit: it combines both lines of work and has two parent commits.
A---B---C---D-----F-----M <- main (HEAD)
\ /
'---E---' <- style-links
Git combined the two branches automatically because they changed different files. When two branches change the same lines, you get a merge conflict - that’s the next page, Merge Conflicts.
64.7 Seeing all branches as a graph
git log --oneline --graph --allOutput (the oldest commits are cut off here):
* c2c5ec0 (HEAD -> main) Merge branch 'style-links'
|\
| * aa25d8c (style-links) Make links dark blue
* | 2fcaf72 Update project status in README
|/
* 14b4ab2 (add-contact-page) Link to contact page from home page
* e5b1cce Add contact page
* c0d14e5 (origin/main) Merge branch 'main' of https://github.com/YOUR-USERNAME/git-tutorial-project
|\
| * 0b2c2b7 Update README.md
* | b25b21a Increase line spacing
|/
* 647a9b5 Add project status to README
Read it from the bottom up. Each * is a commit, the lines on the left show where history splits and joins again, and the names in parentheses are branch labels. --all includes every branch, not only the one you are on. (The lower merge commit, c0d14e5, is the one Maria’s git pull created in Cloning and Pulling.)
64.8 Deleting a branch
Once a branch is merged, its label is no longer needed:
git branch -d add-contact-page style-linksOutput:
Deleted branch add-contact-page (was 14b4ab2).
Deleted branch style-links (was aa25d8c).
Only the labels are deleted; the commits remain part of main’s history. As a safety check, git branch -d refuses to delete a branch whose commits have not been merged, and the error message tells you that git branch -D (capital D) forces the deletion. Use -D only when you really want to throw that work away.
64.9 Pushing to GitHub
Everything so far happened only on Maria’s computer, as git status reminds her:
git statusOutput:
On branch main
Your branch is ahead of 'origin/main' by 5 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
A plain git push sends the updated main to GitHub:
git pushOutput (progress lines such as Enumerating objects... omitted):
To https://github.com/YOUR-USERNAME/git-tutorial-project.git
c0d14e5..c2c5ec0 main -> main
You can also push a branch before merging it, to back it up, to share it with teammates, or to open a pull request (see Pull Requests and Code Review). For example, Maria starts a branch add-footer, commits a page footer, and wants Sam to see it. The first push of a new branch needs -u origin and the branch name:
git push -u origin add-footerOutput (progress lines omitted):
To https://github.com/YOUR-USERNAME/git-tutorial-project.git
* [new branch] add-footer -> add-footer
branch 'add-footer' set up to track 'origin/add-footer'.
-u (short for --set-upstream) connects your local branch to the branch of the same name on GitHub, so from now on a plain git push or git pull works while you are on add-footer. On GitHub, the branch now appears in the branch menu of your repository.
git checkout, the older way to switch branches
git switch was added in Git 2.23 (2019). Before that, one command, git checkout, did both switching and restoring files. You will still see it in older tutorials, Stack Overflow answers, and sometimes in AI answers:
| Older command | Modern equivalent |
|---|---|
git checkout main |
git switch main |
git checkout -b new-branch |
git switch -c new-branch |
Both styles work. This book uses git switch because its name says what it does. Also, many older repositories call their main branch master instead of main; it works exactly the same way.
git stash
Sometimes you are in the middle of something and need to switch branches now, but your work isn’t ready to commit. git stash saves your uncommitted changes on a hidden “shelf” and makes your working directory clean:
git stashOutput:
Saved working directory and index state WIP on main: c2c5ec0 Merge branch 'style-links'
Now you can switch branches freely. When you come back, git stash pop puts the changes back into your files (and removes them from the shelf); git stash list shows what is on the shelf. Stashes are easy to forget, so for anything you want to keep, a commit on your branch is usually the better choice.
64.10 Branches and AI coding agents
As of Oct 6, 2026 · Official docs: Quickstart · Run parallel sessions with worktrees
Before you let Claude Code make a large or risky change, put the work on its own branch. You can type git switch -c try-new-layout yourself or ask in plain English; the Claude Code quickstart suggests prompts such as create a new branch called feature/quickstart. If you like the result, merge the branch into main. If you don’t, switch back to main and delete the branch with git branch -D try-new-layout; main never changed.
Claude Code can also run several sessions at the same time, each in its own folder (a worktree) on its own branch. That is covered in Git Worktrees and Parallel AI Agents.
64.11 Key takeaways
- A branch is a movable label pointing at a commit; HEAD marks the branch you are on.
git switch -c namecreates a branch and switches to it;git switch nameswitches to an existing one;git branchlists them.- Switching branches rewrites the files in your folder, so commit first.
- To merge, switch to the receiving branch and run
git merge other-branch. If the receiving branch hasn’t moved, you get a fast-forward; otherwise Git creates a merge commit. git branch -d namedeletes a merged branch’s label;git push -u origin namepublishes a new branch on GitHub.
Practice Questions
Easy
Question 1 · Short answer
In one or two sentences, what is a branch in Git?
Question 2 · Multiple choice
Which command creates a new branch named fix-typo and switches to it?
git branch fix-typogit switch -c fix-typogit switch fix-typogit merge fix-typo
Question 3 · Matching
Match each command with what it does.
| Command | What it does | |
|---|---|---|
1. git branch |
A. Delete the label of a merged branch | |
2. git switch main |
B. Show the history of every branch as a graph | |
3. git merge feature |
C. List your branches and mark the current one | |
4. git branch -d feature |
D. Move to the existing branch main |
|
5. git log --oneline --graph --all |
E. Bring the commits of feature into the current branch |
Question 4 · True or false
Creating a branch makes a complete copy of all your project files, so you should avoid creating many branches.
Question 5 · Short answer
In the output of git branch, what does the * mean? And in the output of git log, what does HEAD -> main mean?
Question 6 · True or false
To merge the branch add-faq into main, you should first switch to main.
Medium
Question 7 · Predict the output
Maria has only one branch, main. She runs:
git switch -c redesign
git branchWhat does git branch print?
Question 8 · Write the command
You are on the branch add-faq, and its work is finished. Write the commands to merge it into main and then delete the add-faq branch.
Question 9 · Predict
Maria committed a new file, src/contact.html, on the branch add-contact-page. She has not merged it yet. She runs git switch main and then ls src. Is contact.html listed? Has her work been lost?
Question 10 · Scenario
main points at commit B. Since creating feature from B, you made commits C and D on feature; nobody committed to main. You run git switch main and git merge feature. What kind of merge happens, and is a new commit created?
Question 11 · Scenario
This time, after you created feature, someone also added commit F to main, while feature has commit E. You merge feature into main. What does Git create, and where do the two branch labels point afterwards?
Question 12 · Read the output
Use this output to answer the questions below.
* c2c5ec0 (HEAD -> main) Merge branch 'style-links'
|\
| * aa25d8c (style-links) Make links dark blue
* | 2fcaf72 Update project status in README
|/
* 14b4ab2 Link to contact page from home page
- Which branch are you on?
- Which commit is the merge commit?
- Which commit was made on
style-linksbefore the merge, and which one was made onmain?
Question 13 · Write the command
You created a branch add-footer and made two commits on it. Write the command that publishes this branch on GitHub for the first time, so that a plain git push works for it later.
Question 14 · Multiple choice
After merging two branches into main on her laptop, Maria runs git status and sees Your branch is ahead of 'origin/main' by 5 commits. What does this mean?
- Five commits were lost during the merge.
- GitHub has five commits that Maria doesn’t have yet.
- Maria’s
mainhas five commits that are not on GitHub yet; she should rungit push. - Maria must delete five branches before she can push.
Hard
Question 15 · Find the bug
Sam edited src/index.html on his branch but didn’t commit. When he runs git switch main, he gets:
error: Your local changes to the following files would be overwritten by checkout:
src/index.html
Please commit your changes or stash them before you switch branches.
Aborting
Explain what Git is protecting him from, and give two different ways he could continue.
Question 16 · Scenario
Maria runs git branch -d experiment and Git refuses, saying the branch is not fully merged. What does that mean, and what should she do if (a) she wants to keep the work, or (b) she is sure the experiment was a failure?
Question 17 · True or false
After a branch has been merged into main, running git branch -d on it deletes the commits that were made on that branch.
Question 18 · Compare
Compare a fast-forward merge with a merge that creates a merge commit: when does each happen, and how does the history look afterwards?
Question 19 · Long answer
You want Claude Code to try a risky redesign of your website, and you’re not sure you’ll like the result. Describe a branch-based way to work that keeps main safe. Include what you do if you like the result and what you do if you don’t.