Repository: okonet/lint-staged (js)

Issue:
Git directory is not correctly resolved if GIT_WORK_TREE is set to relative path
### Description

Resolving the git directory will fail if the environment variable `GIT_WORK_TREE` is set to a relative path and lint-staged is run from a subdirectory of a Git repository.

This is because while `GIT_DIR` is unset in `resolveGitRepo.js`, `GIT_WORK_TREE` is not.

`GIT_WORK_TREE` can be used to set the worktree directory for a Git repo. `GIT_DIR` specifies only the .git directory. `git rev-parse --show-toplevel` will return the Git worktree directory. If it is set to a relative path, it won't work when git commands are called from a subdirectory, as `.` will then translate to the subdirectory.

In `resolveGitRepo.js`, you need to add `delete process.env.GIT_WORK_TREE` and then everything works as expected.

**Why set GIT_WORK_TREE to a relative path?**

lint-staged is usually called from hook-scripts and if you call `git --work-tree=/ABSOLUTE/PATH/TO/REPO --git-dir=/ABSOLUTE/PATH/TO/REPO/.git commit` from the worktree root, Git will set `GIT_WORK_TREE` to `.`. `GIT_DIR` will remain an absolute path.

### Steps to reproduce

1. Create a Git repo `test-repo`
2. Make a subdirectory (e.g. `my_app`), where your JS app lives and which contains package.json with lint-staged config
3. Modify a file and stage it
4. Run lint-staged from the subdirectory – **THIS WORKS**
5. Go back to your root Git working directory and set `export GIT_WORK_TREE=.`
6. Run lint-staged from the subdirectory – **this will now fail** to detect the git directory, see error below.

### Debug Logs

The Git repo resides at `/Users/USER/Development/test-repo/`. lint-staged resolves it to the subdirectory `/Users/USER/Development/test-repo/my_app`.

<details>
<summary>expand to view</summary>

```
  lint-staged:resolveGitRepo Resolving git repo from `/Users/USER/Development/test-repo/my_app` +0ms
  lint-staged:resolveGitRepo Unset GIT_DIR (was `undefined`) +0ms
  lint-staged:git Running git command [ 'rev-parse', '--show-toplevel' ] +0ms
  lint-staged:resolveGitRepo Failed to resolve git repo with error: [Error: ENOENT: no such file or directory, lstat '/Users/USER/Development/test-repo/my_app/.git'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'lstat',
  path: '/Users/USER/Development/test-repo/my_app/.git'
} +11ms
✖ Current directory is not a git directory!

```

</details>

### Environment

- **OS:** macOS 10.15
- **Node.js:** v13.3.0
- **`lint-staged`:** 10.2.9


Pull request:
fix: Git directory is not resolved if GIT_WORK_TREE is set to relative path
Resolving the git directory failed if the environment variable `GIT_WORK_TREE` is set to a relative path and lint-staged is run from a subdirectory of a Git repository (e.g. multi-package repos).

`GIT_WORK_TREE` can be used to set the worktree directory for a Git repo. `GIT_DIR` specifies only the `.git` directory path. `git rev-parse --show-toplevel` will return the Git worktree directory. If it is set to a relative path, it won't work when git commands are called from a subdirectory, as the relative path will fail to resolve correctly.

While `GIT_DIR` is unset in `resolveGitRepo.js`, `GIT_WORK_TREE` was not. Unsetting `GIT_WORK_TREE` in the same place fixes the issue.

Resolves #886 

Patch:
diff --git a/lib/resolveGitRepo.js b/lib/resolveGitRepo.js
index d194da0..a3e5509 100644
--- a/lib/resolveGitRepo.js
+++ b/lib/resolveGitRepo.js
@@ -35,6 +35,8 @@ const resolveGitRepo = async (cwd) => {
     // Unset GIT_DIR before running any git operations in case it's pointing to an incorrect location
     debugLog('Unset GIT_DIR (was `%s`)', process.env.GIT_DIR)
     delete process.env.GIT_DIR
+    debugLog('Unset GIT_WORK_TREE (was `%s`)', process.env.GIT_WORK_TREE)
+    delete process.env.GIT_WORK_TREE
 
     const gitDir = normalize(await execGit(['rev-parse', '--show-toplevel'], { cwd }))
     const gitConfigDir = normalize(await resolveGitConfigDir(gitDir))


Test patch:
diff --git a/test/resolveGitRepo.spec.js b/test/resolveGitRepo.spec.js
index 11666bd..46086ab 100644
--- a/test/resolveGitRepo.spec.js
+++ b/test/resolveGitRepo.spec.js
@@ -34,6 +34,16 @@ describe('resolveGitRepo', () => {
     process.cwd = processCwdBkp
   })
 
+  it('should resolve to the parent dir when .git is in the parent dir even when the GIT_WORK_TREE environment variable is set', async () => {
+    const expected = normalize(path.dirname(__dirname))
+    const processCwdBkp = process.cwd
+    process.cwd = () => __dirname
+    process.env.GIT_WORK_TREE = './wrong/path/'
+    const { gitDir } = await resolveGitRepo()
+    expect(gitDir).toEqual(expected)
+    process.cwd = processCwdBkp
+  })
+
   it('should return null when not in a git directory', async () => {
     const { gitDir } = await resolveGitRepo({ cwd: '/' }) // assume root is not a git directory
     expect(gitDir).toEqual(null)