Skip to content

Put the translation commit on top when the branch moved during the run - #121

Merged
arvida merged 1 commit into
mainfrom
fix/866-push-branch-moved
Oct 8, 2026
Merged

arvida merged 1 commit into
mainfrom
fix/866-push-branch-moved

Conversation

@arvida

@arvida arvida commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Fixes the CLI half of localheroai/localhero-ai#866. Plan: dev-docs/specs/866-git-push-branch-moved.md (reviewed by Codex twice before implementation).

The bug

On newsroom-demo, two matrix jobs (one per app) committed to the same PR branch. The second git push was rejected because the branch had moved, and pushWithRetry re-sent the same commit three times, then failed with "Check that the workflow grants permissions: contents: write", which was not the problem. That app's translations were lost. The same happens in any repo when a developer pushes while a run commits. The signed-commit path already handled this (#90); the plain git path did not.

The change

  • Classify a failed push by git's output (captured with 2>&1, printed with the token masked):
    • branch moved (fetch first, non-fast-forward, cannot lock ref, stale info) → rebaseOntoMovedBranch
    • permission denied (Permission to … denied, 403, could not read Username) → PushPermissionError at once, never a skip
    • anything else → the existing 3-attempt retry
  • rebaseOntoMovedBranch, the plain-git counterpart of findTipSafeToCommitOn:
    • fetch, and only continue if our commit's parent is an ancestor of the new tip (moved forward only, so nothing someone removed comes back)
    • compare files with --no-renames; any overlap → skipped-overlap
    • otherwise rebase --autostash --onto FETCH_HEAD HEAD~1 (replays only our commit; translate leaves localhero.json unstaged) and push again, at most twice
  • Amend pushes (legacy config-trigger syncs) never replay: a moved branch → skip, a permission failure → error.
  • Annotation: the contents: write hint only for permission failures (PushPermissionError, or a signed commit refused with FORBIDDEN/403). Other failures show git's output with no invented hint.

No force push, pull, reset or merge on our side; the tests assert that.

Verified

  • Real git (bare remote, two clones): replay, replay with an unstaged localhero.json, overlap, rename, force-pushed branch, fetch failure.
  • Classification with real git output samples, through autoCommitChanges / autoCommitSyncChanges, including permission-denied-while-moved and the amend path.
  • Mutations caught: removing the amend guard, classifying permission failures as moved, dropping the forward-only check.
  • Full suite: 1495 passed. Code-reviewer agent and two Codex reviews; their findings (renames, the unstaged localhero.json, the signed-commit hint) are fixed with tests.

Before release

RC on newsroom-demo with parallel matrix jobs (drop max-parallel: 1, keep cancel-in-progress: false): both apps' keys translated, one job shows "replayed the translation commit onto …". Plus a single-project regression run on demo-rails.

README guidance: localheroai/localhero-action#8 (draft until this ships).

- A push rejected because the branch moved now fetches the branch and
  replays our single commit onto the new tip, then pushes again, when the
  branch only moved forward and the newer commits left our files alone
  (localheroai/localhero-ai#866). The signed-commit path already did this
- A rewritten branch, an overlapping file or anything uncertain skips with
  the existing branch-changed notice instead of failing or overwriting
- Failed pushes are classified from git's output: a permission failure
  fails at once, other failures keep the bounded retry
- The contents: write hint only appears for permission failures
- Real-git tests cover replay, overlap, renames, a rewritten branch and the
  unstaged localhero.json translate leaves behind
@arvida
arvida merged commit e633862 into main Oct 8, 2026
1 check passed
@arvida arvida mentioned this pull request Oct 8, 2026
arvida added a commit that referenced this pull request Oct 8, 2026
- A push that loses the race to another push replays its translation
  commit on top and pushes again (#121)
- The contents: write hint only appears for permission failures
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant