Most people learn Git as a list of magic spells. But Git has a tiny, beautiful model underneath — and once you can see it, merge conflicts, detached HEADs and rebases stop being scary.
Commits are snapshots, chained together
Every commit stores a snapshot of your project, a message, an author, and a pointer to its parent commit. Follow parents backwards and you get history. The ID (like a1b2c3d) is a hash of all that content — change anything and you get a different commit.

The branch playground
Make a few commits, create a branch, commit on it, switch back to main, commit there too, then merge. Then reset and try merging without committing on main — watch for the fast-forward.
$ git log --oneline
f1bbcd8 add homepage
A branch is a sticky note
Did you notice? Creating a branch didn't copy anything. It just added a label. A branch is literally a tiny file containing one commit ID. When you commit, Git moves the label of the branch you're on forward — and only that one.
HEAD is the note that says which branch you're on. git switch feature moves HEAD and updates your files to that snapshot.
Branches are cheap. Commit often, branch freely — you can always move labels around.
Quick check
You create a branch 'feature' and make 3 commits on it. What happens to 'main'?
Fast-forward vs merge commit
Fast-forward
If main hasn't moved since you branched, there is nothing to combine. git merge feature just slides the main label forward. History stays a straight line.
Merge commit (three-way merge)
If both branches have new commits, Git compares both tips with their common ancestor and creates a new commit with two parents. If both sides changed the same lines differently, that's a merge conflict — Git asks you to choose.
git switch -c feature # create + switch
git commit -am "add search" # label 'feature' moves
git switch main
git merge feature # fast-forward or merge commitAnd rebase?
git rebase main takes your branch's commits and replays them on top of the latest main, producing new commits with new IDs. The result is a straight history, as if you had started your work later.
A simple workflow that scales
- Keep
mainalways deployable and protected. - One short-lived branch per task:
feat/…,fix/…. - Open a pull request, let checks run, get a review, merge.
- Delete the branch after merging — the commits live on in history.


