principle-version-control · git:20260510.d3d736b · 2026-05-10 · sha256 a9c477018f4bedc3
principle-version-control git:20260510.d3d736bA
Immutable. This exact content never changes and is served at /api/v1/blob/a9c477018f4bedc3.
--- name: principle-version-control description: Version control & collaboration hygiene — atomic commits (one logical change), commit-message quality (imperative subject, body explains why), branching strategy (feature-branch vs trunk-based), rebase vs merge trade-offs, squash vs preserve history, PR description quality (what/why/how-to-test). Auto-load when discussing commit hygiene, writing commit messages, choosing a branching strategy, debating rebase vs merge, deciding when to squash, or reviewing PR description quality. --- # Version Control & Collaboration `git log` is a debugging instrument. Its quality depends entirely on the discipline of the people who wrote to it. ## Atomic commits — one logical change One commit, one reason to revert it. The test: can you describe the change in a short subject line without using "and"? - Atomic commits make `git bisect` effective — a failing test traces to the exact change that introduced it. - Atomic commits make reverts safe — reverting "add feature X" does not also undo "fix unrelated bug Y". - Split a large change by concern: refactor first (pure mechanical moves), then behavior change, then test additions. ## Commit messages — what makes them load-bearing Subject line: imperative mood, ≤50 characters, no trailing period. Body: wrap at 72 characters, explain *why*, not *what*. ``` [type] Short imperative subject (≤50 chars) Why this change was made. What would break if reverted. Link the issue or ticket. Note non-obvious constraints. ``` Format schemes — Conventional Commits (`feat:`, `fix:`), `[type] Subject`, JIRA-prefix — are all valid conventions. Pick one; apply consistently. **This repo enforces `[type] Subject`** via `.githooks/commit-msg`; Conventional Commits will be rejected by the hook. See `swe-workbench:workflow-commit-and-pr` for the enforced regex and the `[no ci]` rule. ## Never mix formatting and logic Formatting commits (whitespace, import sort, rename) belong in a separate commit from behavior changes. Reviewers can skim a formatting-only commit in seconds; mixing it into a logic commit makes the diff unreadable and breaks `git blame` for every touched line. ## Branching — feature-branch vs trunk-based **Feature branches** work when: CI is slower than a few minutes, teams have >2 engineers, or releases are discrete. - Keep branches short-lived (days, not weeks). Long-lived branches accumulate merge debt. - Branch naming convention keeps the log readable — `feat/<issue>-<slug>`, `fix/<slug>`, `chore/<slug>`. **Trunk-based development** works when: CI is fast (<5 min), team is small and disciplined, and deployments are continuous. - Requires feature flags for incomplete work landing on trunk. - Pair with small, frequent commits — a day's work at most. ## Rebase vs merge — preserving vs flattening history **Rebase** local, unpublished branches to keep a clean linear history. Never rebase a branch others have pulled — it rewrites SHAs and orphans their work. Exception: `--force-with-lease` on your own solo feature branch is safe when you are the sole author and communicate the rewrite. - `git pull --ff-only` for main: fast-forward only; fail loudly on divergence instead of silently creating a merge commit. - Interactive rebase (`rebase -i`) before opening a PR: squash WIP commits, fix-up typos, reorder logically. **Merge** preserves the full branch topology — useful when the branch history documents a parallel exploration or a release boundary. - `--no-ff` merge commits document "this was a feature branch" even when the history would fast-forward cleanly. See `swe-workbench:workflow-cleanup-merged` for post-merge local cleanup (fast-forward pull, squash-merge branch detection). ## Squash vs preserve on merge **Squash** when: branch commits are WIP saves, fix-ups, or cleanup noise. The merged result is one clean commit on main. **Preserve** when: each commit is a meaningful, independently-reviewable step. The branch history survives on main and remains bisectable. Default rule: if you would be embarrassed for a commit message to live on main forever, squash it. ## PR descriptions — what reviewers actually need A PR description answers three questions: 1. **What changed?** — the minimal summary a stranger needs to understand scope. 2. **Why?** — link the issue; explain the motivation if it is not obvious from the ticket. 3. **How to verify?** — test plan, repro steps, or a note that CI covers it. Flag risk explicitly: migrations, breaking API changes, coordinated-deploy requirements. See `swe-workbench:workflow-pr-review` for review orchestration and `swe-workbench:principle-clean-code` for the small-change discipline that makes atomic commits tractable. ## When strict discipline is overkill - Solo throwaway repos or local experiments — no reviewer means no need for archaeology-quality messages. - Single-shot scripts that will never be modified again. - Exploratory spikes: commit freely with "wip"; squash the whole branch before the real PR. Even then: one commit per coherent idea costs nothing and pays off the first time you need `git bisect`. ## Red Flags | Flag | Problem | |------|---------| | "WIP", "fix", "asdf" on a shared branch | Bisect useless; history tells no story | | Force-push to a branch others have pulled | Rewrites SHAs; orphans teammates' local branches | | Formatting mixed with logic in one commit | `git blame` broken; reviewer cannot skim the diff | | "fix typo" chains on a feature branch | Noise that should be squashed before the PR | | Branch open for >2 weeks | Merge debt compounds; rebase conflicts grow | | PR body empty or only "closes #N" | No context for reviewer; future archaeology blocked | | `git pull` without `--ff-only` on main | Creates a silent merge commit when local main has diverged; `--ff-only` fails loudly instead |