diff --git a/lib/getStagedFiles.js b/lib/getStagedFiles.js index 61f2dd6c2..e36b80712 100644 --- a/lib/getStagedFiles.js +++ b/lib/getStagedFiles.js @@ -4,8 +4,15 @@ const execGit = require('./execGit') module.exports = async function getStagedFiles(options) { try { - const lines = await execGit(['diff', '--staged', '--diff-filter=ACMR', '--name-only'], options) - return lines ? lines.split('\n') : [] + // Docs for --diff-filter option: https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---diff-filterACDMRTUXB82308203 + // Docs for -z option: https://git-scm.com/docs/git-diff#Documentation/git-diff.txt--z + const lines = await execGit( + ['diff', '--staged', '--diff-filter=ACMR', '--name-only', '-z'], + options + ) + // With `-z`, git prints `fileA\u0000fileB\u0000fileC\u0000` so we need to remove the last occurrence of `\u0000` before splitting + // eslint-disable-next-line no-control-regex + return lines ? lines.replace(/\u0000$/, '').split('\u0000') : [] } catch { return null } diff --git a/test/getStagedFiles.spec.js b/test/getStagedFiles.spec.js index 07bc7aa50..879ccf0df 100644 --- a/test/getStagedFiles.spec.js +++ b/test/getStagedFiles.spec.js @@ -5,7 +5,7 @@ jest.mock('../lib/execGit') describe('getStagedFiles', () => { it('should return array of file names', async () => { - execGit.mockImplementationOnce(async () => 'foo.js\nbar.js') + execGit.mockImplementationOnce(async () => 'foo.js\u0000bar.js\u0000') const staged = await getStagedFiles() expect(staged).toEqual(['foo.js', 'bar.js']) }) diff --git a/test/runAll.unmocked.spec.js b/test/runAll.unmocked.spec.js index 52af3b574..88cfd687b 100644 --- a/test/runAll.unmocked.spec.js +++ b/test/runAll.unmocked.spec.js @@ -843,6 +843,33 @@ describe('runAll', () => { expect(await execGit(['log', '-1', '--pretty=%B'], { cwd: workTreeDir })).toMatch('test') expect(await readFile('test.js', workTreeDir)).toEqual(testJsFilePretty) }) + + test.each([['on'], ['off']])( + 'should handle files with non-ascii characters when core.quotepath is %s', + async quotePath => { + await execGit(['config', 'core.quotepath', quotePath]) + + // Stage multiple ugly files + await appendFile('привет.js', testJsFileUgly) + await execGit(['add', 'привет.js']) + + await appendFile('你好.js', testJsFileUgly) + await execGit(['add', '你好.js']) + + await appendFile('👋.js', testJsFileUgly) + await execGit(['add', '👋.js']) + + // Run lint-staged with `prettier --write` and commit pretty files + await gitCommit(fixJsConfig) + + // Nothing is wrong, so a new commit is created and files are pretty + expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2') + expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('test') + expect(await readFile('привет.js')).toEqual(testJsFilePretty) + expect(await readFile('你好.js')).toEqual(testJsFilePretty) + expect(await readFile('👋.js')).toEqual(testJsFilePretty) + } + ) }) describe('runAll', () => {