Preserve force-with-lease rejection details in push errors (#36027)

This commit is contained in:
Jinjing
2026-10-29 10:59:55 -07:00
committed by GitHub
parent bc228ff5cb
commit e182f1daa2
3 changed files with 27 additions and 3 deletions
@@ -1,6 +1,7 @@
import { describe, expect, it, vi, beforeEach } from 'vitest'
import { createEditorStore, flushAsyncRemoteRefresh } from './editor-slice-test-harness'
import { isSyncPushStageError } from '@/lib/source-control-remote-error'
import { normalizeGitErrorMessage } from '../../../../shared/git-remote-error'
const { toastErrorMock } = vi.hoisted(() => ({
toastErrorMock: vi.fn()
@@ -537,7 +538,19 @@ describe('createEditorSlice remote branch actions', () => {
it('maps force-with-lease rejection into force-push guidance', async () => {
const store = createEditorStore()
const pushError = new Error('fatal: stale info')
const pushError = new Error(
normalizeGitErrorMessage(
new Error(
[
'Command failed: git push --force-with-lease --set-upstream origin HEAD:feature',
'To https://github.com/acme/repo.git',
' ! [rejected] HEAD -> feature (stale info)',
"error: failed to push some refs to 'https://github.com/acme/repo.git'"
].join('\n')
),
'push'
)
)
gitPushMock.mockRejectedValueOnce(pushError)
await expect(
+10
View File
@@ -13,6 +13,16 @@ afterEach(() => {
})
describe('normalizeGitErrorMessage', () => {
it.each(['remote: stale info', 'hook: stale info'])(
'does not classify unrelated output as a lease rejection: %s',
(output) => {
const tail = "error: failed to push some refs to 'origin'"
const error = new Error(`Command failed: git push\n${output}\n${tail}`)
expect(normalizeGitErrorMessage(error, 'push')).toBe(tail)
}
)
it.each(['push', 'pull', 'fetch', 'upstream'] as const)(
'classifies noninteractive credential refusal for %s',
(operation) => {
+3 -2
View File
@@ -6,6 +6,7 @@ const HTTPS_TOKEN_URL_PATTERN = /(https?:\/\/)[^\s/@:]+@/gi
const SUBMODULE_PUSH_FAILURE_PATTERN = /Unable to push submodule ['"](.+?)['"]/i
const SUBMODULE_PUSH_FAILURE_SENTINEL_PATTERN =
/failed to push all needed submodules|Unable to push submodule/i
const STALE_LEASE_REJECTION_PATTERN = /^\s*!\s+\[rejected\][^\r\n]*\(stale info\)\s*$/m
const SUBMODULE_REMOTE_CHANGED_PATTERN =
/non-fast-forward|fetch first|updates were rejected|remote contains work that you do not have/i
const NORMALIZED_SUBMODULE_PUSH_FAILURE_PATTERN =
@@ -165,8 +166,8 @@ export function normalizeGitErrorMessage(error: unknown, operation?: GitRemoteOp
return 'Push rejected: remote has newer commits (non-fast-forward). Please pull or sync first.'
}
if (operation === 'push' && isPushHookFailure(raw)) {
// Why: pre-push hook output is the actionable part, printed before git's generic "failed to push some refs" tail line.
if (operation === 'push' && (STALE_LEASE_REJECTION_PATTERN.test(raw) || isPushHookFailure(raw))) {
// Lease rejection and pre-push hook details precede Git's generic "failed to push some refs" tail line.
return raw.trim()
}