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.
Scanned 6/5/2026
Install via CLI
openskills install event4u-app/agent-config---
model_tier: medium
name: git-workflow
description: "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."
domain: process
execution:
type: assisted
handler: internal
allowed_tools: ["github"]
workspaces:
- engineering
packs:
- engineering-base
---
# 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)
## 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. Run the project's quality pipeline (see `quality-tools` skill) — typically: type-checker → auto-fixer → linter → type-checker.
2. Run 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).
3. Rebase onto `main`.
4. 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.
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:
```bash
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
```bash
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
```bash
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):
```bash
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
```bash
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
```bash
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?
```bash
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:
| Pattern | Future | Action |
|---|---|---|
| Local has the same logical work as origin, just reshaped (squash/rebase) | **Local** | After PR-review check (step 4), `git push --force-with-lease=<branch>:<origin-sha>` |
| Origin has commits local does not reflect (another contributor pushed) | **Origin** | Tag any local-ahead work for cherry-pick, then `git reset --hard @{u}` |
| Both sides have genuine independent work | **ask user** | Never 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:
```bash
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.
## 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
No comments yet. Be the first to comment!