We recently hit a confusing merge conflict when trying to merge main back into staging. After investigation, the root cause was clear: PR #25 (Staging Changes) was a partial merge of staging into main — most likely a squash-merge or cherry-pick of selected commits rather than a full merge of the branch tip.
This left main with only a subset of staging's work, while staging kept evolving with new features (the POS customer-deposit module, settle-invoice improvements, etc.). The two branches diverged, and any subsequent main → staging merge produced conflicts in files that had been modified independently on both sides.
feature/* ──PR──▶ staging ──full merge──▶ main
- Features live in
feature/*branches (ordev_vasant,litnimax/connect-…, and other topical branches). - Features are merged into
stagingvia PR — either a full merge or a squash, depending on team convention. - When
stagingis ready for release, it is merged intomainas a full merge of the current branch state. Never a partial / squash / cherry-pick.
- Merging
stagingintomainis always a full merge (or rebase + fast-forward) of the current tip. No squash, no cherry-picking individual commits. - Protect
mainon GitHub: enable branch protection to disallow squash merges. Require merge commits when integratingstagingintomain. - Keep
stagingin sync after eachmainrelease: after mergingstagingintomain, rungit checkout staging && git merge --ff-only origin/main(or rebase) so thatstagingnever falls behind. This makes futuremain → stagingmerges either fast-forwards or no-ops.
A true fast-forward is impossible (both branches have exclusive commits). To resolve:
- Locally (safe, reversible):
git branch -f main staging git checkout mainmainnow points tostaging's tip. The previous main-only commits remain in the reflog if you need to recover anything. - On the remote: do not force-push
mainunless branch protection allows it and the team is aware. The cleanest long-term fix is to mergestagingintomainas a single merge commit so that history is preserved.
If you want named release points, you can additionally create release/v1.0, release/v1.1, etc. from main after each release. This is optional and sits on top of the base workflow above.