Merging is how finished work lands on main; knowing the two kinds keeps history readable.
git merge <branch> brings the commits of <branch> into the branch you are on. There are two outcomes.
Fast-forward: if your current branch hasn't moved since <branch> was created, its commits simply sit on top of yours. Git just slides your pointer forward; no new commit is made and history stays a straight line.
Three-way merge: if both branches have new commits, Git finds their merge base (the last common ancestor), compares each side with it, and combines the changes. It records the result as a merge commit with two parents. Changes to different files, or different parts of the same file, combine automatically; overlapping edits become conflicts (next lesson).
You can control which you get:
git merge --no-ff featurealways creates a merge commit, even when a fast-forward is possible, preserving the fact that a group of commits was one feature.git merge --ff-only featurerefuses unless a fast-forward is possible. Good for updating a localmainfromorigin/mainwithout accidental merges.git merge --squash featurestages the combined changes as one new commit you then create, without recording the branch as merged.
After merging, delete the feature branch with git branch -d; its commits are safe in main's history. git branch --merged lists branches already merged into the current one, handy for cleanup.
Merge never rewrites existing commits. That makes it safe on shared branches, at the price of merge commits in the graph.
Example
git switch main
git merge feature/login # Fast-forward (main hadn't moved)
git switch main
git merge --no-ff feature/search -m "Merge search feature"
git log --oneline --graph -5
# * b4e7d21 Merge search feature
# |\
# | * 91aa3c0 Add search index
# * | 3f9a2c1 Fix totals
# |/
git branch --merged # safe-to-delete branches
git branch -d feature/searchRemember
- Fast-forward just moves the pointer; three-way merge makes a two-parent commit
- The merge base is the common ancestor Git diffs both sides against
- --no-ff keeps feature grouping; --ff-only refuses real merges
- Merging never rewrites existing commits
Watch out for
- Using --squash and then deleting the branch with -d — Git doesn't consider it merged, so -d refuses; that's expected.