diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b346bd6d0d5..45cf04e641ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## master +### Performance + +- `[jest-changed-files]` limit git and hg commands to specified roots ([#6732](https://github.com/facebook/jest/pull/6732)) + ### Fixes - `[babel-jest]` Make `getCacheKey()` take into account `createTransformer` options ([#6699](https://github.com/facebook/jest/pull/6699)) diff --git a/e2e/__tests__/jest_changed_files.test.js b/e2e/__tests__/jest_changed_files.test.js index 1f492585f1a1..30d162303d3e 100644 --- a/e2e/__tests__/jest_changed_files.test.js +++ b/e2e/__tests__/jest_changed_files.test.js @@ -224,6 +224,25 @@ test('gets changed files for git', async () => { ).toEqual(['file5.txt']); }); +test('monitors only root paths for git', async () => { + writeFiles(DIR, { + 'file1.txt': 'file1', + 'nested_dir/file2.txt': 'file2', + 'nested_dir/second_nested_dir/file3.txt': 'file3', + }); + + run(`${GIT} init`, DIR); + + const roots = [path.resolve(DIR, 'nested_dir')]; + + const {changedFiles: files} = await getChangedFilesForRoots(roots, {}); + expect( + Array.from(files) + .map(filePath => path.basename(filePath)) + .sort(), + ).toEqual(['file2.txt', 'file3.txt']); +}); + test('gets changed files for hg', async () => { if (process.env.CI) { // Circle and Travis have very old version of hg (v2, and current @@ -326,3 +345,29 @@ test('gets changed files for hg', async () => { .sort(), ).toEqual(['file5.txt']); }); + +test('monitors only root paths for hg', async () => { + if (process.env.CI) { + // Circle and Travis have very old version of hg (v2, and current + // version is v4.2) and its API changed since then and not compatible + // any more. Changing the SCM version on CIs is not trivial, so we'll just + // skip this test and run it only locally. + return; + } + writeFiles(DIR, { + 'file1.txt': 'file1', + 'nested_dir/file2.txt': 'file2', + 'nested_dir/second_nested_dir/file3.txt': 'file3', + }); + + run(`${HG} init`, DIR); + + const roots = [path.resolve(DIR, 'nested_dir')]; + + const {changedFiles: files} = await getChangedFilesForRoots(roots, {}); + expect( + Array.from(files) + .map(filePath => path.basename(filePath)) + .sort(), + ).toEqual(['file2.txt', 'file3.txt']); +}); diff --git a/packages/jest-changed-files/src/git.js b/packages/jest-changed-files/src/git.js index bb3d286ebe81..672108ad7f96 100644 --- a/packages/jest-changed-files/src/git.js +++ b/packages/jest-changed-files/src/git.js @@ -51,28 +51,40 @@ const adapter: SCMAdapter = { const changedSince: ?string = options && (options.withAncestor ? 'HEAD^' : options.changedSince); + const includePaths: Array = (options && options.includePaths) || []; + if (options && options.lastCommit) { return await findChangedFilesUsingCommand( - ['show', '--name-only', '--pretty=%b', 'HEAD'], + ['show', '--name-only', '--pretty=%b', 'HEAD'].concat(includePaths), cwd, ); } else if (changedSince) { const committed = await findChangedFilesUsingCommand( - ['log', '--name-only', '--pretty=%b', 'HEAD', `^${changedSince}`], + [ + 'log', + '--name-only', + '--pretty=%b', + 'HEAD', + `^${changedSince}`, + ].concat(includePaths), cwd, ); const staged = await findChangedFilesUsingCommand( - ['diff', '--cached', '--name-only'], + ['diff', '--cached', '--name-only'].concat(includePaths), cwd, ); const unstaged = await findChangedFilesUsingCommand( - ['ls-files', '--other', '--modified', '--exclude-standard'], + ['ls-files', '--other', '--modified', '--exclude-standard'].concat( + includePaths, + ), cwd, ); return [...committed, ...staged, ...unstaged]; } else { return await findChangedFilesUsingCommand( - ['ls-files', '--other', '--modified', '--exclude-standard'], + ['ls-files', '--other', '--modified', '--exclude-standard'].concat( + includePaths, + ), cwd, ); } diff --git a/packages/jest-changed-files/src/hg.js b/packages/jest-changed-files/src/hg.js index 4f3d33552793..029709546cdd 100644 --- a/packages/jest-changed-files/src/hg.js +++ b/packages/jest-changed-files/src/hg.js @@ -34,14 +34,17 @@ const adapter: SCMAdapter = { options: Options, ): Promise> => new Promise((resolve, reject) => { - let args = ['status', '-amnu']; + const includePaths: Array = (options && options.includePaths) || []; + + const args = ['status', '-amnu']; if (options && options.withAncestor) { args.push('--rev', `ancestor(${ANCESTORS.join(', ')})`); } else if (options && options.changedSince) { args.push('--rev', `ancestor(., ${options.changedSince})`); } else if (options && options.lastCommit === true) { - args = ['tip', '--template', '{files%"{file}\n"}']; + args.push('-A'); } + args.push(...includePaths); const child = childProcess.spawn('hg', args, {cwd, env}); let stdout = ''; let stderr = ''; diff --git a/packages/jest-changed-files/src/index.js b/packages/jest-changed-files/src/index.js index 234393a7c149..a1aedfab904e 100644 --- a/packages/jest-changed-files/src/index.js +++ b/packages/jest-changed-files/src/index.js @@ -27,12 +27,14 @@ export const getChangedFilesForRoots = async ( ): ChangedFilesPromise => { const repos = await findRepos(roots); + const changedFilesOptions = Object.assign({}, {includePaths: roots}, options); + const gitPromises = Array.from(repos.git).map(repo => - git.findChangedFiles(repo, options), + git.findChangedFiles(repo, changedFilesOptions), ); const hgPromises = Array.from(repos.hg).map(repo => - hg.findChangedFiles(repo, options), + hg.findChangedFiles(repo, changedFilesOptions), ); const changedFiles = (await Promise.all( diff --git a/types/ChangedFiles.js b/types/ChangedFiles.js index 1b7249710a9b..9e31be9286fb 100644 --- a/types/ChangedFiles.js +++ b/types/ChangedFiles.js @@ -13,6 +13,7 @@ export type Options = {| lastCommit?: boolean, withAncestor?: boolean, changedSince?: string, + includePaths?: Array, |}; export type ChangedFiles = Set;