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; main was never touched.
  • Keep main working. 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 branch

Output:

* main

To create a new branch and switch to it, use git switch -c (-c means “create”):

git switch -c add-contact-page
git branch

Output:

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 src

Output:

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.

Commit before you switch

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-page

Output:

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-links

Git 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 --all

Output (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-links

Output:

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 status

Output:

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 push

Output (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-footer

Output (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 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.

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 stash

Output:

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 name creates a branch and switches to it; git switch name switches to an existing one; git branch lists 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 name deletes a merged branch’s label; git push -u origin name publishes a new branch on GitHub.

Practice Questions

Easy

Question 1 · Short answer

In one or two sentences, what is a branch in Git?

A branch is a movable label (a name) that points at a commit - the newest commit of one line of work. When you make a new commit on that branch, Git moves the label forward to the new commit.

Question 2 · Multiple choice

Which command creates a new branch named fix-typo and switches to it?

  1. git branch fix-typo
  2. git switch -c fix-typo
  3. git switch fix-typo
  4. git merge fix-typo

b. -c tells git switch to create the branch first. git branch fix-typo (a) creates the branch but leaves you on your current branch. git switch fix-typo (c) only works if the branch already exists. git merge (d) combines branches; it doesn’t create one.

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

1 → C, 2 → D, 3 → E, 4 → A, 5 → B.

Question 4 · True or false

Creating a branch makes a complete copy of all your project files, so you should avoid creating many branches.

False. A branch is just a small label that points at a commit. Creating one is instant and copies nothing, so it’s fine to create a branch for every feature, fix, or experiment.

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?

Both tell you which branch you are currently on. In git branch, the * marks the current branch. In git log, HEAD -> main means HEAD (Git’s “you are here” marker) is pointing at the branch main.

Question 6 · True or false

To merge the branch add-faq into main, you should first switch to main.

True. git merge brings the named branch’s work into the branch you are on. So you switch to the receiving branch (git switch main) and then run git merge add-faq.

Medium

Question 7 · Predict the output

Maria has only one branch, main. She runs:

git switch -c redesign
git branch

What does git branch print?

  main
* redesign

Both branches exist, and the * shows she is now on redesign (Git lists branches in alphabetical order).

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.

git switch main
git merge add-faq
git branch -d add-faq

You must be on main (the receiving branch) when you merge. After the merge, git branch -d safely removes the label, because its commits are now part of main.

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?

No, contact.html is not listed, but nothing is lost. Switching branches rewrites the files in the project folder to match the branch you switch to, and main doesn’t contain that file yet. The file is safely stored in the add-contact-page branch; git switch add-contact-page brings it back.

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?

A fast-forward merge. Because main hasn’t moved since the branch started, Git simply slides the main label forward to D. No new commit is created, and Git prints Fast-forward.

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?

Git creates a merge commit (call it M) with two parents, F and E, so it contains the work of both branches. Git opens your editor for the merge message (unless you use --no-edit). Afterwards main points at M; feature still points at E, because merging moves only the branch you are on.

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
  1. Which branch are you on?
  2. Which commit is the merge commit?
  3. Which commit was made on style-links before the merge, and which one was made on main?
  1. main (HEAD -> main).
  2. c2c5ec0, “Merge branch ‘style-links’”. Its two parent lines join at the |\ below it.
  3. aa25d8c was made on style-links (it carries that label). 2fcaf72 was made on main while the branch was in progress. Both started from 14b4ab2.

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.

git push -u origin add-footer

origin is the GitHub remote, and -u (--set-upstream) connects your local add-footer to origin/add-footer, so later pushes and pulls on this branch need no extra arguments.

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?

  1. Five commits were lost during the merge.
  2. GitHub has five commits that Maria doesn’t have yet.
  3. Maria’s main has five commits that are not on GitHub yet; she should run git push.
  4. Maria must delete five branches before she can push.

c. origin/main is where main was on GitHub when Maria last talked to GitHub. “Ahead by 5” means her local main has five commits that GitHub doesn’t have, because merging happened only on her computer. git push sends them. Nothing was lost (a), it’s not GitHub that is ahead (b - that would say “behind”), and branches don’t block pushing (d).

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.

src/index.html is different on main, so switching would overwrite Sam’s uncommitted edits with main’s version - and uncommitted work can’t be recovered. Git refuses instead of destroying his work. He can:

  1. Commit the changes on his current branch (git add src/index.html, git commit -m "..."), then switch.
  2. Stash them (git stash), switch, and later return and run git stash pop.

(If he doesn’t want the edits at all, git restore src/index.html discards them - permanently.)

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?

It means experiment has commits that are not part of the branch she is on, so deleting the label would make that work hard or impossible to find again.

  1. To keep the work: merge it first (git switch main, git merge experiment), then git branch -d experiment succeeds.
  2. To throw it away: git branch -D experiment (capital D) forces the deletion. Its unmerged commits are then effectively lost, so be sure.

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.

False. Only the label is removed. The commits were merged into main and remain in its history (you can still see them with git log).

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?

  • Fast-forward: happens when the receiving branch (e.g. main) has no new commits since the other branch started. Git just moves the main label forward to the other branch’s latest commit. No new commit is created and history stays one straight line.
  • Merge commit: happens when both branches have new commits. Git creates a new commit with two parents that combines both lines of work. In git log --graph, history splits and joins again at the merge commit.

A good answer mentions the condition (has the receiving branch moved?), whether a new commit appears, and the shape of the history.

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.

  1. Commit (or otherwise clean up) your current work so git status is clean.
  2. Create a branch for the experiment: git switch -c redesign (or ask Claude Code to create one), and let Claude make and commit its changes there.
  3. Review and test the redesign (look at the diff, open the site).
  4. If you like it: git switch main, git merge redesign, then git branch -d redesign and git push.
  5. If you don’t: git switch main and git branch -D redesign. main never changed, so nothing needs to be undone.

A good answer stresses that all of Claude’s changes stay on the experiment branch until you decide to merge.

© 2025-2026 Y. Rosenthal. All rights reserved.