fix(git): use branch-safe author names as username fallback (#25984)

Co-authored-by: Kyou0203 <kyou12138@gmail.com>
This commit is contained in:
Neil
2026-10-06 15:59:38 -07:00
committed by GitHub
co-authored by Kyou0203
parent 1eeb8f3f72
commit 2137295bb6
2 changed files with 43 additions and 3 deletions
+8 -1
View File
@@ -326,7 +326,14 @@ export async function resolveLocalGitUsernameDetailed(
}
if (await localRepoHasEffectiveGitHubRemote(repoPath)) {
const outcome = await getGhLoginOutcome()
return { username: outcome.login, authoritative: !outcome.timedOut }
if (outcome.login || outcome.timedOut) {
return { username: outcome.login, authoritative: !outcome.timedOut }
}
}
// Author names become prefixes only when they already form a safe branch component.
const authorName = (await readGitStdout(repoPath, ['config', '--get', 'user.name'])).trim()
if (isBranchSafeHostedLogin(authorName)) {
return { username: authorName, authoritative: true }
}
return { username: '', authoritative: true }
}
+35 -2
View File
@@ -148,7 +148,8 @@ describe('resolveLocalGitUsername', () => {
expect(gitExecFileAsyncMock.mock.calls.map(([args]) => args)).toEqual([
['config', '--get', 'github.user'],
['config', '--get', 'user.username'],
['remote']
['remote'],
['config', '--get', 'user.name']
])
expect(ghExecFileAsyncMock).not.toHaveBeenCalled()
})
@@ -197,16 +198,48 @@ describe('resolveLocalGitUsername', () => {
expect(ghExecFileAsyncMock).toHaveBeenCalledTimes(1)
})
it('does not derive GitHub username prefixes from non-GitHub remotes', async () => {
it('does not derive GitHub username prefixes from free-form author names on non-GitHub remotes', async () => {
originRemoteUrl = 'https://gitlab.com/stablyai/orca.git'
gitConfig['user.email'] = 'demo@example.com'
gitConfig['user.name'] = 'Demo User'
ghExecFileAsyncMock.mockResolvedValueOnce({ stdout: 'gh-demo\n', stderr: '' })
// Spaces make "Demo User" branch-unsafe; free-form author identity stays out of prefixes.
await expect(resolveLocalGitUsername('/repo')).resolves.toBe('')
expect(ghExecFileAsyncMock).not.toHaveBeenCalled()
})
it('falls back to branch-safe user.name when github.user and gh login are absent', async () => {
// Issue #11590: only `user.name = lorengroves` is configured; branch prefix was empty.
originRemoteUrl = 'https://github.com/stablyai/orca.git'
gitConfig['user.name'] = 'lorengroves'
ghExecFileAsyncMock.mockRejectedValue(makeExecError('gh unavailable'))
await expect(resolveLocalGitUsername('/repo')).resolves.toBe('lorengroves')
})
it('uses branch-safe user.name on non-GitHub remotes when no explicit username keys exist', async () => {
originRemoteUrl = 'https://gitlab.com/stablyai/orca.git'
gitConfig['user.name'] = 'lorengroves'
ghExecFileAsyncMock.mockResolvedValueOnce({ stdout: 'gh-demo\n', stderr: '' })
await expect(resolveLocalGitUsername('/repo')).resolves.toBe('lorengroves')
expect(ghExecFileAsyncMock).not.toHaveBeenCalled()
})
it.each([
'person@example.com',
'42+person',
'person..name',
'person.lock',
'person\\name',
'person/name'
])('does not rewrite or accept unsafe author name %s', async (authorName) => {
originRemoteUrl = 'https://gitlab.com/stablyai/orca.git'
gitConfig['user.name'] = authorName
await expect(resolveLocalGitUsername('/repo')).resolves.toBe('')
})
it('ignores a secondary GitHub mirror when the effective remote is GitLab', async () => {
// Why: a GitLab-primary repo with a GitHub mirror must not pick up the
// GitHub account name as its branch prefix — only the effective remote