Git workflow: branching, merging, and pull request discipline
Branching habits, merge strategies, and how to build a traceable change history with Git and pull requests.
There is a significant difference between using Git as a file backup tool and using it to meaningfully manage a change history. In the early days, the git add -A, git commit -m "update", git push cycle was enough. But a few months later, when I asked “why did I change this line?”, I couldn’t find the answer anywhere in that commit history.
Branching and commit discipline is a worthwhile habit even when working alone. When working in a team, it becomes a necessity.
Branch strategy: when should you open a branch?
A simple rule: the main (or master) branch always represents working, deployable code. A separate branch is opened to try something new, fix a bug, or develop a feature.
# New feature branch from main
git checkout main
git pull
git checkout -b feature/user-notifications
Writing meaningful branch names matters. Names like fix/login-redirect-bug, feature/invoice-pdf, and hotfix/payment-callback describe the purpose of the branch without needing any further explanation.
Commit messages: what should they say?
Messages like “update”, “fix”, or “test” are meaningless. A good commit message answers the question: “What will happen when this commit is applied?”
A widely accepted format:
feat: add user notification preference saving
- Notification types (email, SMS) can be toggled on/off independently.
- Preferences are written to the user_notification_settings table.
The first line is short (around 50 characters), written in imperative mood (added, fixed, removed). The second part is optional, but if it answers the “why” question, it is extremely valuable.
Using prefixes like feat, fix, refactor, docs, and style makes it easier to filter through history. On a shared project where everyone follows the same format, git log transforms into a meaningful piece of documentation.
Merge strategies
When merging a feature branch into main, there are a few options:
Merge commit: Preserves the branch history and the merge point. Preferable for large features.
git checkout main
git merge feature/user-notifications
Squash merge: Squashes all commits from the branch into a single commit. Keeps the history cleaner for small changes.
git merge --squash feature/user-notifications
git commit -m "feat: add user notification preferences"
Rebase: Replays the branch history on top of main, producing a linear history. It produces clean results but should be used carefully on shared branches.
Declaring any one of these definitively “correct” is difficult. What matters is consistency: using the same strategy throughout a project.
Code review with pull requests
When working on GitHub or GitLab, opening a pull request instead of merging a branch directly has become second nature. I do this even when working alone, because looking at my own changes through a pull request lets me see things in an open diff that I would have missed inside the controller.
When working in a team:
- Open a branch, make the changes.
- Open a pull request and write a brief description of what was done.
- A teammate reviews and approves (or leaves comments).
- Merge into
main.
This cycle produces deliberate, reviewed changes — instead of “I have no idea if it was working, I just pushed it.”
Reading the history
Meaningful commit messages are required to actually make use of git log and git blame:
git log --oneline --graph # Compact history with branching graph
git log -p src/Payments/ # Changes made in a specific directory
git blame app/Models/Order.php # Who changed each line, and when
git blame is a poor name — it is not about blame, it is about finding the answer to “who wrote this code and why?” If commit messages are meaningful, the answer to that question is already right there in the commit message.
Git discipline offers no immediate return, but three months down the line when you wonder “why did I make this change?”, you will understand exactly how valuable it is.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.