# Git Flow Pro

> Handle git hygiene — write commit messages that explain the why, name branches that match the issue, draft PR descriptions reviewers actually read. Knows Conventional Commits, semantic branch names, and how to break a noisy WIP into clean atomic commits. Use when the user says "commit this", "make a PR", "write the commit message", "draft a PR description", "clean up my branch", "rebase this", or finishes work and wants it shipped.

- Skill: `ak-ship/git-flow-pro` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ak-ship/git-flow-pro`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ak-ship/git-flow-pro/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ak-ship (https://skillmd.com/u/ak-ship)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ak-ship/git-flow-pro

---


# git-flow-pro — small, clear commits; PRs that get reviewed

## When to use this skill

Trigger when the user wants to package work for review or history. Strong signals:

- "commit this", "make a commit", "git commit"
- "open a PR", "draft a PR description"
- "clean up my branch", "squash these", "rebase onto main"
- "what should I name this branch?"

Do *not* trigger for: pushing to main (require confirmation), force-pushing to shared branches (require confirmation), or rewriting published history (require confirmation).

## The output contract

Git artifacts that:

1. **Tell a story** — the commit log reads like a changelog. Each commit is one logical change.
2. **Match conventions** — Conventional Commits (or whatever the repo uses), the team's branch naming, the team's PR template.
3. **Explain the *why*** — not the diff. The diff already shows what changed.
4. **Stay safe** — no force-push to shared branches without explicit confirmation; no rewriting commits that have been reviewed.

## Workflow

### 1 — Sense the conventions

Before writing anything:

- `git log --oneline -20` — what does this project's history look like? Conventional Commits (`feat:`, `fix:`)? Imperative present tense? Past tense?
- `cat .github/pull_request_template.md 2>/dev/null` — is there a PR template?
- `git branch -r --contains HEAD | head` — what's the trunk branch called (`main`, `master`, `trunk`, `develop`)?
- `git config user.email` and the recent author list — match the style of the current contributors.

Mirror what's there. Don't introduce Conventional Commits to a project that uses prose, and vice versa.

### 2 — Stage with intent

Read `git status` and `git diff --cached` (and unstaged). Group changes by intent:

- One feature → one commit
- A test for that feature → squashed into the same commit, or right after
- An unrelated typo fix you noticed → separate commit
- Generated files (`package-lock.json`, etc.) → include if they belong to the same change

Use `git add -p` mentally — pick hunks per commit, not whole files when the file has mixed concerns.

### 3 — Write the commit message

Format (when Conventional Commits is in use):

```
<type>(<scope>): <imperative present-tense subject, < 72 chars>

<body — what changed and *why*, wrapped at 72 chars>

<footer — Refs, Closes, Co-authored-by, BREAKING CHANGE>
```

Types: `feat`, `fix`, `refactor`, `perf`, `test`, `docs`, `build`, `ci`, `chore`, `style`, `revert`.

Subject rules:
- Imperative present: "add" not "added"/"adds"
- No trailing period
- Capitalize first letter after the colon: `feat(auth): Add password reset` — or all-lowercase, whatever the repo does
- Under 72 chars

Body rules:
- Skip the body for trivial commits (typo, comment, version bump)
- Otherwise: 1–3 short paragraphs. Lead with *why* this change was needed, then *what* changed if non-obvious. The diff covers the rest.
- Reference issues at the end: `Refs #1234`, `Closes #567`

If the project doesn't use Conventional Commits, drop the type prefix but keep the imperative subject rule.

### 4 — Branch naming

Match the team's style. Common patterns (use whichever the repo's branch list shows):

- `<type>/<short-slug>`: `feat/password-reset`, `fix/login-redirect`
- `<author>/<short-slug>`: `ashish/password-reset`
- `<issue>-<slug>`: `1234-password-reset`

Keep slugs short: 2–4 words, kebab-case, no full sentences.

### 5 — PR description

Use the template if one exists. Otherwise default to:

```
## What this PR does

<2–4 sentences. The user-visible change first, the technical change second.>

## Why

<The motivation — the bug, the requirement, the upstream blocker. Link the issue.>

## How

<Only if non-obvious. Skip if the diff is self-explanatory.>

## Testing

- [ ] What you verified manually
- [ ] What's covered by automated tests
- [ ] What you explicitly did *not* test

## Risk

<New endpoints? Schema migration? Removed code? If none — write "Low — pure additive change.">
```

Always link the issue. Always include a screenshot/GIF if the change is visual.

### 6 — Cleaning up a noisy branch

When the user has 12 WIP commits to squash before merging:

1. `git log --oneline <base>..HEAD` — list what's there.
2. Identify the *logical* commits (usually 1–4). Don't squash everything to one — atomic history is more useful than a single squash.
3. `git rebase -i <base>` — use `pick` for the first commit of each group, `squash` for the rest. Rewrite messages on the way through.
4. After rebase, force-push with lease: `git push --force-with-lease`. **Never** plain `--force` on a shared branch.

### 7 — Safety rails

Before any destructive operation, confirm explicitly with the user:

- Force-push to a branch other than your own personal branch
- `git reset --hard`
- `git rebase` of commits that have already been reviewed
- `git push` to `main`/`master`/`production`

## Patterns and anti-patterns

✅ **Do**:
- Commit early, commit often, *then* squash.
- Keep commits buildable individually — bisect needs that to work.
- Sign commits if the repo requires it (`git config commit.gpgsign true`).
- Use `Co-authored-by:` trailers when pairing.

❌ **Don't**:
- Don't write commit messages that say "fix bug" or "WIP". Future-you will hate present-you.
- Don't `git push --force` without `--force-with-lease`. The lease prevents wiping out someone else's pushed commit.
- Don't include unrelated changes in a PR ("while I was here, I also..."). They belong in a separate PR.
- Don't write a PR description that just lists the files changed. The reviewer can see that.

## Example invocation

> User: "I'm done with the password reset feature. Make a clean commit and open a PR."

1. `git status`: 7 files changed, on branch `ashish/wip`.
2. `git log --oneline -20`: project uses Conventional Commits, subjects in imperative.
3. Group: 6 files are the feature, 1 is a typo fix in an unrelated component.
4. Commit 1: `chore: fix typo in login error message`. Commit 2: `feat(auth): add password reset endpoint and email flow`.
5. Branch is currently `ashish/wip` — rename to `feat/password-reset` (`git branch -m`).
6. Push: `git push -u origin feat/password-reset`.
7. PR description: 2-paragraph "what" + "why", link to issue #1234, testing checklist (manual: reset email arrives, link works, password updates; automated: unit + e2e covered).
8. Open the PR via `gh pr create` with the body above.

## See also

- `code-auditor` — run a final review before the PR is opened
- `doc-craft` — for the README/CHANGELOG update that should accompany a feature
- `ship-it` — to verify the PR's CI is green before merging

