Git workflow
Git workflow
Learning Git commands and learning how a team operates Git are two different things. This article covers branching strategies, commit message conventions, version notation, PR review, and hook tools.
1. About Git
A distributed version control system created by Linus Torvalds in 2005. Even with the same tool, the team's operational decisions (branching strategy, merge policy, message convention) shape outcomes considerably.
2. Branching strategies
Four main branches.
Trunk-Based Development — Codified by Paul Hammant at trunkbaseddevelopment.com. The model where "we collaborate on a single trunk and avoid long-lived branches". Small teams commit to trunk directly; larger teams use short-lived feature branches (a day to a few) plus PRs. Unfinished features hide behind feature flags. Google's giant monorepo is the textbook case.
Git Flow — Vincent Driessen's 2010 blog post "A successful Git branching model". Five branch kinds — main, develop, feature/*, release/*, hotfix/*. Fits packaged software with fixed release cycles, and is widely seen as too heavy for continuous-deployment environments.
GitHub Flow — A simple model GitHub settled on around 2011. A single main line, short feature branches, PR-based merges, deploy on merge. Common in web services and SaaS.
GitLab Flow — A variant GitLab codified. Adds environment branches (production, pre-production) or release branches on top of GitHub Flow, fitting environments where deploy timing must be separated.
The right model depends on context. Release cadence, deployment automation, and team size all shift the answer.
3. Commit message conventions
Conventional Commits 1.0.0 (conventionalcommits.org) is the most common:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Common types — feat (new feature, semver MINOR), fix (bug fix, PATCH), docs, style, refactor, perf, test, build, chore, ci. Breaking changes go as feat!: or via a BREAKING CHANGE: footer (MAJOR).
Sticking to this convention plugs in naturally with auto-changelog and auto-semver tools (semantic-release, changesets).
4. Version notation
Semantic Versioning 2.0.0 (semver.org, Tom Preston-Werner) — MAJOR.MINOR.PATCH:
- MAJOR — incompatible API changes.
- MINOR — backwards-compatible feature additions.
- PATCH — backwards-compatible bug fixes.
The format also covers prereleases like 1.2.3-alpha.1 and build metadata like 1.2.3+build.42. The 0.y.z range is for early development with no compatibility promise.
Other notations exist too — CalVer (date-based, like 2024.10.0). Libraries often pick SemVer; applications often pick CalVer.
5. PR review
PRs / MRs on GitHub, GitLab, and Bitbucket are units that gather code changes for review. Common recommendations:
- Small PRs — When a PR carries a single intent, review is faster and regressions are fewer (a widely cited finding).
- CODEOWNERS — Auto-assigns reviewers per directory. GitHub and GitLab both support it.
- Async review — In time-zone-spread teams, immediate replies are hard. Agree on a review SLA and how to mark blockers (
Request changesvsComment). - Merge policy — Pick one of squash / rebase / merge commit. It shapes history and changelog automation.
6. Git hook tools
Hooks are Git's built-in mechanism for inserting scripts at points like commit and push (.git/hooks/). Sharing them across a team needs tooling.
- Husky (Node) — Manages hooks via
package.jsonand.husky/. The de facto standard in JS. - lint-staged (Node) — Runs linters / formatters only on staged files. Pairs with Husky.
- lefthook (Evil Martians, Go) — Language-agnostic, single binary.
- pre-commit (pre-commit.com, Python) — Multi-language hook manager with a tool list in
.pre-commit-config.yaml. Common in Python and Go ecosystems.
7. Common shapes
# Short feature branch + PR (GitHub Flow)
git switch -c feat/login-form
# ... work ...
git add -p
git commit -m "feat(auth): add email login form"
git push -u origin feat/login-form
gh pr create --fill # GitHub CLI
Apart from .gitignore, large binaries belong in Git LFS (git lfs track "*.psd").
CODEOWNERS example:
# path owner
/apps/web/ @org/frontend
/services/api/ @org/backend
*.md @org/docs
pre-commit example config:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff
- id: ruff-format
8. Common pitfalls
git push --force overwriting a shared branch — --force-with-lease is one step safer.
Merge vs rebase — without a team-agreed policy, one person's rebase can scramble another's local history.
Big PRs — review tends to slip into formality. Keeping both commits and PRs small is more effective at catching defects.
Missing BREAKING CHANGE: footer — leaving only feat! makes some auto-changelog tools drop body content beyond the title.
Mixed Windows line endings (core.autocrlf) — every diff fills with line-ending noise. Use .gitattributes to set policy per file type.
Closing thoughts
Operational decisions move outcomes more than Git commands do. Trunk-Based or GitHub Flow's simplicity, Conventional Commits, Semantic Versioning, small PRs, and a clear merge policy together reduce collaboration friction the most. Consistent commit messages are the foundation for changelog and release automation.
Next
- gradle
- editor-setup
References include the Pro Git Book, Conventional Commits 1.0.0, Semantic Versioning 2.0.0, Trunk-Based Development, GitHub Flow Guide, Vincent Driessen's Git Flow (2010), GitLab Flow, pre-commit.com, and Google Engineering Practices Code Review.