Git (SCM) Best Practices
#git#scm#version-control#best-practices#devops#collaboration
Git is the dominant source control management (SCM) tool. Using it well keeps history readable, merges smooth, and CI/CD reliable. This post outlines best practices for commits, branches, and workflows that scale from solo work to large teams.
Commit messages
- One logical change per commit — Easier to review, revert, and bisect. Avoid “fix stuff” or dumping a whole feature in one commit.
- Write for humans — Future you and your team will search and read these. Good messages explain why as well as what.
- Conventional format — A common pattern:
<type>(<scope>): <short summary>
[optional body: details, rationale, breaking changes]
[optional footer: Refs #123, BREAKING CHANGE: ...]
| Type | Use for |
|---|---|
feat | New feature |
fix | Bug fix |
docs | Documentation only |
refactor | Code change, no fix/feat |
test | Adding or updating tests |
chore | Build, tooling, dependencies |
Examples: feat(auth): add SSO login, fix(api): handle null pagination. Many teams enforce this with commitlint or branch protection.
Branching strategy
Choose a model that fits your release cadence and team size.
| Strategy | Best for | Main idea |
|---|---|---|
| Trunk-based | Small teams, frequent deploys | Short-lived branches off main; merge often. |
| Git Flow | Versioned releases, hotfixes | Long-lived develop + release/*, hotfix/*. |
| GitHub Flow | Single production branch | main is deployable; feature branches only. |
Practical tips:
- Short-lived branches — Integrate with
main(ordevelop) at least daily to avoid painful merges. - Descriptive names —
feat/order-checkout,fix/login-timeout,docs/api-readme; avoidjohn-branchortest2. - Delete merged branches — Keeps the branch list meaningful; the history is already in
main.
Before you commit
- Review your diff —
git status,git diff, andgit diff --staged. Don’t commit commented-out code, debug prints, or unrelated files. - Run checks locally — Linters, tests, and formatters. Fix failures before pushing so CI stays green.
- Keep secrets out — Never commit passwords, API keys, or tokens. Use
.gitignoreand secret scanners; if leaked, rotate immediately and usegit filter-repoor BFG to remove from history if policy requires it.
.gitignore and hygiene
- Ignore by default — IDE config (e.g.
.idea/), OS files (.DS_Store), build outputs (dist/,node_modules/), env files with secrets (.env,.env.local). - One repo, one .gitignore — Prefer a root-level
.gitignore(and maybe a few in subdirs for large monorepos). Document any “ignore by convention” in the README. - Don’t commit generated artifacts — Binaries, compiled assets, and logs should be produced by build or run, not stored in Git (unless you have a clear policy, e.g. release binaries in a separate repo).
Pull requests (merge requests)
- Small, focused PRs — Few hundred lines and a clear purpose. Easier review and fewer merge conflicts.
- Describe context — What problem it solves, how to test, and any follow-ups. Link issues/tickets.
- Respond to review — Address comments or discuss; re-request review after updates so the reviewer knows to look again.
- Squash or no squash — Either keep a clean linear history with “Squash and merge” or preserve full history with “Merge commit”; pick one and stick to it in the project.
Rebase vs merge
| Approach | When to use | Trade-off |
|---|---|---|
| Merge | Integrating feature branches | Preserves history; can be noisy. |
| Rebase | Updating your branch onto main | Linear history; never rebase shared branches. |
Rule of thumb: Rebase your own branch onto main before opening a PR so the diff is up to date. Do not rebase branches others are using (e.g. shared develop); use merge there to avoid rewriting published history.
Tags and releases
- Semantic versioning — Tag releases as
v1.2.3. Use annotated tags:git tag -a v1.2.3 -m "Release 1.2.3". - Tags in CI/CD — Many pipelines build or deploy only when a tag is pushed (e.g.
v*). Keep tag names consistent so automation is simple.
Summary
| Area | Best practice |
|---|---|
| Commits | One logical change; conventional message format. |
| Branches | Short-lived; descriptive names; delete after merge. |
| Before commit | Review diff; run tests/lint; no secrets. |
| .gitignore | Ignore build output, IDE, OS files, and secret files. |
| PRs | Small and focused; good description; respond to review. |
| Rebase | Rebase your branch onto main; never rebase shared branches. |
| Releases | Annotated tags (e.g. v1.2.3) and consistent naming. |
Adopting these practices keeps your Git history clear and your SCM a solid base for collaboration and automation.
Comments