diff --git a/packages/vitest/src/node/vcs/git.ts b/packages/vitest/src/node/vcs/git.ts index 96f467c34..bc7a37641 100644 --- a/packages/vitest/src/node/vcs/git.ts +++ b/packages/vitest/src/node/vcs/git.ts @@ -20,7 +20,7 @@ export class GitVCSProvider implements VCSProvider { } return result.stdout - .split('\n') + .split('\0') .filter((s) => s !== '') .map((changedPath) => resolve(this.root, changedPath)) } @@ -47,16 +47,17 @@ export class GitVCSProvider implements VCSProvider { } private getFilesSince(hash: string) { - return this.resolveFilesWithGitCommand(['diff', '--name-only', `${hash}...HEAD`]) + return this.resolveFilesWithGitCommand(['diff', '--name-only', '-z', `${hash}...HEAD`]) } private getStagedFiles() { - return this.resolveFilesWithGitCommand(['diff', '--cached', '--name-only']) + return this.resolveFilesWithGitCommand(['diff', '--cached', '--name-only', '-z']) } private getUnstagedFiles() { return this.resolveFilesWithGitCommand([ 'ls-files', + '-z', '--other', '--modified', '--exclude-standard', diff --git a/test/e2e/test/git-changed.test.ts b/test/e2e/test/git-changed.test.ts index 3035071f3..93a4116aa 100644 --- a/test/e2e/test/git-changed.test.ts +++ b/test/e2e/test/git-changed.test.ts @@ -1,7 +1,8 @@ +import { execFileSync } from 'node:child_process' import { tmpdir } from 'node:os' import { join } from 'node:path' import { describe, expect, it } from 'vitest' -import { createFile, editFile, resolvePath, runVitest, useFS } from '../../test-utils' +import { createFile, editFile, resolvePath, runVitest, useFS, useTmpFS } from '../../test-utils' const fileName = 'fixtures/git-changed/related/rerun.temp' @@ -68,7 +69,7 @@ it('fails when git cannot resolve the changed revision', async () => { }) expect(exitCode).toBe(1) - expect(stderr).toContain('Command `git diff --name-only does-not-exist...HEAD` failed:') + expect(stderr).toContain('Command `git diff --name-only -z does-not-exist...HEAD` failed:') }) it('fails when --changed is used outside of a git repository', async () => { @@ -86,6 +87,47 @@ it('fails when --changed is used outside of a git repository', async () => { `) }) +it('finds changed files with names that git quotes', async () => { + const { root, editFile } = useTmpFS({ + 'src/café/menu.js': 'export const menu = 1', + 'src/größe.js': 'export const size = 1', + 'menu.test.js': `import { menu } from './src/café/menu.js'\nimport { test } from 'vitest'\ntest('menu', () => {})`, + 'größe.test.js': `import { size } from './src/größe.js'\nimport { test } from 'vitest'\ntest('size', () => {})`, + 'naïve.test.js': `import { test } from 'vitest'\ntest('naive', () => {})`, + 'other.test.js': `import { test } from 'vitest'\ntest('other', () => {})`, + }) + const git = (...args: string[]) => execFileSync('git', args, { cwd: root, stdio: 'ignore' }) + + git('init') + git('config', 'user.name', 'vitest') + git('config', 'user.email', 'vitest@test') + git('config', 'commit.gpgsign', 'false') + git('add', '-A') + git('commit', '-m', 'base') + editFile('src/café/menu.js', (content) => `${content}\n`) + git('commit', '-am', 'committed change') + editFile('src/größe.js', (content) => `${content}\n`) + git('add', 'src/größe.js') + editFile('naïve.test.js', (content) => `${content}\n`) + + const { stderr, testTree } = await runVitest({ changed: 'HEAD~1', root }) + + expect(stderr).toBe('') + expect(testTree()).toMatchInlineSnapshot(` + { + "größe.test.js": { + "size": "passed", + }, + "menu.test.js": { + "menu": "passed", + }, + "naïve.test.js": { + "naive": "passed", + }, + } + `) +}) + // Fixes #4674 it.skipIf(process.env.ECOSYSTEM_CI)( 'related correctly runs only related tests inside a workspace',