Forced pushes are a process smell
Over-reliance on forced pushes isn't an agent quirk to work around; it's a sign of a bad coding practice for agents or humans.
I recently picked up a bit of work that I’d set down for a week and finished it off. It’s a branch on my fork of a fairly active project; upstream is the main project repo and origin is my fork. It was time to wrap up and push the work to my fork, then put up a PR.
origin/main from upstream; please rebase this worktree/branch.… we have to force push one simple commit to manage a rebase where no one’s touched anything we’re working on? That makes no sense, but I’ll be the first to admit that my git expertise is strongest with a small core of git commands. There have definitely been times when I’ve gotten lost in a detached HEAD state or struggled with a three-way merge, and I’ve gotten very, very familiar with the many faces of git reset to clean up a mistake. I can easily imagine that Claude’s right and I just don’t understand why. Since Claude’s a patient teacher, I started asking why.
GAAAAHHHHH!! Enough already! Time to get tight, terse, and walk through this step by step.
--force because you rewrote or amended a commit?After all the words, it took two questions to get to the root of the problem, a rewritten commit after the original had been pushed. Easy to unwind, which we did, no --force needed.
There are a lot of varied feelings about the balance of many commits and a squash vs. fewer commits and a straight merge; I’m not here to litigate that debate. I’m definitely on the frequent small commits side, though, for several reasons:
- I can easily push progress up so I can pull it down into other workspaces or branches
- I can see my iterative progress, and doing so can often see the better path
- When I introduce a bug or break something, it’s not hard to run back to the last known working state
- When I’m working with others, I can see the back and forth which often kicks an intuition about something we missed or what to do next
And there’s one other thing, which feels both silly but worth saying out loud: when I’m working with a coding agent like Claude, I can see the value of my time reviewing, redirecting, and improving things. It’s personally rewarding to see where my work made our work better.
So, ultimately, we added this memory:
---
name: prefer-followup-commits-on-pushed-branches
description: "On an already-pushed branch, add a new commit and integrate upstream by merging — never amend/rebase/reset pushed commits (that forces a push)"
metadata:
node_type: memory
type: feedback
originSessionId: [...]
---
Once any commit on a branch has been pushed (or the branch backs an open PR), never rewrite it — no amend, no rebase, no reset. Rewriting a pushed commit changes its SHA, so the pushed tip is no longer an ancestor of the new tip and the next push is not a fast-forward: it demands `--force`. This applies to *every* reason to touch history, not just review feedback:
- **Review feedback / follow-up changes:** add a new follow-up commit on top.
- **Integrating updated `main` into the pushed branch:** `git merge origin/main` (a merge commit keeps the pushed tip as a parent → fast-forward push). Do NOT `git rebase origin/main` — that rewrites the pushed commit and forces the push. This is the exact trap hit on fix/2112 (2026-07-11): a pushed commit was rebased onto refreshed main, then required a force-push to publish.
**Why:** Nothing in the project's AGENTS.md or CONTRIBUTING.md mandates squashing, amending, or a linear branch history — amending/rebasing was my own default, and Jim prefers a clean fast-forward with the delta legible as its own commit. Force-pushes are separately gated behind explicit human confirmation per Jim's CLAUDE.md, so any workflow that *requires* one on a shared branch is the wrong default.
**How to apply:** Amend/rebase/reset are fine only for commits that are still local-only (never pushed). The moment a commit is pushed, treat its history as frozen: make changes as a new commit and pull in upstream via merge, both conventional-commit + `--signoff`, GPG-signed, pushed as a fast-forward with no `--force`. If you believe a rewrite is genuinely needed on a pushed branch, stop and ask first. See [[never-submit-prs-without-permission]].
Lesson learned, on both sides I think.