Skip to content

Commit

Permalink
Clarify git worktree handling
Browse files Browse the repository at this point in the history
Signed-off-by: Bart Jaskulski <[email protected]>
  • Loading branch information
bart-jaskulski committed Nov 29, 2024
1 parent caabb1d commit 21b9a55
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 25 deletions.
64 changes: 44 additions & 20 deletions src/Locator/GitRepositoryDirLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@

namespace GrumPHP\Locator;

use GrumPHP\Exception\RuntimeException;
use GrumPHP\Util\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;

class GitRepositoryDirLocator
{
Expand Down Expand Up @@ -38,28 +35,55 @@ public function locate(string $gitDir): string

$gitRepositoryDir = $matches[1];

if ($this->filesystem->isAbsolutePath($gitRepositoryDir)) {
if (!$this->filesystem->isFile($gitRepositoryDir.DIRECTORY_SEPARATOR.'commondir')) {
throw new RuntimeException('The git directory for worktree could not be found.');
}

$worktreeRelativeRoot = trim(
$this->filesystem->readPath(
$gitRepositoryDir.DIRECTORY_SEPARATOR.'commondir'
)
);

return $this->filesystem->realpath(
$this->filesystem->makePathAbsolute(
$worktreeRelativeRoot,
$gitRepositoryDir
)
);
if ($this->isWorktree($gitRepositoryDir)) {
return $this->locateWorktreeRoot($gitRepositoryDir);
}

return $this->filesystem->buildPath(
dirname($gitDir),
$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
)
);
}
}
11 changes: 6 additions & 5 deletions test/Unit/Locator/GitRepositoryDirLocatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +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
{
$worktreeRoot = $this->workspace.'/git_root/worktrees/git_worktree/';
mkdir($worktreeRoot, 0777, true);
$this->filesystem->dumpFile($worktreeRoot.'/commondir', '../..');
$this->filesystem->dumpFile($this->gitDir, 'gitdir: '.$this->workspace.'/git_root/worktrees/git_worktree');
$this->assertEquals($this->workspace.DIRECTORY_SEPARATOR.'git_root', $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));
}
}

0 comments on commit 21b9a55

Please sign in to comment.