A common small-team setup has a production branch: merge into it, push, and the server pulls it. It works until the day someone commits directly to it, force-pushes it, or merges the wrong thing and the branch history no longer matches what anyone intended. The branch stops being a record and becomes a rumor.
A tag does not have these problems.
What deploying a tag looks like#
The release flow is: finish work on main, tag the commit, deploy the tag.
git tag -a v1.14.0 -m "v1.14.0"
git push origin v1.14.0
On the server, instead of git pull, the deploy script fetches and checks out the exact tag:
git fetch --tags
git checkout v1.14.0
That is a detached HEAD on purpose - the server is not a place where commits happen, so there is nothing to lose. The result is that “what is running in production” has one unambiguous answer, and it is the same answer git tag gives everyone on the team.
Why this beats an environment branch#
- Tags do not drift. A branch can pick up a stray commit; a tag points at one commit forever.
- Rollback is a checkout. Deploy the previous tag and you are back, with no revert commit needed - When to Reset, Restore, or Revert stays a developer-machine topic, not a 2 AM production topic.
- The release log writes itself. Tag messages and annotated-tag history double as the skeleton of a changelog, which is the whole trick in Keep a Changelog Straight From Git Tags.
The Tagging Releases Properly post covers annotation and naming; this one is about making the tag the unit of deployment.
Keeping staging honest#
If you want a staging environment, deploy the same way: tag a release candidate (v1.14.0-rc.1), deploy that tag to staging, and promote by re-tagging or tagging the final release on the same commit. “Staging runs something production has never seen” becomes structurally impossible.
For the full pipeline around this - build steps, env handling, the push-to-server mechanics - Deploy Laravel With Git Push walks through it. The short version of both posts: branches are for work in progress, tags are for facts. Deploy facts.
Share this post
Keep reading
A First CI Pipeline for a Laravel Project
Install, lint, test, build - four jobs that run on every pull request and turn 'works on my machine' into a check everyone can see.
A Staging Environment Worth Having
Staging only catches what it shares with production. Same deploy path, same services, sanitized data - and a clear rule for what it is for.
Branch Naming Conventions That Scale Past Three People
Branch names are a user interface for your team's work. The conventions that survive growth, the ones that collapse, and why boring always wins.