Skip to main content
Glama

CTX: Context as Code (CaC) tool

by context-hub
MIT License
235
  • Apple
  • Linux
GitDiffFinder.php7.46 kB
<?php declare(strict_types=1); namespace Butschster\ContextGenerator\Source\GitDiff; use Butschster\ContextGenerator\Application\FSPath; use Butschster\ContextGenerator\Application\Logger\LoggerPrefix; use Butschster\ContextGenerator\Lib\Finder\FinderInterface; use Butschster\ContextGenerator\Lib\Finder\FinderResult; use Butschster\ContextGenerator\Lib\TreeBuilder\FileTreeBuilder; use Butschster\ContextGenerator\Source\Fetcher\FilterableSourceInterface; use Butschster\ContextGenerator\Source\GitDiff\Fetcher\CommitRangeParser; use Butschster\ContextGenerator\Source\GitDiff\Fetcher\GitSourceFactory; use Butschster\ContextGenerator\Source\GitDiff\Fetcher\GitSourceInterface; use Psr\Log\LoggerInterface; use Spiral\Files\FilesInterface; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; /** * This finder extracts diffs from git commits, stashes, and other sources and applies filters to them */ final readonly class GitDiffFinder implements FinderInterface { public function __construct( private FilesInterface $files, private GitSourceFactory $sourceFactory, private FileTreeBuilder $fileTreeBuilder, private CommitRangeParser $rangeParser, #[LoggerPrefix(prefix: 'git-diff-finder')] private ?LoggerInterface $logger = null, ) {} /** * Find git commit diffs based on the given source configuration * * @param FilterableSourceInterface $source Source configuration with filter criteria * @param string $basePath Optional base path to normalize file paths in the tree view * @return FinderResult The result containing found diffs and tree view */ public function find(FilterableSourceInterface $source, string $basePath = '', array $options = []): FinderResult { if (!$source instanceof GitDiffSource) { throw new \InvalidArgumentException('Source must be an instance of CommitDiffSource'); } // Get the commit range from the source $commitRange = $this->rangeParser->resolve($source->commit); $this->logger?->debug('Resolved commit range', [ 'original' => $source->commit, 'resolved' => $commitRange, ]); // Get the appropriate Git source for this commit range $gitSource = $this->sourceFactory->create($commitRange); $this->logger?->debug('Selected Git source', [ 'source' => $gitSource::class, ]); // Create a temporary directory for the diffs $tempDir = FSPath::temp()->join('git-diff-' . \uniqid())->toString(); $this->files->ensureDirectory($tempDir); try { // Get file infos from the Git source $this->logger?->debug('Getting file infos from Git source'); $fileInfos = $gitSource->createFileInfos($source->repository, $commitRange, $tempDir); if (empty($fileInfos)) { $this->logger?->info('No changes found for the commit range', [ 'commitRange' => $commitRange, ]); return new FinderResult([], 'No changes found'); } $this->logger?->debug('Found files', [ 'count' => \count($fileInfos), ]); // Apply filters if needed $fileInfos = $this->applyFilters($fileInfos, $source, $tempDir); $this->logger?->debug('After applying filters', [ 'count' => \count($fileInfos), ]); // Get file paths for tree view $filePaths = \array_map( static fn(SplFileInfo $fileInfo) => \method_exists($fileInfo, 'getOriginalPath') ? $fileInfo->getOriginalPath() : $fileInfo->getRelativePathname(), $fileInfos, ); // Generate tree view $treeView = $this->generateTreeView($filePaths, $gitSource, $commitRange, $options); return new FinderResult(\array_values($fileInfos), $treeView); } catch (\Throwable $e) { $this->logger?->error('Error finding git diffs', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); throw $e; } finally { $this->files->deleteDirectory($tempDir); } } /** * Apply filters to the file infos * * @param array<SplFileInfo> $fileInfos * @return array<SplFileInfo> */ private function applyFilters(array $fileInfos, FilterableSourceInterface $source, string $tempDir): array { if (empty($fileInfos)) { return []; } $this->logger?->debug('Applying filters to file infos', [ 'fileCount' => \count($fileInfos), 'name' => $source->name(), 'path' => $source->path(), 'notPath' => $source->notPath(), 'contains' => $source->contains(), 'notContains' => $source->notContains(), ]); $finder = new Finder(); $finder->in($tempDir); // Apply name filter if ($source->name() !== null) { $finder->name($source->name()); } // Apply path filter if ($source->path() !== null) { $finder->path($source->path()); } // Apply notPath filter if ($source->notPath() !== null) { $finder->notPath($source->notPath()); } // Apply contains filter if ($source->contains() !== null) { $finder->contains($source->contains()); } // Apply notContains filter if ($source->notContains() !== null) { $finder->notContains($source->notContains()); } // Get the filtered files $filteredPaths = []; foreach ($finder as $file) { $relativePath = FSPath::create($file->getPathname())->trim($tempDir)->toString(); $filteredPaths[$relativePath] = true; } // Filter the file infos $filtered = \array_filter($fileInfos, static function (SplFileInfo $fileInfo) use ($filteredPaths, $tempDir) { $relativePath = FSPath::create($fileInfo->getPathname())->trim($tempDir)->toString(); return isset($filteredPaths[$relativePath]); }); $this->logger?->debug('Filter results', [ 'originalCount' => \count($fileInfos), 'filteredCount' => \count($filtered), ]); return $filtered; } /** * Generate a tree view of the changed files * * @param array<string> $files List of file paths * @param GitSourceInterface $gitSource The Git source used for the commits * @param string|array $commitRange Commit range for the header * @return string Text representation of the file tree */ private function generateTreeView( array $files, GitSourceInterface $gitSource, string|array $commitRange, array $options = [], ): string { if (empty($files)) { return "No changes found\n"; } // Get a descriptive header from the Git source $treeHeader = $gitSource->formatReferenceForDisplay($commitRange) . "\n"; // Build the tree $tree = $this->fileTreeBuilder->buildTree($files, '', $options); return $treeHeader . $tree; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/context-hub/generator'

If you have feedback or need assistance with the MCP directory API, please join our Discord server