Merging combines another branch's commits into your current one. How Git does it depends on whether the branches have diverged.
Fast-forward — your current branch hasn't moved since you created feature. Git can just slide main's pointer forward to feature's tip. No new commit, linear history. Diagrammatically: main → A → B, feature → A → B → C → D becomes main → A → B → C → D.
Three-way merge — both branches have new commits since they split. Git can't fast-forward, so it builds a new merge commit with two parents (one from each side). History stays truthful but gets a diamond shape.
--no-ff forces a merge commit even when fast-forward would work. Teams use this to preserve "this batch of commits was one feature" in the log. Always run merge from the target: git switch main then git merge feature.