| | |
| | public function compare(string $commitHash) { |
| | - // 1. Parse Commit to find Parent and Tree |
| | $commitData = $this->git->read($commitHash); |
| | $parentHash = ''; |
| | |
| | + // Only diff against the first parent for now |
| | if (preg_match('/^parent ([0-9a-f]{40})/m', $commitData, $matches)) { |
| | $parentHash = $matches[1]; |
| | } |
| | |
| | $newTree = $this->getTreeHash($commitHash); |
| | $oldTree = $parentHash ? $this->getTreeHash($parentHash) : null; |
| | |
| | - // 2. Diff the Trees |
| | return $this->diffTrees($oldTree, $newTree); |
| | } |
 |
| | |
| | if (!$old) { |
| | - // Added |
| | if ($new['is_dir']) { |
| | $changes = array_merge($changes, $this->diffTrees(null, $new['sha'], $currentPath)); |
| | } else { |
| | $changes[] = $this->createChange('A', $currentPath, null, $new['sha']); |
| | } |
| | } elseif (!$new) { |
| | - // Deleted |
| | if ($old['is_dir']) { |
| | $changes = array_merge($changes, $this->diffTrees($old['sha'], null, $currentPath)); |
| | } else { |
| | $changes[] = $this->createChange('D', $currentPath, $old['sha'], null); |
| | } |
| | } elseif ($old['sha'] !== $new['sha']) { |
| | - // Modified |
| | if ($old['is_dir'] && $new['is_dir']) { |
| | $changes = array_merge($changes, $this->diffTrees($old['sha'], $new['sha'], $currentPath)); |
 |
| | $isBinary = false; |
| | |
| | - // Check New Content for Binary |
| | + // Check New Content |
| | if ($newSha) { |
| | $f = new VirtualDiffFile($path, $newContent); |
| | if ($f->isBinary()) $isBinary = true; |
| | } |
| | - // Check Old Content if New was fine or didn't exist |
| | + // Check Old Content |
| | if (!$isBinary && $oldSha) { |
| | $f = new VirtualDiffFile($path, $oldContent); |
 |
| | $newLines = explode("\n", $new); |
| | |
| | - // Simple LCS (Longest Common Subsequence) implementation |
| | $m = count($oldLines); |
| | $n = count($newLines); |
| | |
| | - // Skip identical lines at start |
| | $start = 0; |
| | while ($start < $m && $start < $n && $oldLines[$start] === $newLines[$start]) { |
| | $start++; |
| | } |
| | |
| | - // Skip identical lines at end |
| | $end = 0; |
| | while ($m - $end > $start && $n - $end > $start && $oldLines[$m - 1 - $end] === $newLines[$n - 1 - $end]) { |
 |
| | $finalDiff = []; |
| | |
| | - // Add Context (Top) |
| | for ($i = 0; $i < $start; $i++) { |
| | $finalDiff[] = ['t' => ' ', 'l' => $oldLines[$i]]; |
| | } |
| | |
| | - // Add Changes |
| | foreach ($ops as $op) { |
| | $finalDiff[] = $op; |
| | } |
| | |
| | - // Add Context (Bottom) |
| | for ($i = $m - $end; $i < $m; $i++) { |
| | $finalDiff[] = ['t' => ' ', 'l' => $oldLines[$i]]; |
 |
| | } |
| | |
| | +/** |
| | + * Helper Class to check binary types on in-memory content. |
| | + * Fixes recursion crash by storing name locally. |
| | + */ |
| | class VirtualDiffFile extends File { |
| | - private $content; |
| | - |
| | - public function __construct($name, $content) { |
| | - parent::__construct($name, '', '100644', 0, strlen($content)); |
| | - $this->content = $content; |
| | - } |
| | + private $content; |
| | + private $vName; // Local storage for name since parent::$name is private |
| | |
| | - public function isBinary(): bool { |
| | - $buffer = substr($this->content, 0, 12); |
| | - return MediaTypeSniffer::isBinary($buffer, $this->name); |
| | - } |
| | + public function __construct($name, $content) { |
| | + parent::__construct($name, '', '100644', 0, strlen($content)); |
| | + $this->vName = $name; |
| | + $this->content = $content; |
| | + } |
| | |
| | - public function __get($prop) { |
| | - if ($prop === 'name') return $this->name; |
| | - return null; |
| | - } |
| | + public function isBinary(): bool { |
| | + // Use local $this->vName to avoid accessing private parent::$name |
| | + $buffer = substr($this->content, 0, 12); |
| | + return MediaTypeSniffer::isBinary($buffer, $this->vName); |
| | + } |
| | } |
| | |