Chapter 5 of 10 · Git and GitHub for Beginners — Complete Free Tutorial

Branching and Merging

This fifth chapter of the Git and GitHub for Beginners series introduces the feature beginners love most: the branch. A branch lets you fork the project, try an idea freely, and either merge the result back or throw it away — all in seconds, and without ever putting your working copy at risk.

A branch is just a pointer

The idea that makes Git fast is disarmingly simple: a branch is not a copy of your files. It is a tiny marker that points at one commit — the tip of a line of work. Creating a branch takes no time and almost no space, because nothing is duplicated; the new pointer simply aims at the same commit you are already on. When you add a commit, the current branch's pointer moves forward to it while the others stay put. Your checked-out branch is the one HEAD points to, which is why status messages always open with “On branch main”.

Creating and switching branches

Two commands cover the whole job, and modern Git gives each one a clear name:

  • git switch -c <name> — create a branch and move onto it in one step.
  • git switch <name> — move to an existing branch.
  • git switch - — jump back to the branch you were on previously.

The older git checkout does both jobs (and famously does a third, dangerous thing: discarding file changes, which is why switch was introduced). Because switching rewrites your working tree to match the target branch, commit or stash unfinished work before you jump — Git will often refuse to switch and tell you exactly why.

Merging: fast-forward and three-way

Merging brings one branch's work into another. Git chooses between two strategies automatically:

SituationStrategyResult
The target branch has not moved since you branchedFast-forwardThe pointer simply slides forward — no merge commit, a perfectly straight line
Both branches have new commitsThree-way mergeGit combines both lines using their common ancestor and records a merge commit with two parents
Both sides changed the same linesConflictGit stops and asks you to choose — the whole next chapter

Neither strategy is better; they simply record history differently. Teams that want a linear-looking log often rebase before merging (we weigh that trade-off in Correcting History).

The feature branch workflow

The pattern used by most teams — and the core of GitHub flow — is deliberately boring:

  1. Start from an up-to-date main.
  2. Create a short-lived branch named after the change (fix-footer-links, feature-online-orders).
  3. Commit your work on that branch, ideally in small steps.
  4. Merge it back into main, usually via a pull request so a teammate can review it first.
  5. Delete the branch and start the next one.

Short-lived branches keep merges small, and small merges rarely conflict.

Cleaning up branches

Merged branches are clutter, so delete them once the work lands: git branch -d <name>. The lowercase -d is a safety catch — Git refuses to delete a branch whose commits are not reachable from your current one. If you are certain (for example, the work already lives on a remote branch), git branch -D forces it. Use git branch -a to list local and remote branches together, and get into the habit of pruning after every merge.

git switch -c feature-online-orders
git status

Expected output:

Switched to a new branch 'feature-online-orders'
On branch feature-online-orders
nothing to commit, working tree clean
git switch main
git merge feature-online-orders

Expected output:

Switched to branch 'main'
Updating 4c3b2a1..a1b2c3d
Fast-forward
 index.html      | 12 ++++++++++++
 bakery-menu.txt |  4 ++--
 2 files changed, 14 insertions(+), 2 deletions(-)
git branch

Expected output:

  fix-footer-links
* main

The asterisk always marks the branch you are on — a one-character reminder whenever you are about to commit.

Common mistakes

  • Committing straight onto main — one broken experiment now sits on the branch everyone depends on.
  • Switching branches with unsaved work — Git either blocks you or carries stray changes across; commit or stash first.
  • Letting branches live for weeks — long branches drift far from main and turn a five-minute merge into an afternoon.
  • Reaching for checkout -b out of habit — the old command does double duty and its file-discarding behaviour bites beginners; prefer switch -c.
  • Never deleting merged branches — a list with forty stale entries hides the work that actually matters.

Practice checklist

  1. Create a branch with git switch -c and confirm status reports the new name.
  2. Make two separate commits on the branch and view them with git log --oneline.
  3. Switch back to main and observe that your new commits are not visible there.
  4. Merge the branch and read the output: was it a fast-forward or a merge commit?
  5. Delete the branch with git branch -d, then try it on an unmerged branch to watch the safety catch fire.

What's next?

Merging is only smooth while both sides stay out of each other's way. When they do not, you get a conflict — and in Resolving Merge Conflicts you will learn that conflicts are a five-minute chore, not a disaster.

Quiz: Branching and Merging

5 questions · 70% to pass

Frequently Asked Questions

Git is a distributed version control system: it records snapshots of your project over time, lets you jump back to any point, branch off to try ideas safely, and merge other people's work. It is the industry standard — nearly every developer job assumes basic Git fluency — and it is the best insurance you can have against lost or broken code.

Git is the tool that runs on your machine and stores history locally; it works fully offline. GitHub is one of many websites (GitLab, Bitbucket) that hosts Git repositories in the cloud and adds collaboration features: pull requests, code review, issues and CI. You can use Git forever without GitHub — GitHub needs Git to be useful.

A repository (repo) is your project folder plus a hidden .git directory containing the entire history: every commit, branch and tag. It can be local (only on your computer) or remote (cloned on GitHub). Deleting .git loses the history but keeps your files; deleting the files while keeping .git leaves history with nothing to show.

git add moves changed files into the staging area — your draft of the next snapshot — so you can review exactly what will be recorded. git commit writes the staged snapshot permanently with a message describing why the change happened. Two steps by design: stage deliberately, commit intentionally.

A branch is a movable pointer to a commit — creating one costs almost nothing. Use a branch for every unit of work (feature, fix, experiment): it keeps unfinished code out of main, lets you review changes in a pull request, and makes abandoning a bad idea as simple as deleting the branch.

It depends on where the change lives. Unstaged edits: git restore file. Staged but uncommitted: git restore --staged file. Committed but not pushed: git reset --soft to keep changes staged. Already pushed: git revert commit — it writes a new commit that undoes the old one instead of rewriting shared history. The undoing chapter covers each case with commands.

A conflict happens when two branches changed the same lines and Git cannot choose automatically. Your file gets marked up with <<<<<<<, ======= and >>>>>>> sections; you edit it down to the correct result, remove the markers, then git add and git commit. Conflicts are normal, small and entirely fixable — they are not an error state.

A pull request (PR) is GitHub's proposal to merge one branch into another. It shows the diff, runs checks, and lets teammates comment line by line before the code lands. It is the centre of modern collaboration: work on a branch, open a PR, review, approve, merge.

Write the subject in imperative mood, under about 50 characters: "Add email validation to signup". Explain why, not what, in the body when it is not obvious. Many teams use Conventional Commits (feat:, fix:, docs:) so tools can generate changelogs — the workflows chapter shows the format in practice.

Secrets (API keys, .env files), dependencies (node_modules), build output (dist), and operating-system junk (.DS_Store, Thumbs.db). A .gitignore file excludes them from the start; the common mistakes chapter shows what to do if something already slipped into history.