Branches as Lightweight Pointers (Why They’re Cheap and Useful)
A Git branch is a movable pointer to a commit. Creating a branch does not copy files or duplicate your project; it simply creates a new name that points at the current commit. As you commit on that branch, the pointer moves forward. This makes branches ideal for small projects: you can isolate a small feature or fix without disturbing main, and you can review changes as a focused series of commits.
What “isolated changes” means in practice
mainstays stable: you can keep running the app frommainwhile experimenting elsewhere.- Reviews are faster: a feature branch can be compared directly to
mainto see exactly what changed. - Context switching is easier: you can pause a feature and switch back to
mainfor a quick hotfix.
Viewing and Switching Branches
List branches
git branchThe current branch is marked with *. If you want to see the last commit on each branch:
git branch -vSwitch branches
git switch mainIf you’re used to older commands, git checkout can also switch branches, but git switch is clearer for day-to-day branching workflows.
Creating a Feature Branch from main
A common small-project workflow is: keep main releasable, and do each change (feature, fix, maintenance) on its own branch.
Start from a clean main
git switch maingit statusYou want a clean working tree (no uncommitted changes) before branching, so the branch starts from a known point.
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
Create and switch to a new branch
git switch -c feature/add-config-loaderThis creates the branch at the current main commit and switches to it immediately.
Meaningful branch names (small project friendly)
Use a short prefix to communicate intent. This helps even if you’re the only developer, because it keeps your local branch list readable.
feature/...for user-visible or functional additionsfix/...for bug fixeschore/...for maintenance (refactors, tooling, formatting, dependency bumps)
Good examples:
feature/export-csvfix/null-user-crashchore/update-eslint
Avoid vague names like test, stuff, or new—they make it harder to understand what’s safe to delete or merge later.
Making a Sequence of Small Commits on the Branch
On a feature branch, aim for 2–3 commits that each represent a coherent step. This makes review and debugging easier than one large commit.
Example commit breakdown
- Commit 1: add the new module/function with minimal integration
- Commit 2: wire it into the app and update configuration
- Commit 3 (optional): add tests and small cleanup
As you work, keep checking what will be committed:
git statusStage and commit each step when it’s internally consistent:
git add path/to/file1 path/to/file2git commit -m "Add config loader module"Repeat for the next step(s):
git add path/to/integration-filegit commit -m "Wire config loader into startup"git add path/to/testsgit commit -m "Add tests for config loader"Comparing Branches (What Changed vs main)
Before merging, you’ll often want to answer two questions: “What commits are on my branch that aren’t on main?” and “What file changes will be introduced?”
Compare commit history
Show commits that are on feature/add-config-loader but not on main:
git log main..feature/add-config-loaderThis is useful for checking that your branch contains only the intended commits (no accidental work).
Compare file changes
See the net diff your branch introduces compared to the common base with main:
git diff main...feature/add-config-loaderThe triple-dot form (main...feature) compares your branch to the merge base (the point where the branch diverged). This is usually what you want when preparing a merge, because it shows the changes introduced by the branch itself.
When to Branch vs Commit Directly to main
For small projects, branching is still valuable, but you don’t need ceremony for every change. Use this rule of thumb.
Branch when…
- The change spans multiple commits or will take more than a few minutes.
- You might abandon or rework the change after trying it.
- You want a clean reviewable unit (even if you’re reviewing your own work later).
- The change could break the build temporarily while you iterate.
Commit directly to main when…
- The change is tiny and low risk (e.g., a one-line typo fix) and you can keep
mainworking. - You’re doing an immediate, obvious fix and you don’t need a separate review unit.
Even then, if you’re unsure, branching is cheap: creating a branch is fast and keeps options open.
Hands-On Flow: From Clean main to Merge-Ready Feature Branch
This walkthrough shows a complete, small-feature workflow you can repeat for most changes.
1) Verify main is clean
git switch maingit statusIf you see modified files, either commit them (if they’re ready) or revert them (if they’re accidental) before proceeding. The goal is to start the feature branch from a known clean state.
2) Create the feature branch
git switch -c feature/add-config-loadergit branch -vConfirm you’re on the new branch and it points at the expected commit.
3) Implement the change in 2–3 commits
Commit 1: add the core implementation
# edit files (example paths; use your project structure)git statusgit add src/config/loader.jsgit commit -m "Add config loader"Commit 2: integrate it
git add src/app.js src/config/index.jsgit commit -m "Use config loader during startup"Commit 3 (optional): tests and cleanup
git add test/config-loader.test.jsgit commit -m "Add tests for config loader"4) Compare your branch to main
Check that the branch contains only the intended commits:
git log main..feature/add-config-loaderReview the actual code changes as they will appear when merged:
git diff main...feature/add-config-loader5) Prepare for merging: status, tests, and readable history
Working tree should be clean (no half-finished edits):
git statusRun your tests or checks using your project’s commands (examples):
# choose what applies to your project toolingnpm testpytestgo test ./...Make sure commit messages tell a story: each commit should describe a single step. If you read the list from top to bottom, it should be obvious how the feature was built. A quick way to scan:
git log --oneline --decorate -n 10If the history looks confusing (e.g., “wip”, “try again”, “oops”), consider cleaning it up before merging so future-you can understand why the change exists and how it was introduced.