lint-staged: `lint-staged` doesn't restore unstaged changed when it fails

Description

I’ve experienced an issue, that I’m consistently able to reproduce.

If I do a partial staging of changes, where the staged changes contain linting errors, the unstaged changes are not restored after lint-staged runs.

What’s even more annoying is that the stashed changes doesn’t appear in stash list, meaning the work is lost.

Appear to relate to #62

Steps to reproduce

Not sure how to best produce a small example that reproduces this issue. But these steps triggers the error for me:

  1. Make changes to 2 files
    • In file A, make two changes, one of the changes should fail linting
  2. Stage the changes—but only stage the change that linting would fail on in file A
  3. Run lint-staged

Debug Logs

expand to view
$ lint-staged --debug
  lint-staged:bin Running `lint-staged@8.0.4` +0ms
  lint-staged:find-bin Loaded package.json using `process.cwd()` +0ms
  lint-staged Loading config using `cosmiconfig` +0ms
  lint-staged Successfully loaded config from `/Users/ronni/Development/seatgeek/seatgeek/.lintstagedrc`:
  lint-staged { concurrent: false,
  lint-staged   subTaskConcurrency: 1,
  lint-staged   linters:
  lint-staged    { '*.{css,scss}': [ 'stylelint --fix', 'git add' ],
  lint-staged      '*.js': [ 'eslint --fix', 'git add' ],
  lint-staged      '*.md': [ 'remark . --output --frail', 'git add' ] } } +17ms
  lint-staged:cfg Normalizing config +0ms
  lint-staged:cfg Validating config +1ms
Running lint-staged with the following config:
{
  concurrent: false,
  subTaskConcurrency: 1,
  linters: {
    '*.{css,scss}': [
      'stylelint --fix',
      'git add'
    ],
    '*.js': [
      'eslint --fix',
      'git add'
    ],
    '*.md': [
      'remark . --output --frail',
      'git add'
    ]
  },
  chunkSize: 9007199254740991,
  globOptions: {
    matchBase: true,
    dot: true
  },
  ignore: [],
  renderer: 'verbose'
}
  lint-staged:run Running all linter scripts +0ms
  lint-staged:run Resolved git directory to be `/Users/ronni/Development/seatgeek/seatgeek` +0ms
  lint-staged:run Loaded list of staged files in git:
  lint-staged:run [ 'clientside/js/modals/EmailModal/Success.js' ] +36ms
  lint-staged:gen-tasks Generating linter tasks +0ms
  lint-staged:cfg Normalizing config +39ms
  lint-staged:gen-tasks Generated task:
  lint-staged:gen-tasks { pattern: '*.{css,scss}',
  lint-staged:gen-tasks   commands: [ 'stylelint --fix', 'git add' ],
  lint-staged:gen-tasks   fileList: [] } +15ms
  lint-staged:gen-tasks Generated task:
  lint-staged:gen-tasks { pattern: '*.js',
  lint-staged:gen-tasks   commands: [ 'eslint --fix', 'git add' ],
  lint-staged:gen-tasks   fileList:
  lint-staged:gen-tasks    [ '/Users/ronni/Development/seatgeek/seatgeek/clientside/js/modals/EmailModal/Success.js' ] } +1ms
  lint-staged:gen-tasks Generated task:
  lint-staged:gen-tasks { pattern: '*.md',
  lint-staged:gen-tasks   commands: [ 'remark . --output --frail', 'git add' ],
  lint-staged:gen-tasks   fileList: [] } +1ms
[2018-11-27T13:52:38.563+01:00] Stashing changes... [started]
  lint-staged:git Stashing files... +0ms
  lint-staged:git Running git command [ 'write-tree' ] +1ms
  lint-staged:git Running git command [ 'add', '.' ] +28ms
  lint-staged:git Running git command [ 'write-tree' ] +196ms
  lint-staged:git Running git command [ 'read-tree', 'b8b8946f9aa6dd954161a63f4a2a8f829018af27' ] +29ms
  lint-staged:git Running git command [ 'checkout-index', '-af' ] +123ms
  lint-staged:git Done stashing files! +8s
[2018-11-27T13:52:47.335+01:00] Stashing changes... [completed]
[2018-11-27T13:52:47.336+01:00] Running linters... [started]
[2018-11-27T13:52:47.336+01:00] Running tasks for *.{css,scss} [started]
[2018-11-27T13:52:47.336+01:00] Running tasks for *.{css,scss} [skipped]
[2018-11-27T13:52:47.336+01:00] → No staged files match *.{css,scss}
[2018-11-27T13:52:47.337+01:00] Running tasks for *.js [started]
  lint-staged:make-cmd-tasks Creating listr tasks for commands [ 'eslint --fix', 'git add' ] +0ms
  lint-staged:find-bin Resolving binary for command `eslint --fix` +9s
  lint-staged:find-bin Binary for `eslint --fix` resolved to `/Users/ronni/Development/seatgeek/seatgeek/node_modules/.bin/eslint` +3ms
  lint-staged:task ✔  OS: darwin; File path chunking unnecessary +0ms
  lint-staged:find-bin Resolving binary for command `git add` +0ms
  lint-staged:find-bin Binary for `git add` resolved to `/usr/bin/git` +1ms
  lint-staged:task ✔  OS: darwin; File path chunking unnecessary +1ms
[2018-11-27T13:52:47.342+01:00] eslint --fix [started]
  lint-staged:task bin: /Users/ronni/Development/seatgeek/seatgeek/node_modules/.bin/eslint +0ms
  lint-staged:task args: [ '--fix',
  lint-staged:task   '/Users/ronni/Development/seatgeek/seatgeek/clientside/js/modals/EmailModal/Success.js' ] +0ms
  lint-staged:task opts: { reject: false } +0ms
[2018-11-27T13:52:50.881+01:00] eslint --fix [failed]
[2018-11-27T13:52:50.881+01:00] →
[2018-11-27T13:52:50.881+01:00] Running tasks for *.js [failed]
[2018-11-27T13:52:50.881+01:00] →
[2018-11-27T13:52:50.881+01:00] Running linters... [failed]
[2018-11-27T13:52:50.881+01:00] →



✖ eslint --fix found some errors. Please fix them and try committing again.

/Users/ronni/Development/seatgeek/seatgeek/clientside/js/modals/EmailModal/Success.js
4:10  error  'noop' is defined but never used  no-unused-vars

✖ 1 problem (1 error, 0 warnings)

Environment

  • OS: macOS Mojave
  • Node.js: v8.11.1
  • lint-staged: 8.0.4

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 3
  • Comments: 41 (3 by maintainers)

Most upvoted comments

Happened to me today, all changes lost after lint-staged failed

this is still happening on lint-staged@12.3.7 with node@v16.13.0, just happened to me on OSX and I lost a lot of code changes on several files.

my precommit hook

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run lint:staged

my .lintstagedrc

{
  "*.rb": [
    "bundle exec rubocop -c .rubocop.yml --debug --display-cop-names"
  ],
  "*.{js,jsx}": [
    "npx eslint --config client/.eslintrc.js",
    "npx prettier . --write"
  ],
  "*.scss": [
    "npx stylelint --config .stylelintrc --fix  --custom-syntax postcss-scss"
  ]
}

any ideas?

🎉 This issue has been resolved in version 10.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

🎉 This issue has been resolved in version 9.5.0-beta.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@y3fers0n this is a pretty old issue… can you open a new one with debug logs? Did you find your changes from the git stash list?

Yes, I did found the changes there, thank you

Here’s a way to reproduce it:

module.exports = {
  concurrent: true,
  linters: {
    '*.{md,html,rb}': ['touch .git/index.lock', 'git add'],
  },
}

Make 2 changes in a .md file, stage one hunk and commit. You’ll lose the other.

Note that you’ll have to manually delete .git/index.lock afterwards.

This repro is a hack to simulate what I believe is happening here. Something (maybe magit for me, or my prompt) is locking git in the midst of lint-staged doing its thing. It’s a race condition and only happens sometimes. If it happens though, I get the above error and my work is “gone”. (luckily it’s usually in my undo buffer in emacs, but I have to remember which files were changed).

Hope that helps!

That’s great news! I think we should remove this option since it actually does more harm than good at this point. Thought?

I had this issue as well and after various tests I found the reason!

Whenever my lint-staged config sets concurrent: false unstaged files don’t get restored after linting process errors out. Leaving out the concurrent option or setting it to true solves the problem! I didn’t check the code of lint-staged but hopefully this should help to solve the underlying cause. Awesome tool btw - @okonet!

Ok

module.exports = {
    concurrent: true,
    linters: {
        'src/*.js': [
            'npx eslint'
        ]
    },
}

Fails

module.exports = {
    concurrent: false,
    linters: {
        'src/*.js': [
            'npx eslint'
        ]
    },
}

It seems like that adding exitOnError: false in listrBaseOptions solves the problem - not sure though whether it will still respect other requirements.

https://github.com/okonet/lint-staged/blob/master/src/runAll.js#L60

It is the same issue as #601