From 753ef7281562e0a25a9fe01400d7108143116b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iiro=20J=C3=A4ppinen?= Date: Sun, 29 May 2022 15:01:30 +0300 Subject: [PATCH] feat: add `--diff-filter` option for overriding list of (staged) files The `--diff-filter` option can be used to, for example, include deleted files instead of only the defaults: `--diff-filter=ACMRD`. --- README.md | 4 +++- bin/lint-staged.js | 6 ++++++ lib/getStagedFiles.js | 25 +++++++++++++++++-------- lib/index.js | 3 +++ lib/runAll.js | 4 +++- test/getStagedFiles.spec.js | 18 +++++++++++++++--- test/integration.test.js | 19 +++++++++++++++++++ 7 files changed, 66 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d4f12cb4f..cdd994aba 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ Options: --cwd [path] run all tasks in specific directory, instead of the current -d, --debug print additional debug information (default: false) --diff [string] override the default "--staged" flag of "git diff" to get list of files + --diff-filter [string] override the default "--diff-filter=ACMR" flag of "git diff" to get list of files --max-arg-length [number] maximum length of the command-line argument string (default: 0) --no-stash disable the backup stash, and do not revert in case of errors -q, --quiet disable lint-staged’s own console output (default: false) @@ -113,7 +114,8 @@ Options: - **`--debug`**: Run in debug mode. When set, it does the following: - uses [debug](https://github.com/visionmedia/debug) internally to log additional information about staged files, commands being executed, location of binaries, etc. Debug logs, which are automatically enabled by passing the flag, can also be enabled by setting the environment variable `$DEBUG` to `lint-staged*`. - uses [`verbose` renderer](https://github.com/SamVerschueren/listr-verbose-renderer) for `listr`; this causes serial, uncoloured output to the terminal, instead of the default (beautified, dynamic) output. -- **`--diff`**: By default linters are filtered against all files staged in git, generated from `git diff --staged`. This option allows you to override the `--staged` flag with arbitrary revisions. For example to get a list of changed files between two branches, use `--diff="branch1...branch2"`. You can also read more from about [`git diff`](https://git-scm.com/docs/git-diff) and [`gitrevisions`](https://git-scm.com/docs/gitrevisions). +- **`--diff`**: By default linters are filtered against all files staged in git, generated from `git diff --staged`. This option allows you to override the `--staged` flag with arbitrary revisions. For example to get a list of changed files between two branches, use `--diff="branch1...branch2"`. You can also read more from about [git diff](https://git-scm.com/docs/git-diff) and [gitrevisions](https://git-scm.com/docs/gitrevisions). +- **`--diff-filter`**: By default only files that are _added_, _copied_, _modified_, or _renamed_ are included. Use this flag to override the default `ACMR` value with something else: _added_ (`A`), _copied_ (`C`), _deleted_ (`D`), _modified_ (`M`), _renamed_ (`R`), _type changed_ (`T`), _unmerged_ (`U`), _unknown_ (`X`), or _pairing broken_ (`B`). See also the `git diff` docs for [--diff-filter](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---diff-filterACDMRTUXB82308203). - **`--max-arg-length`**: long commands (a lot of files) are automatically split into multiple chunks when it detects the current shell cannot handle them. Use this flag to override the maximum length of the generated command string. - **`--no-stash`**: By default a backup stash will be created before running the tasks, and all task modifications will be reverted in case of an error. This option will disable creating the stash, and instead leave all modifications in the index when aborting the commit. - **`--quiet`**: Supress all CLI output, except from tasks. diff --git a/bin/lint-staged.js b/bin/lint-staged.js index 66fa7fbe6..21018ddbe 100755 --- a/bin/lint-staged.js +++ b/bin/lint-staged.js @@ -47,6 +47,11 @@ cli.option( 'override the default "--staged" flag of "git diff" to get list of files' ) +cli.option( + '--diff-filter [string]', + 'override the default "--diff-filter=ACMR" flag of "git diff" to get list of files' +) + cli.option('--max-arg-length [number]', 'maximum length of the command-line argument string', 0) /** @@ -92,6 +97,7 @@ const options = { cwd: cliOptions.cwd, debug: !!cliOptions.debug, diff: cliOptions.diff, + diffFilter: cliOptions.diffFilter, maxArgLength: cliOptions.maxArgLength || undefined, quiet: !!cliOptions.quiet, relative: !!cliOptions.relative, diff --git a/lib/getStagedFiles.js b/lib/getStagedFiles.js index 7ab9c0265..08f7723fd 100644 --- a/lib/getStagedFiles.js +++ b/lib/getStagedFiles.js @@ -5,18 +5,27 @@ import normalize from 'normalize-path' import { execGit } from './execGit.js' import { parseGitZOutput } from './parseGitZOutput.js' -/** - * Docs for --diff-filter option: @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---diff-filterACDMRTUXB82308203 - * Docs for -z option: @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt--z - */ -const ARGS = ['diff', '--diff-filter=ACMR', '--name-only', '-z'] - -export const getStagedFiles = async ({ cwd = process.cwd(), diff } = {}) => { +export const getStagedFiles = async ({ cwd = process.cwd(), diff, diffFilter } = {}) => { try { + /** + * Docs for --diff-filter option: + * @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---diff-filterACDMRTUXB82308203 + */ + const diffFilterArg = diffFilter !== undefined ? diffFilter.trim() : 'ACMR' + /** Use `--diff branch1...branch2` or `--diff="branch1 branch2", or fall back to default staged files */ const diffArgs = diff !== undefined ? diff.trim().split(' ') : ['--staged'] - const lines = await execGit([...ARGS, ...diffArgs], { cwd }) + + /** + * Docs for -z option: + * @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt--z + */ + const lines = await execGit( + ['diff', '--name-only', '-z', `--diff-filter=${diffFilterArg}`, ...diffArgs], + { cwd } + ) if (!lines) return [] + return parseGitZOutput(lines).map((file) => normalize(path.resolve(cwd, file))) } catch { return null diff --git a/lib/index.js b/lib/index.js index f815743db..636e3ae59 100644 --- a/lib/index.js +++ b/lib/index.js @@ -50,6 +50,7 @@ const getMaxArgLength = () => { * @param {Object} [options.cwd] - Current working directory * @param {boolean} [options.debug] - Enable debug mode * @param {string} [options.diff] - Override the default "--staged" flag of "git diff" to get list of files + * @param {string} [options.diffFilter] - Override the default "--diff-filter=ACMR" flag of "git diff" to get list of files * @param {number} [options.maxArgLength] - Maximum argument string length * @param {boolean} [options.quiet] - Disable lint-staged’s own console output * @param {boolean} [options.relative] - Pass relative filepaths to tasks @@ -69,6 +70,7 @@ const lintStaged = async ( cwd, debug = false, diff, + diffFilter, maxArgLength = getMaxArgLength() / 2, quiet = false, relative = false, @@ -94,6 +96,7 @@ const lintStaged = async ( cwd, debug, diff, + diffFilter, maxArgLength, quiet, relative, diff --git a/lib/runAll.js b/lib/runAll.js index e2e682bf6..f2cdc0366 100644 --- a/lib/runAll.js +++ b/lib/runAll.js @@ -53,6 +53,7 @@ const createError = (ctx) => Object.assign(new Error('lint-staged failed'), { ct * @param {string} [options.cwd] - Current working directory * @param {boolean} [options.debug] - Enable debug mode * @param {string} [options.diff] - Override the default "--staged" flag of "git diff" to get list of files + * @param {string} [options.diffFilter] - Override the default "--diff-filter=ACMR" flag of "git diff" to get list of files * @param {number} [options.maxArgLength] - Maximum argument string length * @param {boolean} [options.quiet] - Disable lint-staged’s own console output * @param {boolean} [options.relative] - Pass relative filepaths to tasks @@ -71,6 +72,7 @@ export const runAll = async ( cwd, debug = false, diff, + diffFilter, maxArgLength, quiet = false, relative = false, @@ -108,7 +110,7 @@ export const runAll = async ( logger.warn(skippingBackup(hasInitialCommit)) } - const files = await getStagedFiles({ cwd: gitDir, diff }) + const files = await getStagedFiles({ cwd: gitDir, diff, diffFilter }) if (!files) { if (!quiet) ctx.output.push(FAILED_GET_STAGED_FILES) ctx.errors.add(GetStagedFilesError) diff --git a/test/getStagedFiles.spec.js b/test/getStagedFiles.spec.js index 285a6d187..620b8ebd2 100644 --- a/test/getStagedFiles.spec.js +++ b/test/getStagedFiles.spec.js @@ -22,7 +22,7 @@ describe('getStagedFiles', () => { expect(staged).toEqual([normalizePath('/foo.js'), normalizePath('/bar.js')]) expect(execGit).toHaveBeenLastCalledWith( - ['diff', '--diff-filter=ACMR', '--name-only', '-z', '--staged'], + ['diff', '--name-only', '-z', '--diff-filter=ACMR', '--staged'], { cwd: '/' } ) }) @@ -48,7 +48,7 @@ describe('getStagedFiles', () => { expect(staged).toEqual([normalizePath('/foo.js'), normalizePath('/bar.js')]) expect(execGit).toHaveBeenLastCalledWith( - ['diff', '--diff-filter=ACMR', '--name-only', '-z', 'master...my-branch'], + ['diff', '--name-only', '-z', '--diff-filter=ACMR', 'master...my-branch'], { cwd: '/' } ) }) @@ -60,7 +60,19 @@ describe('getStagedFiles', () => { expect(staged).toEqual([normalizePath('/foo.js'), normalizePath('/bar.js')]) expect(execGit).toHaveBeenLastCalledWith( - ['diff', '--diff-filter=ACMR', '--name-only', '-z', 'master', 'my-branch'], + ['diff', '--name-only', '-z', '--diff-filter=ACMR', 'master', 'my-branch'], + { cwd: '/' } + ) + }) + + it('should support overriding diff-filter', async () => { + execGit.mockImplementationOnce(async () => 'foo.js\u0000bar.js\u0000') + const staged = await getStagedFiles({ cwd: '/', diffFilter: 'ACDMRTUXB' }) + // Windows filepaths + expect(staged).toEqual([normalizePath('/foo.js'), normalizePath('/bar.js')]) + + expect(execGit).toHaveBeenLastCalledWith( + ['diff', '--name-only', '-z', '--diff-filter=ACDMRTUXB', '--staged'], { cwd: '/' } ) }) diff --git a/test/integration.test.js b/test/integration.test.js index 4fc81abe6..afc0bda45 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -1335,6 +1335,25 @@ describe('lint-staged', () => { expect(console.printHistory()).toMatch('prettier --list-different:') expect(console.printHistory()).toMatch('test.js') }) + + it('should support overriding default --diff-filter', async () => { + // Stage ugly file + await appendFile('test.js', testJsFileUgly) + await execGit(['add', 'test.js']) + + // Run lint-staged with `--diff-filter=D` to include only deleted files. + const passed = await lintStaged({ + config: { '*.js': 'prettier --list-different' }, + cwd, + diffFilter: 'D', + stash: false, + }) + + // Lint-staged passed because no matching (deleted) files + expect(passed).toEqual(true) + + expect(console.printHistory()).toMatch('No staged files found') + }) }) describe('lintStaged', () => {