Git workflow and versioning
Skill codexskills/agent-forge/skills/git-workflow-and-versioning
Trunk-based development with atomic commits, disciplined versioning, and a forensic bisect workflow. Covers commit granularity, branch naming, conventional commits, semver, merge strategies, and the `git bisect` workflow for finding regressions. Designed to make `git log` a reliable audit trail and `git bisect` a reliable tool.From its SKILL.md
npx -y skills add codexskills/agent-forge --skill git-workflow-and-versioningAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
12.2 KB, ~3.0k tokens by cl100k_base, as published. Nobody here has run it
Git Workflow & Versioning
Overview
Git is the most widely used tool in software engineering and the most misused. Most teams treat it as a dumpster — large, unstructured commits on long-lived branches merged with incomprehensible messages. This skill treats Git as a forensic tool. Every commit is a save point with a verifiable hypothesis. Every merge records intent. Every tag corresponds to a deployable artifact.
Core principle: trunk-based development. The main (or master)
branch is always deployable. Branches are short-lived (hours to days, not
weeks). The typical change is approximately 100 lines, fitting in a single
commit. Larger changes are split into a sequence of atomic commits on a
short branch, each passing CI.
When to Use
- Starting a new project or repository
- Creating a branch for any change
- Committing work-in-progress or complete changes
- Writing commit messages
- Reviewing or merging a pull request
- Choosing a version number for a release
- Rebasing, squashing, or resolving merge conflicts
- Running
git bisectto find a regression - Setting up CI/CD triggers and branch protection rules
- Onboarding new team members to the project's Git conventions
Process
Step 1: Choose the Right Branch Strategy
For most teams, the optimal strategy is short-lived feature branches merged to trunk.
Branch types:
| Branch | Purpose | Lifespan | Base |
|---|---|---|---|
main | Production-ready, always deployable | Permanent | — |
feat/<name> | A single feature or bug fix | Hours to 2 days max | main |
fix/<name> | Bug fix (separated from features for release tracking) | Hours to 2 days max | main |
chore/<name> | Refactoring, deps, tooling, docs | Hours to 1 day | main |
release/v<major>.<minor> | Release stabilization (rare — for coordinated releases) | Days, not weeks | main |
hotfix/<name> | Emergency fix from a release branch or tag | Hours | main or release tag |
Naming conventions:
- Lowercase with hyphens:
feat/user-authentication - Include issue/ ticket number:
feat/PROJ-1234-user-auth - Descriptive but concise:
fix/null-pointer-loginnotfix/bug - Use
/separators:feat/,fix/,chore/,release/,hotfix/
Anti-patterns:
- Long-lived branches (more than 2 days without merging to trunk)
devordevelopbranches that lag behindmainby weeks- Branches named after people (
alexs-feature) — anonymity enables collective ownership - Branches named
temportest— they never get deleted
Exit criteria: A branch with a valid name, based on the latest main,
expected to live <48 hours.
Step 2: Make Atomic Commits
A commit is a save point: it should compile, pass tests, and represent one logical change. The ideal commit changes approximately 100 lines (+/- 50). If a change exceeds 200 lines, split it.
Commit granularity rules:
- One commit = one logical change. A feature might be 3-5 commits: (1) add data model, (2) add API endpoint, (3) add frontend component, (4) wire them together.
- If you can describe the change in a sentence, it is atomic enough. If you need "and", split it.
- Refactoring and behavior changes must never share a commit. If you refactor and add a feature in the same commit, bisect cannot distinguish them.
- Formatting changes are either the only change in a commit or they are committed separately before the logic change.
- Every commit passes the project's lint and test suite locally. If CI would fail on this commit, refine it before pushing.
Commit message format (Conventional Commits):
<type>(<scope>): <short summary> (≤72 chars)
<optional body — wrap at 72 chars>
- Explain *why* the change was made, not *what* (the diff shows what)
- Include trade-offs, alternatives considered, reasoning
<optional footer>
- BREAKING CHANGE: <description>
- Closes: #123
- Co-authored-by: Name <email>
Types:
| Type | Meaning | Version Impact |
|---|---|---|
feat | A new feature | MINOR |
fix | A bug fix | PATCH |
refactor | Code change with no behavior change | PATCH |
perf | Performance improvement | PATCH |
test | Adding or fixing tests | PATCH |
docs | Documentation changes | PATCH |
chore | Tooling, dependencies, CI, config | PATCH |
style | Formatting, linting | PATCH |
BREAKING CHANGE | Incompatible API change | MAJOR |
Exit criteria: A commit that compiles, passes tests, has a conventional commit message, and changes ~100 lines.
Step 3: Merge with Intent
Merge strategies (in order of preference):
| Strategy | When to Use | Impact on History |
|---|---|---|
| Squash merge | Merging a branch with multiple small commits into trunk. The entire branch becomes one commit on main. | Linear history, clean bisect, loses individual commit context. |
| Rebase + merge (linear) | You want a linear history with the benefit of individual commits. Rebase the branch onto main, then fast-forward merge. | Linear history, preserves individual commits, requires force-push. |
| Merge commit | When the branch's individual commits are meaningful and rebasing would be impractical (many conflicts, long-running branch). | Non-linear history, preserves all commits and topology, readable merge point. |
Branch protection rules (GitHub/GitLab):
mainis protected: no direct pushes, only PRs/MRs- Required status checks: lint, test, build, security audit, coverage threshold
- Required reviews: at least one approval, blocking comments resolved
- Linear history required (squash or rebase)
- Branch must be up to date with
mainbefore merging
Exit criteria: A clean merge to main with an appropriate strategy,
passing status checks, and a meaningful merge/squash message.
Step 4: Version with Semver
Semantic Versioning (semver): MAJOR.MINOR.PATCH
| Component | Increment When | Example |
|---|---|---|
| MAJOR | Breaking change (API contract changed, backward-incompatible) | 2.0.0 |
| MINOR | New feature (backward-compatible) | 1.3.0 |
| PATCH | Bug fix, refactor, performance (backward-compatible) | 1.2.4 |
Pre-release tags: 1.0.0-alpha.1, 1.0.0-beta.2, 1.0.0-rc.3
Versioning rules:
- Every merge to
mainthat is deployable produces a version decision: maintainers determine if the cumulative unreleased changes since the last tag are MAJOR, MINOR, or PATCH. - Tag every release:
git tag v1.2.3and push tags. - Versions are immutable. If a release is bad, tag the fix as a PATCH bump.
- Maintain a
CHANGELOG.md(keep a changelog convention) that documents every version change with links to PRs/issues. - For libraries/packages: version the published artifact, not just the repository tag.
Exit criteria: Every deployable version has a semver tag, a changelog entry, and an artifact that can be reproduced from the tag.
Step 5: Git Bisect Workflow
When a regression is found, git bisect identifies the exact commit that
introduced it. This is only possible if commits are atomic (Step 2).
Bisect workflow:
# Start bisect
git bisect start
# Mark the current commit as bad (contains the bug)
git bisect bad
# Mark a known-good commit (from a tag, a date, or a known-good SHA)
git bisect good v1.2.0
# Git checks out a commit halfway between good and bad.
# For each checked-out commit:
# 1. Build the project
# 2. Run the reproduction test
# 3. If the test passes: git bisect good
# 4. If the test fails: git bisect bad
# Repeat until the first bad commit is identified.
# When done:
git bisect reset
Automated bisect:
git bisect start HEAD v1.2.0 -- # bad=HEAD, good=v1.2.0
git bisect run npm test # any command that exits 0 (good) or non-0 (bad)
Bisect prerequisites (how commits enable bisect):
- Every commit compiles. If a commit does not compile, bisect cannot use it.
- Every commit passes tests. If unrelated tests are broken, bisect cannot distinguish the regression.
- Refactoring and behavior change are in separate commits. If they are combined, the bisect output is ambiguous.
- Formatting changes are in separate commits. A bisect should never point to a formatting commit as the "first bad commit."
Exit criteria: A git bisect session that identifies the exact commit
introducing a regression, with an automated script (optional but ideal).
Anti-Rationalization Table
| Excuse | Rebuttal |
|---|---|
| "I'll write the commit message after I finish coding." | Write the message before you commit. If you cannot summarize it in one sentence, the change is not atomic. A TODO commit is not a commit. |
| "My branch is only 3 days old, that's not long-lived." | After 48 hours, main has likely moved significantly. The merge becomes painful and bisect becomes imprecise. Short branches are the single best predictor of team velocity. |
| "I'll squash everything on merge, so individual commit quality doesn't matter." | Squashing discards the intermediate state that bisect needs. If you plan to squash, make sure each commit is still atomic and compiles — bisect runs on the final result anyway. But this still makes review harder. |
| "We don't need semver — we ship everything continuously." | Semver communicates compatibility to consumers. Even if you deploy continuously, your API consumers (other services, mobile apps, open-source users) need to know when to expect breakage. |
| "Rebasing rewrites history, which is dangerous." | Rebasing shared branches is dangerous. Rebasing your own feature branch onto main is safe and produces cleaner history. Follow the rule: never rebase main, never rebase shared branches. |
| "The merge conflict was small, so I resolved it without checking with the author." | Every merge conflict represents a communication gap. At minimum, the resolution should be reviewed. If the conflict touched business logic, the original author should verify the resolution. |
| "We don't need branch protection rules because we trust each other." | Trust prevents malice, not mistakes. Branch protection prevents the CI from being bypassed when someone forgets to run tests, a dependency audit fails, or a linter rule is violated. Protection is for process, not people. |
Red Flags
- Commits with messages like "fix", "update", "wip", "asdf", or "."
- Commits that change 500+ files or 10,000+ lines
- Branches that live longer than 1 week
- Merge commits that contain no actual merge (just a fast-forward with a message)
git bisectthat lands on a "merge branch" or "fix formatting" commit- Version tags that do not match semver (e.g.,
v1.2.3.4orv1.02.03) - No tags at all — no way to identify deployable reference points
- Committing
.env,node_modules,vendor/,build/, or other generated files mainbranch broken for more than 30 minutes- Force-push to shared branches (including
main) - Commit that combines formatting, refactoring, and a feature in one diff
Verification
- Check
git log --oneline -20. Does every message follow conventional commits format? - Check
git diff --statfor the last commit. Is it approximately 100 lines? - Check
git bisectcapability: pick a known regression from the last month and verify thatgit bisectlands on an atomic commit, not a merge or formatting commit. - Check branch list:
git branch -a. Any branches older than 2 days? - Check tags:
git tag -l. Do they follow semver? Is the latest tag deployable? - Check branch protection settings: are
mainprotections configured? Required checks? Required reviews? - Check CHANGELOG.md: is it up to date? Does it link to PRs/issues?
- Check lockfiles: are
package-lock.json,poetry.lock,Cargo.lock, etc. committed? - Check
.gitignore: are secrets, build artifacts, and OS files ignored? - Test a commit reverts cleanly:
git revert HEAD. Does CI pass?
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.