Problem
GitHub branch protection requires all commits to have verified GPG signatures. Two commits at the base of the branch (made before GPG signing was configured) were unsigned, blocking the merge.
Root cause
Commits made before commit.gpgsign=true was configured in git have no GPG signature. GitHub requires all commits in a PR to be verified - one unsigned commit blocks the merge regardless of approvals.
Steps to fix
1. Identify unsigned commits
git log --format="%G? %ad %s" --date=short origin/main..HEAD
Look for lines starting with N (no signature). Note the hash of the oldest unsigned commit’s parent on main.
2. Re-sign all branch commits using filter-branch
git filter-branch -f --commit-filter 'git commit-tree -S "$@"' -- origin/main..HEAD
Rewrites all branch commits adding a GPG signature - no content changes, no conflicts.
3. Verify no unsigned commits remain
git log --format="%G?" origin/main..HEAD | Select-String "^N$"
Should return nothing.
4. Force push
Attempt force push via terminal using the configured remote:
git push --force origin <branch>
If this fails with “repository not found” (HTTPS remote, no credentials) or “Could not read from remote repository” (SSH not configured), use a classic PAT with repo scope directly in the URL:
git push --force https://<username>:<PAT>@github.com/<org>/<repo>.git <branch>
5. Update local tracking ref (if GitHub Desktop shows stale state)
git update-ref refs/remotes/origin/<branch> <tip-commit-hash>
Notes
- Avoid
git rebase --exec "git commit --amend -S"if the branch has merge commits - it drops them and causes conflicts - Avoid
git rebase --rebase-merges- re-applying old merge commits causes conflicts from the original merges filter-branchrewrites commit objects without replaying merges, which is what’s needed here- Fine-grained PATs do not work for org repositories - use classic PAT only
- After force pushing, avoid
git pull/ IDE auto-sync until the PR is merged - it will re-introduce the old unsigned commits via a new merge