When working in a Git-based development team, especially in real-world projects, understanding how to manage branches and history is essential. This post explains the practical use of merge, rebase, and squash, as well as how to use demo or staging branches for safer and more flexible workflows.
A git merge preserves full commit history by combining branches using a new merge commit. This creates a non-linear history with visible branch structure.
* Merge branch 'feature/login'
|\
| * Add login UI
| * Hook up auth
* | Update README
|/
* Initial commit
git rebase moves your feature branch commits on top of the latest main, effectively rewriting history to make it look like your work was created linearly.
* Add login UI
* Hook up auth
* Update README
* Initial commit
Squashing combines all your commits into a single one during merge (e.g. GitHub's "Squash and merge").
* Add login feature (squashed)
* Update README
* Initial commit
When main has moved ahead and your feature branch is behind, you must resolve conflicts before merging.
main into your feature branchgit checkout feature/login
git merge origin/main
✅ Safe, preserves history
❌ Will create a merge commit
maingit checkout feature/login
git rebase origin/main
✅ Cleaner history
⚠️ Requires force-push if already pushed
Always resolve conflicts inside your feature branch, not on
main.
Git has no way to know if your conflict resolution is logically wrong. If you accidentally delete part of a previously merged feature during rebase/merge, Git will accept it as valid. That’s why it's important to:
git diff / git log -p)demo or staging BranchMany teams use a branch like demo, staging, or dev as an intermediate testing environment.
maingit checkout demo
git merge feature/login
git merge feature/auth
If a feature is later rejected, you can simply reset demo:
git checkout demo
git reset --hard origin/main
✅ Keeps main clean
✅ Allows testing in parallel
demo back into mainmain from their respective branches| Strategy | History | Keeps Commits? | Clean? | Safe on Shared Branch? |
|---|---|---|---|---|
| Merge | Non-linear | ✅ Yes | ❌ | ✅ Yes |
| Rebase | Linear | ✅ Yes | ✅ | ⚠️ Risky if shared |
| Squash | Linear | ❌ No | ✅✅ | ✅ Yes |
When used wisely, these strategies give you the best of both worlds: clean, readable history and safe, flexible workflows.
If you're managing features across multiple environments, introducing a demo or staging branch lets you test without polluting your production history.
Use merge for safety, rebase for clarity, and squash for simplicity. Just be deliberate — and communicate with your team.