From 0d627a52846d63cd6fc6018a8d7779ef454a99b2 Mon Sep 17 00:00:00 2001 From: Piotr Bosak Date: Fri, 24 Jun 2022 16:34:01 +0200 Subject: [PATCH] fix: correctly handle git stash when using MSYS2 (#1178) --- lib/gitWorkflow.js | 12 ++++- test/unit/getBackupStash.spec.js | 93 ++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 test/unit/getBackupStash.spec.js diff --git a/lib/gitWorkflow.js b/lib/gitWorkflow.js index 9813aa4dd..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' @@ -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}}` } diff --git a/test/unit/getBackupStash.spec.js b/test/unit/getBackupStash.spec.js new file mode 100644 index 000000000..4c55fab38 --- /dev/null +++ b/test/unit/getBackupStash.spec.js @@ -0,0 +1,93 @@ +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' + + 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 + }) + }) +})