Post
Git from zero to hero: branches, merging and conflicts
Part 4 of the Git guide: what a branch really is, fast-forward and merge commits, how a three-way merge decides, and how to resolve a conflict calmly.
A branch is the label from part 2 that moves forward with each commit. This part uses one to try a pizza recipe in the recipe book without touching main.
Branches
git switch -c pizza
You’re now on pizza. Commits go there, and main stays put:
echo "# Pizza" > pizza.md
git add pizza.md
git commit -m "Add pizza recipe"
echo "Bake 20 minutes." >> pizza.md
git commit -am "Add baking time"
A branch costs almost nothing, so make one per idea.
git branch # list branches, * marks the current one
git switch main # go back; pizza.md disappears from the folder
git switch pizza # and it's back
What a branch really is
A branch isn’t a copy of your project. It’s a tiny file with one commit hash in it:
cat .git/refs/heads/main
cat .git/HEAD
9c1d4e2a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4
ref: refs/heads/pizza
The first file is the main branch: a hash, nothing else. The second is HEAD, saying “I’m on pizza”.
- Creating a branch writes one small file.
- Switching changes what HEAD says and swaps your working files for that snapshot.
- Deleting a branch removes the label. The commits stay, and
git reflogcan find them until Git cleans up unreachable commits after a grace period.
Merging
The pizza is done. Bring it into main:
git switch main
git merge pizza
What happens depends on whether main moved while you were away:
- If
mainhasn’t changed, Git does a fast-forward: it moves themainlabel ahead. No new commit. - If both branches have new commits, Git makes a merge commit with two parents, tying the histories together.
Then clean up:
git branch -d pizza # safe: refuses if it wasn't merged
How Git merges
Git finds the merge base, the last commit both branches share, and compares each branch against it:
git merge-base main pizza # prints the hash of B
Git combines what changed from B to C and from B to D:
- A line changed on one side only is taken as is.
- A line changed on both sides, to the same thing, is taken once.
- A line changed on both sides, differently, is a conflict. Git can’t know which you meant.
Most of the time the two branches touch different places, so merges usually just work.
Conflicts
Auto-merging pizza.md
CONFLICT (content): Merge conflict in pizza.md
Automatic merge failed; fix conflicts and then commit the result.
Open the file. Git marked both versions:
<<<<<<< HEAD
Bake 20 minutes.
=======
Bake 25 minutes.
>>>>>>> pizza
Between <<<<<<< and ======= is yours (main), and the rest is theirs (pizza). Decide, delete the markers, and keep what you want:
Bake 25 minutes.
git add pizza.md
git commit # finishes the merge
Changed your mind halfway? git merge --abort puts everything back. A conflict is not an error, it’s Git asking a question only you can answer.
- Merge
maininto your branch often (or rebase onto it), so differences never pile up. Keep branches short-lived and focused. git statusduring a conflict lists Unmerged paths, the files still waiting for you.git addmarks one resolved.- To take one side wholesale, use
git checkout --ours <file>or--theirs, thengit addit. During a rebase the meaning flips: “ours” is the branch you’re rebasing onto, “theirs” is your own commit being replayed. - Editors (VS Code, JetBrains) offer one-click “accept current / incoming”, and
git mergetoolopens a visual tool.