From b01659d2ea8d6a2085f3ea8f226f4c76138290ef Mon Sep 17 00:00:00 2001 From: Piotr Bosak Date: Fri, 24 Jun 2022 10:41:38 +0200 Subject: [PATCH 1/3] fix: correctly handle git stash when using MSYS2 --- lib/gitWorkflow.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/gitWorkflow.js b/lib/gitWorkflow.js index 9813aa4dd..45167cf5e 100644 --- a/lib/gitWorkflow.js +++ b/lib/gitWorkflow.js @@ -103,6 +103,16 @@ export class GitWorkflow { ctx.errors.add(GetBackupStashError) throw new Error('lint-staged automatic backup is missing!') } + + /** + * https://github.com/okonet/lint-staged/issues/1121 + * Detect MSYS in login shell mode and escape braces + * to prevent interpolation + */ + if (!!process.env.MSYSTEM && !!process.env.LOGINSHELL) { + return `refs/stash@\\{${index}\\}` + } + return `refs/stash@{${index}}` } From e3c39dbd57e4c8dbeb98bf15834feeb4f5224a18 Mon Sep 17 00:00:00 2001 From: Piotr Bosak Date: Fri, 24 Jun 2022 15:11:57 +0200 Subject: [PATCH 2/3] test: Adds unit tests for MSYS shell detection (as recommended by @iiroj) --- lib/gitWorkflow.js | 2 +- test/unit/getBackupStash.spec.js | 94 ++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 test/unit/getBackupStash.spec.js diff --git a/lib/gitWorkflow.js b/lib/gitWorkflow.js index 45167cf5e..9f0df245d 100644 --- a/lib/gitWorkflow.js +++ b/lib/gitWorkflow.js @@ -43,7 +43,7 @@ const processRenames = (files, includeRenameFrom = true) => return flattened }, []) -const STASH = 'lint-staged automatic backup' +export const STASH = 'lint-staged automatic backup' const PATCH_UNSTAGED = 'lint-staged_unstaged.patch' diff --git a/test/unit/getBackupStash.spec.js b/test/unit/getBackupStash.spec.js new file mode 100644 index 000000000..9291ff2bb --- /dev/null +++ b/test/unit/getBackupStash.spec.js @@ -0,0 +1,94 @@ +import { execGit } from '../../lib/execGit.js' +import { GitWorkflow, STASH } from '../../lib/gitWorkflow.js' +import { getInitialState } from '../../lib/state.js' +import { GetBackupStashError } from '../../lib/symbols' + +jest.mock('../../lib/execGit.js', () => ({ + execGit: jest.fn(async () => ''), +})) + +describe('gitWorkflow', () => { + const options = { gitConfigDir: '.' } + + describe('getBackupStash', () => { + it('should throw when stash not found', async () => { + const gitWorkflow = new GitWorkflow(options) + const ctx = getInitialState() + + await expect(gitWorkflow.getBackupStash(ctx)).rejects.toThrowError( + 'lint-staged automatic backup is missing!' + ) + + expect(ctx.errors.has(GetBackupStashError)).toEqual(true) + }) + + it('should throw when stash not found even when other stashes are', async () => { + const gitWorkflow = new GitWorkflow(options) + const ctx = getInitialState() + + execGit.mockResolvedValueOnce('stash@{0}: some random stuff') + + await expect(gitWorkflow.getBackupStash(ctx)).rejects.toThrowError( + 'lint-staged automatic backup is missing!' + ) + + expect(ctx.errors.has(GetBackupStashError)).toEqual(true) + }) + + it('should return ref to the backup stash', async () => { + const gitWorkflow = new GitWorkflow(options) + const ctx = getInitialState() + + execGit.mockResolvedValueOnce( + [ + 'stash@{0}: some random stuff', + `stash@{1}: ${STASH}`, + 'stash@{2}: other random stuff', + ].join('\n') + ) + + await expect(gitWorkflow.getBackupStash(ctx)).resolves.toEqual('refs/stash@{1}') + }) + + it('should return unescaped ref to the backup stash when using MSYS2 without login shell', async () => { + const gitWorkflow = new GitWorkflow(options) + const ctx = getInitialState() + + process.env.MSYSTEM = 'MSYS' + delete process.env.LOGINSHELL + + execGit.mockResolvedValueOnce( + [ + 'stash@{0}: some random stuff', + `stash@{1}: ${STASH}`, + 'stash@{2}: other random stuff', + ].join('\n') + ) + + await expect(gitWorkflow.getBackupStash(ctx)).resolves.toEqual('refs/stash@{1}') + + delete process.env.MSYSTEM + }) + + it('should return escaped ref to the backup stash when using MSYS2 with login shell', async () => { + const gitWorkflow = new GitWorkflow(options) + const ctx = getInitialState() + + process.env.MSYSTEM = 'MSYS' + process.env.LOGINSHELL = 'bash' + + execGit.mockResolvedValueOnce( + [ + 'stash@{0}: some random stuff', + `stash@{1}: ${STASH}`, + 'stash@{2}: other random stuff', + ].join('\n') + ) + + await expect(gitWorkflow.getBackupStash(ctx)).resolves.toEqual('refs/stash@\\{1\\}') + + delete process.env.MSYSTEM + delete process.env.LOGINSHELL + }) + }) +}) From 0d7d7a6c0f5661dc4e502d1d347e66d60c5a7407 Mon Sep 17 00:00:00 2001 From: Piotr Bosak Date: Fri, 24 Jun 2022 15:17:45 +0200 Subject: [PATCH 3/3] improvement: LOGINSHELL env variable deletion is unnecessary if tests aren't run on MSYS2 environment --- test/unit/getBackupStash.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/getBackupStash.spec.js b/test/unit/getBackupStash.spec.js index 9291ff2bb..4c55fab38 100644 --- a/test/unit/getBackupStash.spec.js +++ b/test/unit/getBackupStash.spec.js @@ -55,7 +55,6 @@ describe('gitWorkflow', () => { const ctx = getInitialState() process.env.MSYSTEM = 'MSYS' - delete process.env.LOGINSHELL execGit.mockResolvedValueOnce( [