Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/treetrek.git
<?php
require_once __DIR__ . '/BasePage.php';
require_once __DIR__ . '/../git/GitDiff.php';

class DiffPage extends BasePage {
  private $currentRepo;
  private $git;
  private $hash;

  public function __construct(
    array $repositories,
    array $currentRepo,
    Git $git,
    string $hash
  ) {
    parent::__construct( $repositories );
    $this->currentRepo = $currentRepo;
    $this->git = $git;
    $this->hash = $hash;
    $this->title = substr( $hash, 0, 7 );
  }

  public function render() {
    $this->renderLayout( function() {
      $commitData = $this->git->read( $this->hash );
      $diffEngine = new GitDiff( $this->git );
      $lines = explode( "\n", $commitData );
      $msg = '';
      $isMsg = false;
      $headers = [];

      foreach( $lines as $line ) {
        if( $line === '' ) {
          $isMsg = true;
          continue;
        }

        if( $isMsg ) {
          $msg .= $line . "\n";
        } else {
          if( preg_match( '/^(\w+) (.*)$/', $line, $m ) ) {
            $headers[$m[1]] = $m[2];
          }
        }
      }

      $changes = $diffEngine->compare( $this->hash );

      $this->renderBreadcrumbs();

      $author = $headers['author'] ?? 'Unknown';
      $author = preg_replace( '/<[^>]+>/', '<email>', $author );

      echo '<div class="commit-details">';
      echo '<div class="commit-header">';
      echo '<h1 class="commit-title">' . htmlspecialchars( trim( $msg ) ) . '</h1>';
      echo '<div class="commit-info">';
      echo '<div class="commit-info-row"><span class="commit-info-label">Author</span>' .
           '<span class="commit-author">' . htmlspecialchars( $author ) . '</span></div>';
      echo '<div class="commit-info-row"><span class="commit-info-label">Commit</span>' .
           '<span class="commit-info-value">' . $this->hash . '</span></div>';

      if( isset( $headers['parent'] ) ) {
        $repoUrl = '&repo=' . urlencode( $this->currentRepo['safe_name'] );

        echo '<div class="commit-info-row"><span class="commit-info-label">Parent</span>' .
             '<span class="commit-info-value">';
        echo '<a href="?action=commit&hash=' . $headers['parent'] . $repoUrl .
             '" class="parent-link">' . substr( $headers['parent'], 0, 7 ) . '</a>';
        echo '</span></div>';
      }

      echo '</div></div></div>';

      echo '<div class="diff-container">';

      foreach( $changes as $change ) {
        $this->renderFileDiff( $change );
      }

      if( empty( $changes ) ) {
        echo '<div class="empty-state"><p>No changes detected.</p></div>';
      }

      echo '</div>';
    }, $this->currentRepo );
  }

  private function renderFileDiff( $change ) {
    $statusIcon = 'fa-file';
    $statusClass = '';

    if( $change['type'] === 'A' ) {
      $statusIcon = 'fa-plus-circle';
      $statusClass = 'status-add';
    }

    if( $change['type'] === 'D' ) {
      $statusIcon = 'fa-minus-circle';
      $statusClass = 'status-del';
    }

    if( $change['type'] === 'M' ) {
      $statusIcon = 'fa-pencil-alt';
      $statusClass = 'status-mod';
    }

    echo '<div class="diff-file">';
    echo '<div class="diff-header">';
    echo '<span class="diff-status ' . $statusClass . '">' .
         '<i class="fa ' . $statusIcon . '"></i></span>';
    echo '<span class="diff-path">' . htmlspecialchars( $change['path'] ) . '</span>';
    echo '</div>';

    if( $change['is_binary'] ) {
      echo '<div class="diff-binary">Binary files differ</div>';
    } else {
      echo '<div class="diff-content">';
      echo '<table><tbody>';

      foreach( $change['hunks'] as $line ) {
        if( isset( $line['t'] ) && $line['t'] === 'gap' ) {
          echo '<tr class="diff-gap"><td colspan="3">...</td></tr>';
          continue;
        }

        $class = 'diff-ctx';
        $char = ' ';

        if( $line['t'] === '+' ) {
          $class = 'diff-add';
          $char = '+';
        }

        if( $line['t'] === '-' ) {
          $class = 'diff-del';
          $char = '-';
        }

        echo '<tr class="' . $class . '">';
        echo '<td class="diff-num" data-num="' . $line['no'] . '"></td>';
        echo '<td class="diff-num" data-num="' . $line['nn'] . '"></td>';
        echo '<td class="diff-code"><span class="diff-marker">' . $char . '</span>' .
             htmlspecialchars( $line['l'] ) . '</td>';
        echo '</tr>';
      }

      echo '</tbody></table>';
      echo '</div>';
    }

    echo '</div>';
  }

  private function renderBreadcrumbs() {
    $safeName = urlencode( $this->currentRepo['safe_name'] );
    $crumbs = [
      '<a href="?">Repositories</a>',
      '<a href="?repo=' . $safeName . '">' .
        htmlspecialchars( $this->currentRepo['name'] ) . '</a>',
      '<a href="?action=commits&repo=' . $safeName . '">Commits</a>',
      substr( $this->hash, 0, 7 )
    ];

    echo '<div class="breadcrumb">' . implode( ' / ', $crumbs ) . '</div>';
  }
}