mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
Preserve force-with-lease rejection details in push errors (#36027)
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user