You are working on a stable version of your project. A feature idea comes up — something worth trying, but risky enough that you do not want it touching the working code until you know it works. Or you need to fix a bug while another change is already half-done.
Branches solve this. They let you maintain multiple independent lines of development in the same repository and merge them when you are ready.
A branch is a pointer to a commit. Not a copy of your files — a
pointer. Creating one is cheap: it is a single file in .git/refs/
containing a forty-character hash. When you commit on a branch, the
pointer moves forward to the new commit. When you switch branches,
git swaps the working tree and index to match what that pointer
references. Nothing is duplicated.
Creating a branch
git branch featureYou are still on main. Switch to the new branch:
git switch featureCreate and switch in one step:
git switch -c featureThe classic form works too and you will see it in older guides and existing scripts:
git checkout -b featuregit branch with no arguments lists all branches and marks the
current one with *.
Diverging
Make two commits on feature:
echo "feature line 1" >> notes.txt
git add notes.txt && git commit -m "add feature line 1"
echo "feature line 2" >> notes.txt
git add notes.txt && git commit -m "add feature line 2"Switch back to main and make one commit there:
git switch main
echo "main update" > main-notes.txt
git add main-notes.txt && git commit -m "add main-notes"Look at the history across both branches:
git log --oneline --graph --allOutput:
* 3f8a1d2 (HEAD -> main) add main-notes
| * b4c9e7f (feature) add feature line 2
| * a1d3c8e add feature line 1
|/
* 9e2b4f1 add hello.txt
(earlier commits omitted for clarity)The history has forked. main and feature share a common ancestor
and have diverged from it. git log --graph makes the topology
visible; this becomes important when you have multiple long-lived
branches.
Merging
Merge feature back into main:
git switch main
git merge featureFast-forward merge: if main has not moved since feature
branched, git simply advances main's pointer to the tip of
feature. No merge commit is created. The history is linear.
Merge commit: when both branches have new commits (as above), git cannot fast-forward. It creates a new commit with two parents — one from each branch. The graph shows the branches rejoining:
* 7d1a3b9 (HEAD -> main) Merge branch 'feature'
|\
| * b4c9e7f (feature) add feature line 2
| * a1d3c8e add feature line 1
* | 3f8a1d2 add main-notes
|/
* 9e2b4f1 add hello.txtThe merge commit records when the integration happened and what was integrated. Prefer it over rebasing for long-lived branches — the integration point should be visible in the log.
Resolving a conflict
Sometimes two branches modify the same part of the same file. Git cannot decide which version to keep, so it stops and asks you.
Manufacture a conflict: both branches write to the same line.
git switch -c conflict-demo
echo "branch A version" > shared.txt
git add shared.txt && git commit -m "branch A edit"
git switch main
echo "branch B version" > shared.txt
git add shared.txt && git commit -m "branch B edit"
git merge conflict-demoGit stops:
CONFLICT (content): Merge conflict in shared.txt
Automatic merge failed; fix conflicts and then commit the result.Open shared.txt. Git has written both versions with markers:
<<<<<<< HEAD
branch B version
=======
branch A version
>>>>>>> conflict-demoBetween <<<<<<< HEAD and ======= is what the current branch had.
Between ======= and >>>>>>> is what the incoming branch had.
Edit the file to the result you want, delete all three marker lines
(they are not valid in any file format — they are temporary
annotations), then:
git add shared.txt
git commitGit opens your editor with a pre-filled merge commit message. Save and close to finish. The editor is the merge tool. No external program required.
Cleaning up
Delete a merged branch:
git branch -d featureGit refuses if the branch has not been merged — the commits would
become unreachable. To delete a branch that has not been merged when
you are certain you do not need those commits, use -D. Do not use
-D reflexively; it discards work.