Skip to content

Commit f3378be

Browse files
giladgdiiroj
authored andcommitted
feat: --no-stash flag implies --no-hide-partially-staged
1 parent f4f61f3 commit f3378be

File tree

10 files changed

+110
-7
lines changed

10 files changed

+110
-7
lines changed

.changeset/empty-gifts-leave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'lint-staged': minor
3+
---
4+
5+
Using the `--no-stash` flag no longer discards all unstaged changes to partially staged files, which resulted in inadvertent data loss. This fix is available with a new flag `--no-hide-partially-staged` that is automatically enabled when `--no-stash` is used.

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ Options:
117117
"--no-stash".
118118
--diff-filter [string] override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
119119
--max-arg-length [number] maximum length of the command-line argument string (default: 0)
120-
--no-stash disable the backup stash, and do not revert in case of errors
120+
--no-stash disable the backup stash, and do not revert in case of errors. Implies
121+
"--no-hide-partially-staged".
122+
--no-hide-partially-staged disable hiding unstaged changes from partially staged files
121123
-q, --quiet disable lint-staged’s own console output (default: false)
122124
-r, --relative pass relative filepaths to tasks (default: false)
123125
-x, --shell [path] skip parsing of tasks for better shell support (default: false)
@@ -140,7 +142,8 @@ Options:
140142
- **`--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). This option also implies `--no-stash`.
141143
- **`--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).
142144
- **`--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.
143-
- **`--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. Can be re-enabled with `--stash`.
145+
- **`--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. Can be re-enabled with `--stash`. This option also implies `--no-hide-partially-staged`.
146+
- **`--no-hide-partially-staged`**: By default, unstaged changes from partially staged files will be hidden. This option will disable this behavior and include all unstaged changes in partially staged files. Can be re-enabled with `--hide-partially-staged`
144147
- **`--quiet`**: Supress all CLI output, except from tasks.
145148
- **`--relative`**: Pass filepaths relative to `process.cwd()` (where `lint-staged` runs) to tasks. Default is `false`.
146149
- **`--shell`**: By default linter commands will be parsed for speed and security. This has the side-effect that regular shell scripts might not work as expected. You can skip parsing of commands with this option. To use a specific shell, use a path like `--shell "/bin/bash"`.

bin/lint-staged.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,25 @@ cli
6767
.addOption(
6868
new Option(
6969
'--no-stash',
70-
'disable the backup stash, and do not revert in case of errors'
70+
'disable the backup stash, and do not revert in case of errors. Implies "--no-hide-partially-staged".'
71+
).default(false)
72+
)
73+
74+
/**
75+
* We don't want to show the `--hide-partially-staged` flag because it's on by default, and only show the
76+
* negatable flag `--no-hide-partially-staged` in stead. There seems to be a bug in Commander.js where
77+
* configuring only the latter won't actually set the default value.
78+
*/
79+
cli
80+
.addOption(
81+
new Option('--hide-partially-staged', 'hide unstaged changes from partially staged files')
82+
.default(null)
83+
.hideHelp()
84+
)
85+
.addOption(
86+
new Option(
87+
'--no-hide-partially-staged',
88+
'disable hiding unstaged changes from partially staged files'
7189
).default(false)
7290
)
7391

@@ -104,6 +122,8 @@ const options = {
104122
relative: !!cliOptions.relative,
105123
shell: cliOptions.shell /* Either a boolean or a string pointing to the shell */,
106124
stash: !!cliOptions.stash, // commander inverts `no-<x>` flags to `!x`
125+
hidePartiallyStaged:
126+
cliOptions.hidePartiallyStaged == null ? !!cliOptions.stash : !!cliOptions.hidePartiallyStaged, // commander inverts `no-<x>` flags to `!x`
107127
verbose: !!cliOptions.verbose,
108128
}
109129

lib/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ const lintStaged = async (
7777
shell = false,
7878
// Stashing should be disabled by default when the `diff` option is used
7979
stash = diff === undefined,
80+
hidePartiallyStaged = stash,
8081
verbose = false,
8182
} = {},
8283
logger = console
@@ -101,6 +102,7 @@ const lintStaged = async (
101102
relative,
102103
shell,
103104
stash,
105+
hidePartiallyStaged,
104106
verbose,
105107
}
106108

lib/messages.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ export const skippingBackup = (hasInitialCommit, diff) => {
3838
return chalk.yellow(`${warning} Skipping backup because ${reason}.\n`)
3939
}
4040

41+
export const skippingHidePartiallyStaged = (stash, diff) => {
42+
const reason =
43+
diff !== undefined
44+
? '`--diff` was used'
45+
: !stash
46+
? '`--no-stash` was used'
47+
: '`--no-hide-partially-staged` was used'
48+
49+
return chalk.yellow(
50+
`${warning} Skipping hiding unstaged changes from partially staged files because ${reason}.\n`
51+
)
52+
}
53+
4154
export const DEPRECATED_GIT_ADD = chalk.yellow(
4255
`${warning} Some of your tasks use \`git add\` command. Please remove it from the config since all modifications made by tasks will be automatically added to the git commit index.
4356
`

lib/runAll.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
NO_TASKS,
2323
SKIPPED_GIT_ERROR,
2424
skippingBackup,
25+
skippingHidePartiallyStaged,
2526
} from './messages.js'
2627
import { normalizePath } from './normalizePath.js'
2728
import { resolveGitRepo } from './resolveGitRepo.js'
@@ -30,7 +31,7 @@ import {
3031
cleanupEnabled,
3132
cleanupSkipped,
3233
getInitialState,
33-
hasPartiallyStagedFiles,
34+
shouldHidePartiallyStagedFiles,
3435
restoreOriginalStateEnabled,
3536
restoreOriginalStateSkipped,
3637
restoreUnstagedChangesSkipped,
@@ -79,6 +80,7 @@ export const runAll = async (
7980
shell = false,
8081
// Stashing should be disabled by default when the `diff` option is used
8182
stash = diff === undefined,
83+
hidePartiallyStaged = stash,
8284
verbose = false,
8385
},
8486
logger = console
@@ -112,6 +114,11 @@ export const runAll = async (
112114
logger.warn(skippingBackup(hasInitialCommit, diff))
113115
}
114116

117+
ctx.shouldHidePartiallyStaged = hidePartiallyStaged
118+
if (!ctx.shouldHidePartiallyStaged && !quiet) {
119+
logger.warn(skippingHidePartiallyStaged(hasInitialCommit && stash, diff))
120+
}
121+
115122
const files = await getStagedFiles({ cwd: gitDir, diff, diffFilter })
116123
if (!files) {
117124
if (!quiet) ctx.output.push(FAILED_GET_STAGED_FILES)
@@ -280,7 +287,7 @@ export const runAll = async (
280287
{
281288
title: 'Hiding unstaged changes to partially staged files...',
282289
task: (ctx) => git.hideUnstagedChanges(ctx),
283-
enabled: hasPartiallyStagedFiles,
290+
enabled: shouldHidePartiallyStagedFiles,
284291
},
285292
{
286293
title: `Running tasks for ${diff ? 'changed' : 'staged'} files...`,
@@ -295,7 +302,7 @@ export const runAll = async (
295302
{
296303
title: 'Restoring unstaged changes to partially staged files...',
297304
task: (ctx) => git.restoreUnstagedChanges(ctx),
298-
enabled: hasPartiallyStagedFiles,
305+
enabled: shouldHidePartiallyStagedFiles,
299306
skip: restoreUnstagedChangesSkipped,
300307
},
301308
{

lib/state.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import {
1212
export const getInitialState = ({ quiet = false } = {}) => ({
1313
hasPartiallyStagedFiles: null,
1414
shouldBackup: null,
15+
shouldHidePartiallyStaged: true,
1516
errors: new Set([]),
1617
events: new EventEmitter(),
1718
output: [],
1819
quiet,
1920
})
2021

21-
export const hasPartiallyStagedFiles = (ctx) => ctx.hasPartiallyStagedFiles
22+
export const shouldHidePartiallyStagedFiles = (ctx) =>
23+
ctx.hasPartiallyStagedFiles && ctx.shouldHidePartiallyStaged
2224

2325
export const applyModificationsSkipped = (ctx) => {
2426
// Always apply back unstaged modifications when skipping backup

test/integration/__fixtures__/files.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,21 @@ export const prettyJS = `module.exports = {
33
};
44
`
55

6+
export const prettyJSWithChanges = `module.exports = {
7+
foo: "bar",
8+
bar: "baz",
9+
};
10+
`
11+
612
export const uglyJS = `module.exports = {
713
'foo': 'bar'
814
}
915
`
16+
export const uglyJSWithChanges = `module.exports = {
17+
'foo': 'bar',
18+
'bar': 'baz'
19+
}
20+
`
1021

1122
export const invalidJS = `const obj = {
1223
'foo': 'bar'
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { jest } from '@jest/globals'
2+
3+
import { withGitIntegration } from './__utils__/withGitIntegration.js'
4+
import * as fileFixtures from './__fixtures__/files.js'
5+
import * as configFixtures from './__fixtures__/configs.js'
6+
7+
jest.setTimeout(20000)
8+
jest.retryTimes(2)
9+
10+
describe('lint-staged', () => {
11+
test(
12+
'skips hiding unstaged changes from partially staged files with --no-hide-partially-staged',
13+
withGitIntegration(async ({ execGit, gitCommit, readFile, writeFile }) => {
14+
await writeFile('.lintstagedrc.json', JSON.stringify(configFixtures.prettierWrite))
15+
16+
// Stage ugly file
17+
await writeFile('test.js', fileFixtures.uglyJS)
18+
await execGit(['add', 'test.js'])
19+
20+
// modify file with unstaged changes
21+
await writeFile('test.js', fileFixtures.uglyJSWithChanges)
22+
23+
// Run lint-staged with --no-hide-partially-staged
24+
const stdout = await gitCommit({ lintStaged: { hidePartiallyStaged: false } })
25+
26+
expect(stdout).toMatch(
27+
'Skipping hiding unstaged changes from partially staged files because `--no-hide-partially-staged` was used'
28+
)
29+
30+
// Nothing is wrong, so a new commit is created
31+
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2')
32+
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('test')
33+
expect(await readFile('test.js')).toEqual(fileFixtures.prettyJSWithChanges)
34+
})
35+
)
36+
})

test/integration/no-stash.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ describe('lint-staged', () => {
2424
const stdout = await gitCommit({ lintStaged: { stash: false } })
2525

2626
expect(stdout).toMatch('Skipping backup because `--no-stash` was used')
27+
expect(stdout).toMatch(
28+
'Skipping hiding unstaged changes from partially staged files because `--no-stash` was used'
29+
)
2730

2831
// Nothing is wrong, so a new commit is created
2932
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2')
@@ -44,6 +47,7 @@ describe('lint-staged', () => {
4447
gitCommit({
4548
lintStaged: {
4649
stash: false,
50+
hidePartiallyStaged: true,
4751
config: {
4852
'*.js': async () => {
4953
const testFile = path.join(cwd, 'test.js')

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy