| name | git-workflow |
| description | Reference guide for the project's Git workflow — branch strategy, commit style, PR lifecycle, rebase vs merge, history rewriting rules, git hooks, stash/worktree/bisect/reflog/cherry-pick, plus the enforced never-push-to-main + CodeRabbit + pre-commit/pre-push hook policy. Activates when creating branches, writing commits, or opening PRs. Use when this capability is needed. |
| metadata | {"author":"ClankerGuru"} |
Git Workflow
For related topics see:
/gh-cli — gh pr create, gh pr merge --auto, CodeRabbit queries
/github-actions — CI triggers that gate every PR
/opsx-propose — change-lifecycle integration with branches
Project rules (non-negotiable)
- Never push directly to
main. Always branch + PR.
- Never include AI / Claude / Anthropic branding in commit
messages, PR titles, PR descriptions, comments, or any content
pushed to the repo. No "Generated with Claude Code", no
Co-Authored-By: Claude, nothing.
- Always branch + PR, even for a one-line fix.
- Wait for CodeRabbit and address every comment before merge.
- Prove it works — run the feature end-to-end before pushing and
include evidence (logs, output) in the PR body.
- Don't skip hooks with
--no-verify. The pre-commit hook runs
the full build (./gradlew build): compile, test, detekt, ktlint,
coverage. If the hook fails, fix the cause.
- Don't force-push to
main. Force-push feature branches only
when the PR still belongs to you and hasn't been reviewed.
Branch strategy
Trunk-based: one long-lived branch (main). All work lives on
short-lived feature branches that merge back via PR and delete on
merge.
git checkout -b feature/add-retry-logic
git checkout -b fix/null-pointer-in-sync
git checkout -b change/catalog-polish
Naming prefixes — project convention:
| Prefix | Use for |
|---|
feature/ | New functionality |
fix/ | Bug fixes |
change/ | opsx-tracked changes |
chore/ | Dep bumps, tooling |
docs/ | Documentation only |
refactor/ | Non-behavioural restructuring |
Keep branches short-lived (hours to days). A branch open for two weeks
is a merge-conflict factory — rebase frequently.
Commit messages
First line under 72 characters, imperative mood, no trailing period.
Body wraps at 72 columns. Explain why, not just what.
Add exponential backoff to sync task
Sync was failing silently on transient network errors. Added retry
with backoff (100ms, 200ms, 400ms) and clear error messages when all
retries are exhausted.
Fixes #142
Short fixes can be one-liners:
Fix null branch name in checkout task
Bad (rejected in review):
Fix stuff # vague
WIP # not a commit
Update code # says nothing
Co-Authored-By: Claude <...> # forbidden AI attribution
Generated with Claude Code # forbidden
The PR lifecycle
git checkout main
git pull --ff-only origin main
git checkout -b feature/add-retry-logic
git add -p
git commit -m "Add exponential backoff to sync task"
git push -u origin feature/add-retry-logic
gh pr create --title "Add retry logic to sync task" --body "..."
gh pr checks <n> --watch
git commit --fixup <hash>
git push
gh pr merge <n> --squash --delete-branch
gh pr merge <n> --squash --delete-branch --auto
--squash is the default strategy here — it flattens review-round
commits into a single on-main commit that matches the PR title and
body.
Staging and committing
git status
git diff
git diff --staged
git diff main...HEAD
git add path/file.kt
git add -p
git add -A
git restore --staged path/file.kt
git restore path/file.kt
git commit -m "..."
git commit --amend
git commit --fixup <sha>
git commit --no-verify
Keeping a branch fresh — rebase, not merge
git fetch origin
git rebase origin/main
git add <resolved-files>
git rebase --continue
git rebase --abort
Force-push your branch after a rebase (only yours, not shared):
git push --force-with-lease
Don't git pull on a feature branch. It creates a merge commit
from origin/<branch> into your local copy. Prefer git pull --rebase
or explicit git fetch + git rebase.
Interactive rebase — clean up before review
git rebase -i origin/main
Commands: pick, reword, edit, squash (merge + edit message),
fixup (merge silently), drop, exec.
Fixup autosquash workflow:
git commit --fixup <old-sha>
git commit --fixup <old-sha>
git rebase -i --autosquash origin/main
Interactive rebase is safe only on unshared history. Once pushed and
reviewed, prefer adding new commits.
Merge vs rebase — when to use which
- Rebase your PR branch onto
main before review and before merge.
Clean history, linear graph.
- Merge happens automatically via
gh pr merge --squash. The
squash merge keeps main linear.
- Never rebase a shared/public branch (anything other contributors
or CI have based work on).
- Never merge
main into your branch repeatedly — it bloats the
history with noise merges. Rebase instead.
History-rewriting rules:
- Rewrite your own un-pushed / un-reviewed commits freely.
- Once a PR is open and reviewed, prefer new commits (
--fixup) over
force-push; reviewers can see each round.
- Once merged to
main, history is immutable. Never force-push
main.
Tag-based releases
git tag v0.41.0
git push origin v0.41.0
gh release create v0.41.0 --title "v0.41.0" --generate-notes
gh release create v0.41.0 --title "v0.41.0" --generate-notes
Tag naming: v<major>.<minor>.<patch> (SemVer). The release workflow
strips the v and validates SemVer — v0.41 or release-41 are
rejected.
Pushing a bare tag does not trigger release.yml — it triggers on
release: published. Always create the release.
Branch cleanup
git checkout main
git pull --ff-only origin main
git branch -d feature/add-retry-logic
git branch -D feature/abandoned
git fetch --prune
git branch --merged main
git branch --merged main | grep -vE '(^\*|main)' | xargs -r git branch -d
Git hooks (project)
The repo ships hooks under config/hooks/. Point git at them:
git config core.hooksPath config/hooks
What they do:
- pre-commit — runs
./gradlew build (compile, test, detekt,
ktlint, Kover verify). Blocks the commit on any failure.
- pre-push — blocks direct pushes to
main. Forces the PR flow.
Don't disable or bypass. If a hook is slow, fix the build, don't skip
the check.
Stash — short-lived detours
git stash push -m "halfway through retry logic"
git stash push --keep-index
git stash push -u
git stash list
git stash show -p stash@{0}
git stash apply stash@{0}
git stash pop
git stash drop stash@{0}
git stash clear
Stash is for quick context switches. For longer parking, make a commit
on a branch and come back to it.
Worktrees — multiple checkouts of one repo
git worktree add ../hotfix main
git worktree add ../review-42 --detach $(git rev-parse origin/pr/42)
git worktree list
git worktree remove ../hotfix
git worktree prune
Worktrees share the same .git directory — objects and refs are
shared. Great for reviewing a PR without blowing away your current
state.
Inspecting history
git log --oneline --graph --decorate --all
git log --author="slop"
git log --since="2 weeks ago"
git log --grep "retry"
git log -S "runCatching" -- src/
git log --follow path/File.kt
git log main..feature/x
git log feature/x...main
git show <sha>
git show <sha>:path/file.kt
git blame path/file.kt -L 100,150
git blame -w -C -C -C path/file.kt
git shortlog -sn --all
git bisect — hunt a regression
git bisect start
git bisect bad HEAD
git bisect good v0.40.0
./repro-script.sh
git bisect bad
git bisect reset
git bisect start HEAD v0.40.0
git bisect run ./scripts/repro.sh
git reflog — the recovery tool
git reflog
git reflog show feature/x
git reset --hard HEAD@{3}
git checkout HEAD@{1}
git reflog | grep feature/lost
git branch feature/lost <sha>
Almost nothing in git is irrecoverable until git gc runs. When in
doubt, check reflog first.
Cherry-pick — specific commits between branches
git cherry-pick <sha>
git cherry-pick <sha1>..<sha2>
git cherry-pick -x <sha>
git cherry-pick --continue / --abort / --skip
Use for backporting a hotfix to a release branch. Don't abuse for
general branch sync — rebase is cleaner.
Reset, revert, restore
git reset --soft <sha>
git reset --mixed <sha>
git reset --hard <sha>
git revert <sha>
git restore path/file.kt
git restore --staged path/file.kt
git restore --source=<sha> path/file.kt
Never git reset --hard a branch that's pushed; use git revert
instead so collaborators don't lose commits.
Remote management
git remote -v
git remote add upstream https://github.com/org/upstream-repo
git remote set-url origin git@github.com:org/repo.git
git fetch --all --prune
git fetch upstream main:upstream-main
For forks: origin = your fork, upstream = source of truth. Rebase
onto upstream/main before opening PRs.
Config essentials
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git config --global pull.rebase true
git config --global rebase.autoStash true
git config --global rerere.enabled true
git config --global diff.colorMoved zebra
git config --global fetch.prune true
git config --global push.default simple
git config --global push.autoSetupRemote true
Local (per repo) overrides drop the --global.
Anti-patterns
git push origin main:main
git commit --no-verify -m "ship it"
git push --force origin main
git commit --amend --no-edit
git push --force-with-lease
git merge main
git rebase origin/main
git commit -m "stuff"
git commit -m "wip"
git commit -m "fix bug"
git commit -m "fix(something)
Co-Authored-By: Claude <...>"
gh pr merge 42 --squash
Common pitfalls
- Detached HEAD after
git checkout <sha> — make a branch before
committing, or the commits are reachable only via reflog.
git pull creates a merge commit — set pull.rebase true or
use git pull --rebase.
git push --force vs --force-with-lease — always prefer the
latter. It refuses the push if the remote moved, preventing
silent overwrites of others' commits.
- Line-ending churn on Windows/macOS — set
core.autocrlf = input
on Windows and commit a .gitattributes for text files.
- Large files sneaking in — once pushed, they stay in history.
Audit
git log --stat before merging; use git lfs for anything
10 MB.
- Merge conflict markers committed — grep for
<<<<<<< pre-commit
(the project's hook runs a full build which compiles Kotlin and
catches this, but YAML/Markdown slip through).
- Rebased a shared branch, broke co-workers — never rebase a branch
someone else has checked out. Communicate before rebasing review
feedback if others are collaborating on the branch.
References
Source: ClankerGuru/opsx — distributed by TomeVault.