fix(git): skip upstream remote probes when the remote is absent (#18455)

* fix(git): skip upstream remote probes when the remote is absent

Issue and PR resolvers listed remotes by probing `git remote get-url
upstream` on every poll, including origin-only clones where that remote
cannot exist. List remotes once, cache against git config, and skip the
probe unless `upstream` is present.

* fix(git): avoid stale remote probe cache entries

* fix(github): observe origin repository probe failures

* fix(github): observe verified origin probe failures

* fix(github): skip missing upstream probe for PR lists

* test(github): scope the #9171 lazy-resolution guard to default-branch commands

The guard asserted that no git command runs for an open PR, using "no git at
all" as a proxy for "no default-branch resolution". Remote-name listing is a
separate concern, so allow it and keep every other command forbidden; the
symbolic-ref/rev-parse resolution this issue is about stays unreachable.

---------

Co-authored-by: Neil <neil@stably.ai>
This commit is contained in:
Wooseong Kim
2026-09-13 20:18:22 -07:00
committed by GitHub
co-authored by Neil
parent 16d1ab81d3
commit 31db2774f8
13 changed files with 1016 additions and 241 deletions
+27
View File
@@ -116,6 +116,7 @@ import {
_resetOwnerRepoCache
} from './client'
import { GITHUB_WORK_ITEMS_QUERY_MAX_BYTES } from '../../shared/github/work-items-query-bounds'
import { _resetRemoteNameListingCache } from '../git/remote-name-listing'
import { _resetOriginGitHubApiRepositoryCache } from './github-api-repository'
@@ -156,6 +157,7 @@ describe('listWorkItems', () => {
remoteName === 'origin' ? getOwnerRepoMock(repoPath, connectionId, opts) : null
)
_resetOwnerRepoCache()
_resetRemoteNameListingCache()
_resetMergeQueueCacheForTests()
})
@@ -383,6 +385,31 @@ describe('listWorkItems', () => {
)
})
it('skips upstream PR source probing when the clone only has origin', async () => {
getIssueOwnerRepoMock.mockResolvedValue(null)
getOwnerRepoMock.mockResolvedValue({ owner: 'fork', repo: 'orca' })
gitExecFileAsyncMock.mockResolvedValue({ stdout: 'origin\n' })
ghExecFileAsyncMock.mockResolvedValue({ stdout: '[]' })
await expect(listWorkItems('/origin-only-repo', 10, 'is:pr')).resolves.toMatchObject({
items: [],
sources: {
issues: null,
prs: { owner: 'fork', repo: 'orca' },
originCandidate: { owner: 'fork', repo: 'orca' },
upstreamCandidate: null
}
})
expect(getOwnerRepoForRemoteMock.mock.calls.map(([, remote]) => remote)).not.toContain(
'upstream'
)
expect(gitExecFileAsyncMock).toHaveBeenCalledWith(
['remote'],
expect.objectContaining({ cwd: '/origin-only-repo' })
)
})
it('rejects oversized queries before resolving repo sources or executing gh', async () => {
const secret = 'main-github-work-items-secret'
const oversizedQuery = secret + 'x'.repeat(GITHUB_WORK_ITEMS_QUERY_MAX_BYTES)