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: always resolve real git config dir location if .git is a file #784

Merged
merged 2 commits into from Jan 30, 2020
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
42 changes: 21 additions & 21 deletions lib/resolveGitRepo.js
@@ -1,48 +1,48 @@
'use strict'

const normalize = require('normalize-path')
const fs = require('fs').promises
const path = require('path')

const debugLog = require('debug')('lint-staged:resolveGitRepo')

const execGit = require('./execGit')
const { readBufferFromFile } = require('./file')

/**
* Resolve path to the .git directory, with special handling for
* submodules
* submodules and worktrees
*/
const resolveGitConfigDir = async ({ gitDir, isSubmodule }) => {
const resolveGitConfigDir = async gitDir => {
const defaultDir = path.resolve(gitDir, '.git')
if (!isSubmodule) return normalize(defaultDir)

const buffer = await readBufferFromFile(defaultDir)
const dotGit = buffer.toString()
const gitConfigDir = path.resolve(gitDir, dotGit.replace(/^gitdir: /, '').trim())
return normalize(gitConfigDir)
const stats = await fs.lstat(defaultDir)
// If .git is a directory, use it
if (stats.isDirectory()) return defaultDir
// Otherwise .git is a file containing path to real location
const file = (await readBufferFromFile(defaultDir)).toString()
return path.resolve(gitDir, file.replace(/^gitdir: /, '')).trim()
}

/**
* Resolve git directory and possible submodule paths
*/
module.exports = async function resolveGitRepo(options = {}) {
module.exports = async function resolveGitRepo(cwd) {
try {
debugLog('Resolving git repo from `%s`', cwd)
// git cli uses GIT_DIR to fast track its response however it might be set to a different path
// depending on where the caller initiated this from, hence clear GIT_DIR
debugLog('Deleting GIT_DIR from env with value `%s`', process.env.GIT_DIR)
delete process.env.GIT_DIR

// The git repo root directory; this points to the root of a submodule instead of the parent
const gitDir = await execGit(['rev-parse', '--show-toplevel'], options)

// A super-project working tree exists only in submodules; poinst to the parent root
const superprojectWorkingTree = await execGit(
['rev-parse', '--show-superproject-working-tree'],
options
)
const gitDir = normalize(await execGit(['rev-parse', '--show-toplevel'], { cwd }))
const gitConfigDir = normalize(await resolveGitConfigDir(gitDir))

const isSubmodule = !!superprojectWorkingTree
const gitConfigDir = await resolveGitConfigDir({ gitDir, isSubmodule })
debugLog('Resolved git directory to be `%s`', gitDir)
debugLog('Resolved git config directory to be `%s`', gitConfigDir)

return { gitDir: normalize(gitDir), gitConfigDir, isSubmodule }
return { gitDir, gitConfigDir }
} catch (error) {
return { error, gitDir: null }
debugLog('Failed to resolve git repo with error:', error)
return { error, gitDir: null, gitConfigDir: null }
}
}
5 changes: 1 addition & 4 deletions lib/runAll.js
Expand Up @@ -55,11 +55,8 @@ module.exports = async function runAll(
) {
debugLog('Running all linter scripts')

const { gitDir, gitConfigDir, isSubmodule } = await resolveGitRepo({ cwd })
const { gitDir, gitConfigDir } = await resolveGitRepo(cwd)
if (!gitDir) throw new Error('Current directory is not a git directory!')
debugLog('Resolved git directory to be `%s`', gitDir)
debugLog('Resolved git config directory to be `%s`', gitConfigDir)
if (isSubmodule) debugLog('Current git directory is a submodule')

const files = await getStagedFiles({ cwd: gitDir })
if (!files) throw new Error('Unable to get staged files!')
Expand Down
24 changes: 24 additions & 0 deletions test/runAll.unmocked.spec.js
Expand Up @@ -777,6 +777,30 @@ describe('runAll', () => {
expect(await execGit(['log', '-1', '--pretty=%B'], { cwd: submoduleDir })).toMatch('test')
expect(await readFile('test.js', submoduleDir)).toEqual(testJsFilePretty)
})

it('should handle git worktrees', async () => {
// create a new branch and add it as worktree
const workTreeDir = path.resolve(cwd, 'worktree')
await execGit(['branch', 'test'])
await execGit(['worktree', 'add', workTreeDir, 'test'])

// Stage pretty file
await appendFile('test.js', testJsFilePretty, workTreeDir)
await execGit(['add', 'test.js'], { cwd: workTreeDir })

// Run lint-staged with `prettier --list-different` and commit pretty file
await runAll({
config: { '*.js': 'prettier --list-different' },
cwd: workTreeDir,
quiet: true
})
await execGit(['commit', '-m test'], { cwd: workTreeDir })

// Nothing is wrong, so a new commit is created
expect(await execGit(['rev-list', '--count', 'HEAD'], { cwd: workTreeDir })).toEqual('2')
expect(await execGit(['log', '-1', '--pretty=%B'], { cwd: workTreeDir })).toMatch('test')
expect(await readFile('test.js', workTreeDir)).toEqual(testJsFilePretty)
})
})

describe('runAll', () => {
Expand Down