Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: correctly handle git stash when using MSYS2 #1178

Merged
merged 3 commits into from Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 11 additions & 1 deletion lib/gitWorkflow.js
Expand Up @@ -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'

Expand Down Expand Up @@ -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}}`
}

Expand Down
93 changes: 93 additions & 0 deletions 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
})
})
})