agentsclimarketplace

Git workflow

Skill event4u-app/agent-config/dist/agent-src/skills/git-workflow

Use when working with Git — branch naming, commit messages, PR creation, rebasing, or the code review process — even when the user says 'push this' or 'merge the branch' without naming Git.From its SKILL.md

Install
npx -y skills add event4u-app/agent-config --skill git-workflow

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 7 stars7 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

14.8 KB, ~3.7k tokens by cl100k_base, as published. Nobody here has run it

git-workflow

When to use

Use when preparing PRs, finishing branches, or following the team's Git workflow.

Do NOT use when:

  • Code writing or review (use php-coder or code-review skill)
  • CI/CD pipeline changes (use github-ci skill)

Live remote state first — never from memory

BEFORE ANY MERGE / PUSH / PR / BRANCH ACTION — OR ANY CLAIM OR QUESTION
ABOUT THEIR STATE — QUERY THE LIVE REMOTE. NEVER FROM MEMORY OR
CONVERSATION HISTORY. A PR MAY ALREADY BE MERGED OR CLOSED REMOTELY.
ASKING WHAT `gh pr view` ANSWERS IS A CHEAP QUESTION — CHECK, DON'T ASK.

Local branch view + conversation memory both go stale the moment anyone else — a maintainer, a parallel agent, an auto-merge rule — acts on the remote. Acting or asking on stale state is the recurring failure this kills (canonical: asking "shall I merge these 4 PRs?" when all four were already merged remotely). Run first, every time:

git fetch origin --quiet
gh pr view <number> --json number,state,mergeStateStatus,mergedAt,baseRefName
# state: OPEN | MERGED | CLOSED — act only on the live value
  • A state question is self-answering — never ask "is it merged?", "mergeable?", "did it push?", "still open?". gh pr view / git fetch answers it; asking is a cheap question (per no-cheap-questions).
  • MERGED / CLOSED → nothing to merge or push; report the live state and stop.
  • Before merging → re-fetch + re-read state + mergeStateStatus in the same turn; never merge on a status seen earlier in the conversation.
  • "Based on main" / "current" → prove it with git rev-list --count HEAD..origin/main (after git fetch); non-zero ⇒ behind and not current — merge main in before opening a PR (see /create-pr § 1b).

Conventions

→ See guideline docs/guidelines/php/git.md for branch naming, commit messages, PR conventions. → See commit-conventions rule for commit format, types, and scope rules. → Use conventional-commits-writing skill for generating/reviewing commit messages.

Procedure: Before opening a PR

  1. Quality pipeline + tests — only when quality.local_auto_run: true (see quality-tools § Execution policy): type-checker → auto-fixer → linter → type-checker, then the project's test command (detect from manifest: php artisan test / vendor/bin/phpunit (PHP), npm test / pnpm test / vitest / jest (JS-TS), pytest (Python), cargo test (Rust), go test ./... (Go)). Under the default (false / missing) skip both — remote CI on the PR is the gate; say so instead of claiming they passed.
  2. Rebase onto main.
  3. Fill in PR template completely.

Procedure: Finish a branch

When implementation is complete and all tests pass:

Work complete. What would you like to do?

1. Push and create a Pull Request
2. Keep the branch as-is (I'll handle it later)
3. Discard this work

Option 1: Push and create PR

  1. Run quality pipeline + tests (only when quality.local_auto_run: true; default false → skip, remote CI is the gate).
  2. git push -u origin <branch>.
  3. gh pr create using PR template.

Option 2: Keep as-is

Report: "Branch <name> preserved locally." — do nothing.

Option 3: Discard

Confirm first — list branch name and commit count. Wait for explicit confirmation. Then:

git checkout main
git branch -D <feature-branch>

PR template

The project uses .github/pull_request_template.md:

  1. Jira ticket link (badge)
  2. Description — what and why
  3. Type of change
  4. Checklist (docs, rebase, quality, review, tests, QA)
  5. Links + screenshots

Default branch

  • main is default/production branch.
  • Merge strategy: merge commits (not squash).

Procedure: Safe squash-after-push

Use ONLY when the user explicitly authorized a squash on a branch that is already on origin. The whole sequence runs in one turn — never end the session between rewrite and push.

Trigger context: git-history-discipline rule routed here.

1. Snapshot before touching anything

BRANCH=$(git branch --show-current)
DATE=$(date +%F)
git fetch origin
git tag "safe-squash-pre/${BRANCH}/${DATE}" HEAD
git tag "safe-squash-origin/${BRANCH}/${DATE}" "@{u}"

Two tags = two recoveries (local tip + origin tip). Do not skip the tags — git reflog is TTL-bounded and unreliable across sessions.

2. Verify aligned starting state

git rev-list --left-right --count HEAD...@{u}
  • 0 0 → aligned, proceed.
  • N 0 (local ahead) → unpushed work, proceed.
  • 0 N (origin ahead) → git pull --ff-only first, then re-check.
  • M N (both non-zero) → divergent. Abandon the squash and run § Divergent-State Recovery below.

3. Perform the squash

Default — soft-reset path (single token-cheap rewrite):

git reset --soft "$(git merge-base HEAD <base>)"
git commit -m "<conventional commit message>"

Interactive rebase only when the user wants per-commit control — it replays derived files (.condensation-hashes.json, router projections) per commit and conflicts on every replay.

4. Re-push in the SAME turn

FETCHED_SHA=$(git rev-parse "@{u}")
git push --force-with-lease="${BRANCH}:${FETCHED_SHA}" origin "${BRANCH}"
git fetch origin
[ "$(git rev-parse HEAD)" = "$(git rev-parse @{u})" ] \
  && echo "OK: origin matches HEAD" \
  || echo "MISMATCH — do not end session"

If the push fails (pre-push hook, network, token budget):

  • Fix the underlying cause now.
  • Re-push immediately.
  • Do not commit new work on top of the squashed-but-unpushed tip.
  • Do not end the session until HEAD == @{u}.

5. Hand off only with verified parity

Report exactly:

  • pre-squash tip SHA (from step 1)
  • pre-squash tag name (for recovery)
  • post-squash tip SHA == origin SHA (verified in step 4)
  • PR number, if any, and confirm it picked up the new tip

Procedure: Divergent-State Recovery

Fires when git rev-list --left-right --count HEAD...@{u} shows both sides non-zero on the current branch.

1. Stop. Do not pull.

A blind git pull --rebase here replays remote commits on top of a local history that may already represent the same work in a different shape — guaranteed conflict storm in derived files, possible double-application of the same change. This is the documented failure mode behind git-history-discipline.

2. Tag both sides immediately

TS=$(date +%FT%H%M)
git tag "diverged-local/${TS}" HEAD
git tag "diverged-origin/${TS}" "@{u}"

3. Diagnose: which side is the correct future?

git log --oneline @{u}..HEAD   # local-only commits
git log --oneline HEAD..@{u}   # origin-only commits
git diff @{u}..HEAD --stat     # shape of local-ahead work

Decision matrix:

PatternFutureAction
Local has the same logical work as origin, just reshaped (squash/rebase)LocalAfter PR-review check (step 4), git push --force-with-lease=<branch>:<origin-sha>
Origin has commits local does not reflect (another contributor pushed)OriginTag any local-ahead work for cherry-pick, then git reset --hard @{u}
Both sides have genuine independent workask userNever decide silently — surface the two commit lists and let the user pick

4. PR review-comment check (mandatory before any force-push)

If a PR is open on this branch:

gh pr view --json reviews,comments
# or via GitHub API: /repos/<owner>/<repo>/pulls/<num>/{reviews,comments}

If review comments are anchored to commits that the force-push will erase → STOP, ask the user how to preserve them. A force-push that destroys live review feedback is unrecoverable from the agent side.

5. Recover or proceed

Use the tags from step 2 to restore either side if step 4 surfaces a problem. After resolution, verify HEAD == @{u} and report both SHAs plus the tags created.

Hard prohibitions on a pushed branch

  • No git pull --rebase after detecting divergent state.
  • No git push --force without --force-with-lease=<branch>:<sha>.
  • No squash-then-end-session — the push must complete in the same turn.
  • No reflog-only recovery — always tag the state explicitly first.

Shared-branch & inherited commits — ask-before-drop protocol

Depth for the git-history-discipline Iron Law on inherited & shared-branch commits (migrated per P4 of road-to-kernel-and-router.md).

User often works in parallel with the agent, and multiple agents may share one PR branch. A commit that looks "unrelated" or "stray" may be deliberate in-flight work the user expects to keep. Reseating a branch onto a different base, git reset --hard-ing away inherited commits, force-pushing over a branch you did not create, or branching from a base with unexpected commits and then "cleaning" them out all silently discard work — the exact failure that law prevents.

Before ANY of these, STOP and ask (one numbered-options prompt per user-interaction):

  • reseating a branch's base (git rebase --onto, git reset --hard <other-base>) in a way that drops commits already on the branch;
  • excluding / not-carrying-forward commits that were on the branch when you started this session;
  • force-pushing (or push <local>:<remote>-replacing) a branch carrying commits you did not author;
  • branching from a base with unexpected commits, then resetting them away.

Preserve-first is necessary but not sufficient. Even when you keep commits reachable (a save-branch / tag), you still ask before the branch the user sees loses them — "I preserved them locally" is not a substitute for the question; the user may be mid-edit on the shared branch and a force-push would clobber their in-flight work regardless of your local backup.

Two protective stops (for the protocol phase)

  1. Pre-rewrite stop. Before any squash / amend / rebase on a branch on origin: git fetch && git rev-list --left-right --count HEAD...@{u}. If either side is non-zero — STOP and run § Divergent-State Recovery. A blind git pull --rebase in this state is the documented failure mode. (§ Safe squash-after-push steps 1–2 implement this stop.)

  2. Post-rewrite stop. After the rewrite, push in the same turn with --force-with-lease=<branch>:<fetched-sha> and verify git rev-parse origin/<branch> equals git rev-parse HEAD. If the push fails (hook, network, token budget) — fix the cause and re-push before ending the session, committing new work, or handing off. (§ Safe squash-after-push step 4 implements this stop.)

If either stop fires and resolution is not immediate → tag the state (git tag local-rewritten-tip-<ISO-date>) and hand control back to the user. Do not let a new session inherit a dirty divergence.

Equivalents that are also forbidden by default

  • git rebase -i (interactive)
  • git rebase --autosquash
  • git commit --fixup / --squash (helpers that feed autosquash)
  • git commit --amend on already-pushed commits
  • git push --force / --force-with-lease (unless paired with the protocol)
  • git reset --hard past unpushed work the user might want
  • Squash-merge of a PR via API or CLI when the user has not picked the merge strategy
  • Cherry-pick rewriting that drops or reorders commits

--amend on the current local commit before the first push is the narrow exception (treated as continuing to compose the commit, not rewriting history).

Amend-after-hook-failure trap (data-loss)

When a pre-commit hook fails, the commit did NOT happen — no new commit object was created. A reflexive git commit --amend at that point does not "retry the commit"; it rewrites the previous, already-good commit, destroying that work. This is the one place the narrow --amend exception above turns into data loss.

Recovery — never amend after a hook failure:

  1. Read the hook output and fix the cause (the lint/test/format failure).
  2. Re-stage the fix (git add).
  3. Create a NEW commit (git commit, not --amend) — the prior commit was never overwritten and must stay intact.

(Migrated from git-history-discipline — recovery now lives next to the mechanism it protects.)

Why history discipline exists

Interactive rebase + fixup loops generate disproportionate token cost every iteration: re-running CI per replayed commit, resolving the same content conflict in three derived files (.condensation-hashes.json, dist/router.json, .windsurfrules), losing the working tree to a stash that silently re-introduces older state. A single conflict can burn the budget of an entire feature.

A previous session squashed a pushed branch, the push hook failed at the token boundary, the session ended — and the next session saw local and origin pointing at different SHAs for the same logical work. A blind git pull --rebase cascaded into conflicts across every derived file. Recovery required forensic SHA-archaeology. The pre/post-rewrite stops make that sequence structurally impossible.

When you'd be tempted

  • "I want commit 3 to come before commit 2 because the topic flows better." → don't. Reviewers read the PR diff.
  • "There are two chore: regenerate commits, ugly." → don't. They are honest checkpoints.
  • "A linter caught an issue in commit 2 — let me fold the fix in." → don't. Add fix(scope): … on top.
  • "I want to drop the WIP commit before pushing." → ask the user first.
  • "Squash-merge when I open the PR will clean it anyway." → also true, also irrelevant — let the merge strategy do that work, not you.
  • "My branch inherited some unrelated commits — I'll reseat it on origin/main so my PR is clean." → don't, ask first. May be the user's parallel work or another agent's. Preserve them and ask which base the user wants.
  • "The remote branch has commits I didn't author and no PR — I'll just force-push over it." → don't. No-PR is not no-owner; ask before replacing a branch you did not create.

Output format

  1. Commits following conventional commit format
  2. PR description with structured sections (if creating PR)

Gotcha

  • Never commit/push/merge without explicit user permission.
  • Keep subject line under 72 chars.
  • Don't rebase shared branches.
  • git stash can lose work — prefer WIP commits.

Do NOT

  • Do NOT commit directly to main.
  • Do NOT push without running quality tools first.
  • Do NOT force-push to shared branches.

Auto-trigger keywords

  • Git workflow
  • branch naming
  • commit message
  • PR convention

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.