Skip to main content
AC
Methodology & Process5.8 KBMIT licensed

git-workflow-and-versioning

Rewritten from patterns in addyosmani/agent-skills (MIT)

Git discipline for every code change: atomic commits, short-lived branches, trunk-based flow, semver, tags, and changelogs. Use when committing, branching, resolving conflicts, organizing parallel work, cutting a release, or choosing a version bump. Not for the end-of-branch merge/PR/cleanup decision: use finishing-a-development-branch. Not for UI implementation itself: use frontend-ui-engineering.

  • git
  • workflow
  • and
  • versioning

SKILL.md

Git Workflow and Versioning

Commits are save points, branches are sandboxes, history is documentation. With agents generating code fast, commit discipline is what keeps changes reviewable and reversible.

Core rules

  1. Trunk-based by default. Main stays deployable. Feature branches merge back within 1-3 days. Every day a branch lives it accumulates merge risk. Incomplete features go behind flags, not on long-lived branches. Teams on gitflow keep the commit rules below regardless of branching model.
  2. Commit every green increment. Pattern: implement slice, test, verify, commit, next slice. If the next change breaks, git reset --hard HEAD costs you one increment, not a day.
  3. Atomic commits. One logical change per commit. "Add task feature, fix sidebar, update deps" is three commits pretending to be one.
  4. Messages explain why. Format: <type>: <short description>, optional body for intent. Types: feat, fix, refactor, test, docs, chore. "update auth.ts" is not a message.
  5. Separate concerns. Formatting apart from behavior. Refactors apart from features. Each is its own commit, ideally its own PR.
  6. Size the change. ~100 lines is easy to review, ~300 acceptable for one logical change, ~1000 must be split before submitting.
  7. Branch names carry type: feature/task-creation, fix/duplicate-tasks, chore/update-deps, refactor/auth-module. Delete branches after merge.

Pre-commit pass

Before every commit: git diff --staged and read it, grep the staged diff for password|secret|api_key|token, run tests, lint, and type check. Automate with hooks (lint-staged + husky or equivalent). .gitignore covers node_modules/, dist/, .env*, *.pem from day one.

Change summaries

After any modification, report three sections: CHANGES MADE (file by file), THINGS I DIDN'T TOUCH intentionally (adjacent problems left alone, with why), POTENTIAL CONCERNS (strictness choices, new dependencies, open questions). The "didn't touch" section proves scope discipline and catches wrong assumptions early.

Parallel work and debugging

Worktrees give each branch its own directory: git worktree add ../project-feature-a feature/task-creation. Agents work in parallel without branch switching, failed experiments get deleted without loss. For hunting regressions: git bisect, git log --grep, git blame.

Releases and versioning

The moment anything depends on your code, "latest on main" stops answering "what am I running and is it safe to upgrade". Version MAJOR.MINOR.PATCH: major for breaking, minor for backward-compatible additions, patch for backward-compatible fixes. Unsure whether a change breaks consumers: assume it does. Tag every release (git tag -a v1.4.0 -m "Release 1.4.0"), derive the version from the tag, keep a human-curated changelog written with the change, not reconstructed at release time. Read references/release-versioning.md when cutting a release, choosing a bump, or writing the changelog.

Good vs bad

Bad: git log --oneline shows x1y2z3a Add task feature, fix sidebar, update deps, refactor utils. Reverting the sidebar fix means surgery. The "patch" release inside it renamed a response field consumers parsed.

Good: Four commits: the validation endpoint, the form component, the wiring, the tests. Each revertable alone. The field rename ships separately as a major with a deprecation window and a changelog entry.

Verification

Before pushing, run git log --oneline -10 and the staged-diff secret grep. Expect: each commit message names one logical change with a type prefix, and the grep returns nothing. A mixed commit: split it with git reset or interactive tooling before pushing. A secret: remove it and rotate the credential, history rewrites do not un-leak keys.

Completion checklist

  • [ ] Each commit does one logical thing
  • [ ] Messages typed and explain why
  • [ ] Tests, lint, type check pass before commit
  • [ ] No secrets in any staged diff
  • [ ] Formatting separated from behavior changes
  • [ ] Branch short-lived, deleted after merge
  • [ ] Change summary delivered with didn't-touch section
  • [ ] Release (if any): correct bump, tag pushed, changelog entry curated

Any box unchecked: not done. Fix or say so.

Red flags

Verbatim rationalizations that precede unreviewable history:

  • "I'll commit when the feature is done"
  • "The message doesn't matter, I'll squash later"
  • "It's just a small fix, bump the patch"
  • "The changelog is just the commit log"
  • "I don't need a .gitignore yet"

Closure rule: if a change is not in a typed, atomic, secret-free commit, it does not count as done, regardless of what is sitting in the working tree.

Footguns

  • Giant uncommitted working tree. One agent misstep destroys hours. Fix: commit every green slice, no exceptions.
  • Breaking change in a patch. Consumers observe behavior, not diff size (Hyrum's Law). Fix: judge the bump by what consumers can observe, assume breaking when unsure.
  • Force-pushing shared branches. Everyone else's history breaks. Fix: force-push only your own unshared branches, and only when explicitly requested.
  • Version hand-edited out of sync with the tag. Artifact says 1.4.0, tag says 1.3.2, nobody can reproduce the build. Fix: derive the version from the tag in CI.

Reference files

More in Methodology & Process

All skills