agentleFS
AI-Degen-69AI-Degen-69/issue-to-pr-skills/skills/git-workflow-and-versioning/SKILL.md

git-workflow-and-versioning

Skill0 starsChanged 51 days agoMITView on GitHub

Structures git workflow practices. Use when making any code change. Use when committing, branching, resolving conflicts, opening or reviewing a pull request (PR), pushing to a remote, or when you need to organize work across multiple parallel streams. Use when cutting a release, choosing a semantic version bump, tagging, or writing a changelog.

  • Reads credentials
  • Deletes or force-pushes
  • Commits and pushes

61 other projects use a version of this, 50 of them edited.

Adopted by

Most recently taken up by

Used with

Projects with it also use marketing-ideas, minimalist-ui and 3 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • silvainfmSkill

    marketing-ideas

    When the user needs marketing ideas, inspiration, or strategies for their SaaS or software product. Also use when the user asks for 'marketing ideas,' 'growth ideas,' 'how to market,' 'marketing strategies,' 'marketing tactics,' 'ways to promote,' or 'ideas to grow.' This skill provides 140 proven marketing approaches organized by category.

    Also in 4 projects that use this

  • AnasX7Skill

    minimalist-ui

    Clean editorial-style interfaces. Warm monochrome palette, typographic contrast, flat bento grids, muted pastels. No gradients, no heavy shadows.

    Also in 4 projects that use this

  • GoogleSkill

    ponytail

    Forces the laziest solution that actually works, simplest, shortest, most minimal. Channels a senior dev who has seen everything: question whether the task needs to exist at all (YAGNI), reach for the standard library before custom code, native platform features before dependencies, one line before fifty. Supports intensity levels: lite, full (default), ultra. Use on ANY coding task: writing, adding, refactoring, fixing, reviewing, or designing code, and choosing libraries or dependencies. Also use whenever the user says "ponytail", "be lazy", "lazy mode", "simplest solution", "minimal solution", "yagni", "do less", or "shortest path", or complains about over-engineering, bloat, boilerplate, or unnecessary dependencies. Do NOT use for non-coding requests (general knowledge, prose, translation, summaries, recipes).

    Also in 5 projects that use this

  • AnasX7Skill

    industrial-brutalist-ui

    Raw mechanical interfaces fusing Swiss typographic print with military terminal aesthetics. Rigid grids, extreme type scale contrast, utilitarian color, analog degradation effects. For data-heavy dashboards, portfolios, or editorial sites that need to feel like declassified blueprints.

    Also in 3 projects that use this

  • OpenAISkill

    image-to-code

    Implement a selected image, screenshot, mockup, or Image Gen reference as a faithful, responsive frontend.

    Also in 3 projects that use this

The file

Show its rules and text

We split this skill into the rules it gives an agent

18 rules. 13 are common, 4 are distinctive, and 1 is very specific to this project.

  • 13

    Common

    Most of the 57 edited versions we read keep these.

  • 4

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Common:

    Always. Every code change flows through git.

    line 14
  2. Common:

    Keep main always deployable.

    line 20
  3. Common:

    Dev branches are costs: Every day a branch lives, it accumulates merge risk.

    line 30
  4. Common:

    Release branches are acceptable: When you need to stabilize a release while main moves forward.

    line 31
  5. Common:

    Feature flags > long branches: Prefer deploying incomplete work behind flags rather than keeping it on a branch for weeks.

    line 32
  6. Common:

    Commit messages explain the *why*, not just the *what*:

    line 67
  7. Common:

    Don't combine formatting changes with behavior changes.

    line 98
  8. Common:

    Keep branches short-lived (merge within 1-3 days) — long-lived branches are hidden costs

    line 134
  9. Common:

    Delete branches after merge

    line 135
  10. Common:

    Prefer feature flags over long-lived branches for incomplete features

    line 136
  11. Common:

    No branch switching needed (each directory has its own branch)

    line 169
  12. Common:

    If one experiment fails, delete the worktree — nothing is lost

    line 170
  13. Distinctive:

    When a branch has sat unmerged while main moved on, cherry-picking or rebasing its commits is not a mechanical operation — the branch's …

    line 175
  14. Distinctive:

    When ported work misbehaves, check the branch out in its own worktree and run it on its own base first.

    line 196
  15. Common:

    After any modification, provide a structured summary.

    line 238
  16. Very specific:

    Before any checkout/reset used as a rollback, name exactly what broke:

    line 308
  17. Distinctive:

    If a broad restore already reverted unrelated work, recover by re-running the operation that produced it (re-sync, re-apply the patch) …

    line 316
  18. Distinctive:

    When the goal is "this mirrored content should not be published", fix it at the canonical source and re-sync, or change the gate/ignore …

    line 322

“Common” is measured: kept by at least 6 in 10 of the 57 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Git Workflow and Versioning

Overview

Git is your safety net. Treat commits as save points, branches as sandboxes, and history as documentation. With AI agents generating code at high speed, disciplined version control is the mechanism that keeps changes manageable, reviewable, and reversible.

When to Use

Always. Every code change flows through git.

Core Principles

Trunk-Based Development (Recommended)

Keep main always deployable. Work in short-lived feature branches that merge back within 1-3 days. Long-lived development branches are hidden costs — they diverge, create merge conflicts, and delay integration. DORA research consistently shows trunk-based development correlates with high-performing engineering teams.

main ──●──●──●──●──●──●──●──●──●──  (always deployable)
        ╲      ╱  ╲    ╱
         ●──●─╱    ●──╱    ← short-lived feature branches (1-3 days)

This is the recommended default. Teams using gitflow or long-lived branches can adapt the principles (atomic commits, small changes, descriptive messages) to their branching model — the commit discipline matters more than the specific branching strategy.

  • Dev branches are costs. Every day a branch lives, it accumulates merge risk.
  • Release branches are acceptable. When you need to stabilize a release while main moves forward.
  • Feature flags > long branches. Prefer deploying incomplete work behind flags rather than keeping it on a branch for weeks.

1. Commit Early, Commit Often

Each successful increment gets its own commit. Don't accumulate large uncommitted changes.

Work pattern:
  Implement slice → Test → Verify → Commit → Next slice

Not this:
  Implement everything → Hope it works → Giant commit

Commits are save points. If the next change breaks something, you can revert to the last known-good state instantly.

2. Atomic Commits

Each commit does one logical thing:

# Good: Each commit is self-contained
git log --oneline
a1b2c3d Add task creation endpoint with validation
d4e5f6g Add task creation form component
h7i8j9k Connect form to API and add loading state
m1n2o3p Add task creation tests (unit + integration)

# Bad: Everything mixed together
git log --oneline
x1y2z3a Add task feature, fix sidebar, update deps, refactor utils

3. Descriptive Messages

Commit messages explain the why, not just the what:

# Good: Explains intent
feat: add email validation to registration endpoint

Prevents invalid email formats from reaching the database.
Uses Zod schema validation at the route handler level,
consistent with existing validation patterns in auth.ts.

# Bad: Describes what's obvious from the diff
update auth.ts

Format:

<type>: <short description>

<optional body explaining why, not what>

Types:

  • feat — New feature
  • fix — Bug fix
  • refactor — Code change that neither fixes a bug nor adds a feature
  • test — Adding or updating tests
  • docs — Documentation only
  • chore — Tooling, dependencies, config

4. Keep Concerns Separate

Don't combine formatting changes with behavior changes. Don't combine refactors with features. Each type of change should be a separate commit — and ideally a separate PR:

# Good: Separate concerns
git commit -m "refactor: extract validation logic to shared utility"
git commit -m "feat: add phone number validation to registration"

# Bad: Mixed concerns
git commit -m "refactor validation and add phone number field"

Separate refactoring from feature work. A refactoring change and a feature change are two different changes — submit them separately. This makes each change easier to review, revert, and understand in history. Small cleanups (renaming a variable) can be included in a feature commit at reviewer discretion.

5. Size Your Changes

Target ~100 lines per commit/PR. Changes over ~1000 lines should be split. See the splitting strategies in code-review-and-quality for how to break down large changes.

~100 lines  → Easy to review, easy to revert
~300 lines  → Acceptable for a single logical change
~1000 lines → Split into smaller changes

Branching Strategy

Feature Branches

main (always deployable)
  │
  ├── feature/task-creation    ← One feature per branch
  ├── feature/user-settings    ← Parallel work
  └── fix/duplicate-tasks      ← Bug fixes
  • Branch from main (or the team's default branch)
  • Keep branches short-lived (merge within 1-3 days) — long-lived branches are hidden costs
  • Delete branches after merge
  • Prefer feature flags over long-lived branches for incomplete features

Branch Naming

feature/<short-description>   → feature/task-creation
fix/<short-description>       → fix/duplicate-tasks
chore/<short-description>     → chore/update-deps
refactor/<short-description>  → refactor/auth-module

Working with Worktrees

For parallel AI agent work, use git worktrees to run multiple branches simultaneously:

# Create a worktree for a feature branch
git worktree add ../project-feature-a feature/task-creation
git worktree add ../project-feature-b feature/user-settings

# Each worktree is a separate directory with its own branch
# Agents can work in parallel without interfering
ls ../
  project/              ← main branch
  project-feature-a/    ← task-creation branch
  project-feature-b/    ← user-settings branch

# When done, merge and clean up
git worktree remove ../project-feature-a

Benefits:

  • Multiple agents can work on different features simultaneously
  • No branch switching needed (each directory has its own branch)
  • If one experiment fails, delete the worktree — nothing is lost
  • Changes are isolated until explicitly merged

Porting Stale Branch Work Onto an Evolved Base

When a branch has sat unmerged while main moved on, cherry-picking or rebasing its commits is not a mechanical operation — the branch's premises expire silently.

Verify deleted symbols' readers against the BASE, before applying

A branch that deletes fields "no code reads" was true when it was written; while it sat unmerged, main can grow readers of those same symbols. Grep for every deleted symbol's readers on the BASE first, never after applying — a pick that deleted the readers makes a post-apply grep circular: it finds nothing because the pick removed them, not because they were unused.

A conflict-free auto-merge can still change behavior

git auto-merges by text context; two versions of a file can interleave into something neither side intended — gates deleted on one side, gates added on the other, the result admitting a different set than either branch alone. Conflict-free is not behavior-preserving. A/B-test the actual runtime behavior on both sides (run the command, count or diff the output) before landing.

Diagnose a broken branch standalone before porting it

When ported work misbehaves, check the branch out in its own worktree and run it on its own base first. That separates "the merge mangled it" (fix the port) from "the branch was always broken" (fix or re-implement the branch) — two different repairs, and guessing wrong wastes the round.

When the branch is genuinely broken, re-implement fresh — do not port

A branch that crashes, carries half-wired logic (reads a key nothing populates), or produces empty/zero results on its own base is not salvageable by transplanting commits. Port the intent into a new issue and implement against current main; transplanting a broken commit re-lands its defect on a base that has moved on.

cherry-pick --no-commit + --abort does not undo a successful pick

--abort only undoes a pick that is mid-conflict. A pick that applied cleanly with --no-commit leaves its changes staged, and --abort exits silently having done nothing — the next dry-run then tests on top of the first pick's leftovers. Undo a successful --no-commit pick with git reset --hard only if the index and worktree were clean before the pick. Prefer running such dry-runs in a disposable worktree so pre-existing edits cannot be discarded. Confirm the tree is clean before the next trial.

The Save Point Pattern

Agent starts work
    │
    ├── Makes a change
    │   ├── Test passes? → Commit → Continue
    │   └── Test fails? → Revert to last commit → Investigate
    │
    ├── Makes another change
    │   ├── Test passes? → Commit → Continue
    │   └── Test fails? → Revert to last commit → Investigate
    │
    └── Feature complete → All commits form a clean history

This pattern means you never lose more than one increment of work. If an agent goes off the rails, git reset --hard HEAD takes you back to the last successful state.

Change Summaries

After any modification, provide a structured summary. This makes review easier, documents scope discipline, and surfaces unintended changes:

CHANGES MADE:
- src/routes/tasks.ts: Added validation middleware to POST endpoint
- src/lib/validation.ts: Added TaskCreateSchema using Zod

THINGS I DIDN'T TOUCH (intentionally):
- src/routes/auth.ts: Has similar validation gap but out of scope
- src/middleware/error.ts: Error format could be improved (separate task)

POTENTIAL CONCERNS:
- The Zod schema is strict — rejects extra fields. Confirm this is desired.
- Added zod as a dependency (72KB gzipped) — already in package.json

This pattern catches wrong assumptions early and gives reviewers a clear map of the change. The "DIDN'T TOUCH" section is especially important — it shows you exercised scope discipline and didn't go on an unsolicited renovation.

Pre-Commit Hygiene

Before every commit:

# 1. Check what you're about to commit
git diff --staged

# 2. Ensure no secrets
git diff --staged | grep -i "password\|secret\|api_key\|token"

# 3. Run tests
npm test

# 4. Run linting
npm run lint

# 5. Run type checking
npx tsc --noEmit

An edit that vanished was stashed, not lost

If git status shows a file you edited as clean, or git diff comes back empty on a change you know you wrote, check git stash list before rewriting it. An interrupted or wrapped operation can hold your work in a stash, and redoing the edit by hand produces a second, subtly different version.

git stash list                              # any wip-* entry is suspect
git stash show -p stash@{0}                 # confirm it is your change
git checkout stash@{0} -- path/to/file      # restore just that file
git stash drop stash@{0}                    # ONLY after all needed edits from this stash are restored/saved

Restore file-by-file, never stash pop the whole entry: the entry usually carries other in-flight work that must stay out of this commit. Drop the stash only after confirming no needed edits remain in it.

Foreign dirt: explain it, then let the operator decide

When the working tree holds a change you did not make, do not bundle it silently and do not silently drop it either. State in one or two plain sentences what it does to the running system and where it lands, then ask whether it belongs in this push. "I'll include everything in the tree" hides a stranger's edit inside your commit; discarding it loses work someone may still need. Report any file you deliberately left untracked or uncommitted so the operator knows it is still there.

Stage by explicit path when the tree is mixed, and re-verify the tree immediately before staging. An operator-approved file set is not frozen: between the approval and your git add, a parallel session, background sync, or earlier tool run can add or modify files. Re-run git status --porcelain right before staging and confirm the staged set (git diff --cached --stat) is still exactly the approved set. A file that appeared mid-flight is not yours to silently bundle — flag it, stage only the approved paths, and name anything you left out in the closeout. With pre-existing modifications or untracked files present, git add -A / git commit -a sweeps them into your commit. Stage only the paths your change owns, then confirm the staged set is exactly your change:

git status --porcelain       # everything dirty, before staging
git add path/a path/b        # explicit paths only
git diff --cached --stat     # the staged set must be exactly your change

Name in the closeout which files were deliberately left unstaged or untracked.

Restoring a bad restore: scope the pathspec to the damage

git checkout HEAD -- <path> restores every tracked file under that path from HEAD — it is not "undo my last mistake", it is "revert this whole tree to the last commit". Running it to undo a deletion of evals/ will also silently revert every uncommitted skill edit sitting in the same tree, and you will not notice until the sync check reports hash mismatches you thought you had fixed.

Before any checkout/reset used as a rollback, name exactly what broke:

git status --short                  # know what is dirty FIRST
git checkout HEAD -- skills/foo/evals/   # the specific dir that broke
git status --short                  # confirm only the intended lines changed

If a broad restore already reverted unrelated work, recover by re-running the operation that produced it (re-sync, re-apply the patch) rather than hand-reconstructing it — then re-run the project gate to confirm green.

Deleting from a mirrored pack fails the mirror gate

A public pack that is synced from a canonical source is verified by comparing file lists, not just content — the gate walks canonical and fails on any file missing from the pack, even one you deleted deliberately for being stale or non-portable. Deleting mirrored files (eval snapshots, historical baselines, retired notes) turns the gate red with "missing file in target" errors, and the generated site catalog cascades the drift into version-check failures.

When the goal is "this mirrored content should not be published", fix it at the canonical source and re-sync, or change the gate/ignore policy — never by deleting from the pack. A green pack is the deliverable; the canonical tree is where that gets decided.

A push rejected as non-fast-forward means the remote moved under you

A rejected git push (! [rejected] ... non-fast-forward, "fetch first") means the remote branch advanced — most often a squash-merge that landed on the default branch after you branched. Reconcile with rebase, not force:

git fetch origin
git log --oneline origin/<branch> -n 3   # what moved on the remote
git log --oneline -n 3                    # your local commits on top of the old base
git pull --rebase origin <branch>         # replay your commit(s) onto the new head
<run the project gate + tests>            # the rebased tree is a NEW tree
git push origin <branch>

Re-run the gate and tests on the rebased state before pushing — a green gate from before the rebase proves nothing about the tree you are about to publish. Force-push only to paper over divergence when you are certain no one else depends on the remote head; otherwise the rebase is the honest, reviewable fix.

Automate this with git hooks: // package.json (using lint-staged + husky) { "lint-staged": { ".{ts,tsx}": ["eslint --fix", "prettier --write"], ".{json,md}": ["prettier --write"] } }


## Handling Generated Files

- **Commit generated files** only if the project expects them (e.g., `package-lock.json`, Prisma migrations)
- **Don't commit** build output (`dist/`, `.next/`), environment files (`.env`), or IDE config (`.vscode/settings.json` unless shared)
- **Have a `.gitignore`** that covers: `node_modules/`, `dist/`, `.env`, `.env.local`, `*.pem`

## Using Git for Debugging

```bash
# Find which commit introduced a bug
git bisect start
git bisect bad HEAD
git bisect good <known-good-commit>
# Git checkouts midpoints; run your test at each to narrow down

# View what changed recently
git log --oneline -20
git diff HEAD~5..HEAD -- src/

# Find who last changed a specific line
git blame src/services/task.ts

# Search commit messages for a keyword
git log --grep="validation" --oneline

Release & Versioning

Commits are how you track change; a version is how your consumers track it. The moment anything else depends on your code — another team, a published package, a deployed client — "latest on main" stops being a sufficient answer to "what am I running, and is it safe to upgrade?" A version number and a changelog are the contract that answers it.

Semantic Versioning

For anything with consumers, version MAJOR.MINOR.PATCH and let the number carry meaning:

  MAJOR  breaking change — consumers must change their code to upgrade
  MINOR  new functionality, backward-compatible — safe to upgrade
  PATCH  bug fix, backward-compatible — safe to upgrade

The number is a promise, so make the code match it. A "patch" that changes behavior consumers relied on is a major change wearing a disguise (Hyrum's Law — see the api-and-interface-design skill). When unsure whether a change is breaking, assume it is; a surprise major is far cheaper than a broken consumer.

Tag the release, and let the tag be the source of truth

A release is an immutable point in history, not a moving branch. Tag it so it can always be reproduced:

git tag -a v1.4.0 -m "Release 1.4.0"
git push origin v1.4.0

Derive the version from the tag rather than hand-editing it in scattered files, so the artifact, the tag, and the changelog can never disagree.

Keep a changelog written for humans

A changelog is not git log. It's the curated, consumer-facing answer to "what changed and do I care?" — grouped by Added / Changed / Fixed / Deprecated / Removed / Security, newest on top, every entry phrased around user impact, not internal mechanics.

## [1.4.0] - 2025-06-12
### Added
- Bulk task import via CSV
### Fixed
- Timezone drift in recurring task due dates
### Deprecated
- `GET /v1/tasks/all` — use the paginated `GET /v1/tasks` (removal in 2.0)

Write the entry in the same change that makes the change, while the impact is fresh — not reconstructed from commit archaeology at release time. Breaking changes get a migration note and a deprecation window (follow the deprecation-and-migration skill); shipping the actual release is the shipping-and-launch skill's job — this section is the versioning contract that feeds it.

Common Rationalizations

RationalizationReality
"I'll commit when the feature is done"One giant commit is impossible to review, debug, or revert. Commit each slice.
"The message doesn't matter"Messages are documentation. Future you (and future agents) will need to understand what changed and why.
"I'll squash it all later"Squashing destroys the development narrative. Prefer clean incremental commits from the start.
"Branches add overhead"Short-lived branches are free and prevent conflicting work from colliding. Long-lived branches are the problem — merge within 1-3 days.
"I'll split this change later"Large changes are harder to review, riskier to deploy, and harder to revert. Split before submitting, not after.
"I don't need a .gitignore"Until .env with production secrets gets committed. Set it up immediately.
"It's just a small fix, bump the patch"Check what consumers can observe. A behavior change they relied on is a major, whatever the diff size.
"The changelog is just the commit log"Commits are for you; the changelog is for consumers, curated by impact. Generating one from raw commits buries what matters.
"We'll write the changelog at release time"By then the impact is reconstructed from memory and half of it is missing. Write the entry with the change.

Red Flags

  • Large uncommitted changes accumulating
  • Commit messages like "fix", "update", "misc"
  • Formatting changes mixed with behavior changes
  • No .gitignore in the project
  • Committing node_modules/, .env, or build artifacts
  • Long-lived branches that diverge significantly from main
  • Force-pushing to shared branches
  • A breaking change shipped under a minor or patch version bump
  • A release with no tag, or a version number hand-edited out of sync with the tag
  • A user-facing release with no changelog entry, or a changelog that's just dumped commit messages

Verification

For every commit:

  • Commit does one logical thing
  • Message explains the why, follows type conventions
  • Tests pass before committing
  • No secrets in the diff
  • No formatting-only changes mixed with behavior changes
  • .gitignore covers standard exclusions

For every release (anything with consumers):

  • The version bump matches the change: breaking → major, additive → minor, fix → patch
  • The release is tagged, and the version is derived from the tag, not hand-edited out of sync
  • The changelog has a curated, human-readable entry grouped by impact for this version

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.