name: git-workflow description: Git workflow conventions — commit hygiene, squashing, force-push, and interactive rebase patterns.
Git Workflow
Follow these conventions for all git operations. Keep history clean.
1. Commit Messages
Use conventional commit format:
type: short description
Optional body with details.
Types: feat, fix, refactor, docs, test, bench, chore
git commit -m "feat: add eager loading with .with() API"
git commit -m "fix: quote SQL reserved words in proxy query"
git commit -m "refactor: split integration tests into focused files"
git commit -m "test: add reactivity tests for reactive:false mode"
Multi-line for bigger changes:
git commit -m "refactor: split integration tests into focused files
Tests:
- Replaced 613-line monolith with 5 focused files
- Added setup.ts with factory for test isolation
- 95 tests pass across 8 files"
2. Commit Granularity
- One logical change per commit. Don't mix unrelated refactors with features.
- Commit frequently while working, then squash before pushing (see below).
- Never leave broken commits in the history — every commit should pass tests.
3. Squashing Commits
When multiple small commits should be one clean commit, use git reset --soft:
Method A: git reset --soft (preferred — simplest)
# Squash last 3 commits into one (keeps all changes staged)
git reset --soft HEAD~3
git commit -m "feat: add new feature with tests and docs"
This is the default approach. It's simple, predictable, and doesn't need scripts.
Preserving timestamps when squashing
By default, squashed commits get the current time. To keep the original earliest commit's timestamp:
# 1. Grab the timestamp of the oldest commit being squashed BEFORE resetting
$ORIG_DATE = git log --format=%aI HEAD~2 | Select-Object -Last 1
# 2. Soft reset and recommit with the original date
git reset --soft HEAD~3
$env:GIT_COMMITTER_DATE = $ORIG_DATE
git commit --date=$ORIG_DATE -m "feat: preserves original timestamp"
Remove-Item Env:\GIT_COMMITTER_DATE
--datesets the author date (when the work was done)GIT_COMMITTER_DATEsets the committer date (when it was applied)- Both should match for a clean history
Method B: Interactive rebase with PowerShell script (Windows)
When you need to squash specific commits (not just the last N), or reorder commits, use interactive rebase. On Windows, GIT_SEQUENCE_EDITOR needs a PowerShell script to automate the todo file edits:
Step 1: Create a temporary rebase editor script:
# Create the script (adjusts the rebase todo to squash all into first)
@'
$file = $args[0]
$lines = Get-Content $file
$newLines = @()
for ($i = 0; $i -lt $lines.Count; $i++) {
if ($i -eq 0) {
$newLines += $lines[$i] # keep first as 'pick'
} elseif ($lines[$i] -match '^pick ') {
$newLines += $lines[$i] -replace '^pick ', 'squash '
} else {
$newLines += $lines[$i]
}
}
$newLines | Set-Content $file
'@ | Set-Content rebase-squash.ps1
Step 2: Run the interactive rebase:
$env:GIT_SEQUENCE_EDITOR = 'powershell -File "./rebase-squash.ps1"'
git rebase -i HEAD~3
# Then edit the commit message when prompted
Step 3: Clean up:
Remove-Item rebase-squash.ps1
Use Method A for straightforward "squash last N" cases. Use Method B only when you need to cherry-pick which commits to squash/reorder.
Force-push after squash
# Safe force push (won't overwrite others' work)
git push --force-with-lease
When to squash:
- WIP/fixup commits before pushing to remote
- "fix typo" or "address review" follow-ups
- Multiple small steps that form one logical change
- When user asks to clean up history
Never squash:
- Already-pushed commits that others may have pulled (unless solo project)
- Commits across different logical changes
4. Amending the Last Commit
For small fixes to the most recent commit:
# Add changes to last commit (keep same message)
git add -A && git commit --amend --no-edit
# Add changes and update message
git add -A && git commit --amend -m "feat: better description"
# Then force push
git push --force-with-lease
5. Standard Workflow
Making changes
# 1. Make your changes
# 2. Stage and commit
git add -A && git commit -m "feat: description"
# 3. Push
git push
Multiple related changes (squash before push)
# Work in progress...
git add -A && git commit -m "wip: initial implementation"
# more work...
git add -A && git commit -m "wip: add tests"
# more work...
git add -A && git commit -m "wip: fix edge case"
# Squash all 3 into one clean commit before pushing
git reset --soft HEAD~3
git commit -m "feat: add feature X with tests"
git push
Fix something after push
# Make the fix
git add -A && git commit --amend --no-edit
git push --force-with-lease
6. Checking State
git status # what's changed
git log --oneline -5 # recent commits
git diff # unstaged changes
git diff --staged # staged changes
7. Rules
Always use
--force-with-leaseinstead of--force— it's safer.Never
cdin commands — use theCwdparameter instead.Check
git log --oneline -5before squashing to verify which commits to combine.Run tests before committing to ensure the commit is clean.
Don't commit generated files — check
.gitignorecoversdist/,node_modules/,*.db, etc.Commit message titles should not reference internal folder names — use the project/library name or describe the change generically.
Commit messages must not reference internal reasoning — never mention conversation details, techniques used during development (e.g. "before/after hook", "pedant avoidance"), or implementation strategies. Messages describe what changed, not why you chose that approach.
Never commit draft/internal files — social media posts, internal notes, conversation logs, and similar files must never be staged. Add them to
.gitignorebefore they can accidentally be committed.Always use the correct git identity — author must be
7flash <7flash@users.noreply.github.com>. If rewriting history or amending commits, ensure this identity is used. Before any push, verify withgit config user.nameandgit config user.email. If wrong, fix withgit config --global user.name "7flash"andgit config --global user.email "7flash@users.noreply.github.com".No trivial standalone commits — never commit a few-line change (dependency bump, typo fix, config tweak, docs one-liner) as a separate commit. Always
--amendit into the parent commit or batch it with the related feature/refactor. If you already committed it, squash before pushing. Each commit in the history should represent a substantial, meaningful unit of work.Always commit before moving on — IMMEDIATELY after completing any task (feature, fix, refactor), commit all pending changes in the affected repo(s). Do NOT move on to the next task without committing first. Do NOT wait until the end of the conversation. Check
git statusin each modified repo. If working across multiple projects (e.g.starwar+geeksy-pumpfun-plugin), commit each one. This is non-negotiable — uncommitted work is lost work.Before any "what's next" or continuation request, check for uncommitted work first — If
git status --shortshows pending changes in the active repo, STOP and commit them before implementing anything else. This applies even if the user just says "continue", "keep going", or "what's next".Work on the default branch directly unless the user explicitly asks for branches — Do not create or stay on feature branches by default. Commit on the repo's default branch (
mainormaster). If the repo has nomainbut does havemaster, usemasterand say so plainly.NEVER commit secrets or config files with private keys — Before EVERY
git add -A, verify.gitignoreincludes ALL sensitive files. The following MUST ALWAYS be gitignored:.config.toml— often contains private keys, RPC endpoints, API secrets.env,.env.*— environment variables with secretswallets.json— wallet private keys*_state*.json,*_queue*.json— runtime state with wallet addresses*.db— databases that may contain user data- Any file referenced by code as containing keys, passwords, or tokens
Before your first commit in a repo, run
git statusand scan the list for ANY file that could contain secrets. If.gitignoredoesn't cover it, add it BEFORE staging. If secrets were already committed, usegit filter-repo --invert-paths --path <file> --forceto purge from ALL history.