Skip to content

Commit

Permalink
fix: always resolve real git config dir location if .git is a file (#784
Browse files Browse the repository at this point in the history
)

* fix: always resolve real git config dir location if .git is a file

* test: ensure lint-staged runs in a worktree directory
  • Loading branch information
iiroj committed Jan 30, 2020
1 parent 9c08e8e commit b98a5ed
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 25 deletions.
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

0 comments on commit b98a5ed

Please sign in to comment.