diff --git a/src/Locator/GitRepositoryDirLocator.php b/src/Locator/GitRepositoryDirLocator.php index c40c2962..32ea7641 100644 --- a/src/Locator/GitRepositoryDirLocator.php +++ b/src/Locator/GitRepositoryDirLocator.php @@ -35,8 +35,8 @@ public function locate(string $gitDir): string $gitRepositoryDir = $matches[1]; - if ($this->filesystem->isAbsolutePath($gitRepositoryDir)) { - return $gitRepositoryDir; + if ($this->isWorktree($gitRepositoryDir)) { + return $this->locateWorktreeRoot($gitRepositoryDir); } return $this->filesystem->buildPath( @@ -44,4 +44,46 @@ public function locate(string $gitDir): string $gitRepositoryDir ); } + + /** + * If a given path (from gitdir value) is absolute and there is a commondir file, it is + * a worktree. + */ + private function isWorktree(string $gitDir): bool + { + return $this->filesystem->isAbsolutePath($gitDir) + && $this->filesystem->isFile($gitDir.DIRECTORY_SEPARATOR.'commondir'); + } + + /** + * Retreiving repository dir for worktree nominally returns path to the configured worktree, + * which does not hold hooks. We need to resolve the actual repository root. + * + * Example directory structure: + * ``` + * /project + * .git/ + * .git/hooks/ + * .git/worktrees/ + * worktree1 + * commondir: relative path to /project/.git + * /worktree1 + * .git: file with path to /project/.git/worktrees/worktree1 + * ``` + */ + private function locateWorktreeRoot(string $gitRepositoryDir): string + { + $worktreeRelativeRoot = trim( + $this->filesystem->readPath( + $gitRepositoryDir.DIRECTORY_SEPARATOR.'commondir' + ) + ); + + return $this->filesystem->realpath( + $this->filesystem->makePathAbsolute( + $worktreeRelativeRoot, + $gitRepositoryDir + ) + ); + } } diff --git a/test/Unit/Locator/GitRepositoryDirLocatorTest.php b/test/Unit/Locator/GitRepositoryDirLocatorTest.php index 00f6872f..789a5642 100644 --- a/test/Unit/Locator/GitRepositoryDirLocatorTest.php +++ b/test/Unit/Locator/GitRepositoryDirLocatorTest.php @@ -31,7 +31,7 @@ protected function setUp(): void $this->filesystem = new Filesystem(); $this->locator = new GitRepositoryDirLocator($this->filesystem); - $this->gitDir = $this->workspace . DIRECTORY_SEPARATOR . '.git'; + $this->gitDir = $this->workspace . DIRECTORY_SEPARATOR . '.git'; } /** @@ -69,7 +69,11 @@ public function it_can_passthrough_git_dir_path_if_file_is_not_parseable(): void */ public function it_can_locate_git_dir_in_workspaces(): void { - $this->filesystem->dumpFile($this->gitDir, 'gitdir: /dev/null'); - $this->assertEquals('/dev/null', $this->locator->locate($this->gitDir)); + $ourWorktreeProject = $this->workspace.'/project1/'; + $worktreeGitRoot = $this->gitDir.'/worktrees/worktree1/'; + mkdir($worktreeGitRoot, 0777, true); + $this->filesystem->dumpFile($worktreeGitRoot.'/commondir', '../..'); + $this->filesystem->dumpFile($ourWorktreeProject.'/.git', 'gitdir: '.$this->gitDir.'/worktrees/worktree1'); + $this->assertEquals($this->gitDir, $this->locator->locate($this->gitDir)); } }