Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/treetrek.git

Updates files sizes

Author Dave Jarvis <email>
Date 2026-02-08 20:40:00 GMT-0800
Commit 91f32a7078271f35e9b51300d411ea5e2f934f20
Parent 7bc6c6a
new/Git.php
}
- /**
- * Iterates over all repositories in the current path.
- * Encapsulates the discovery and sorting (business logic).
- */
- public function eachRepository(callable $callback): void {
- if (!is_dir($this->path)) {
- return;
+ public function getObjectSize(string $sha): int {
+ $loose = "{$this->objPath}/" . substr($sha, 0, 2) . "/" . substr($sha, 2);
+ if (file_exists($loose)) {
+ $f = @fopen($loose, 'rb');
+ if (!$f) return 0;
+ $data = fread($f, 128);
+ fclose($f);
+ $inflated = @gzuncompress($data);
+ if (!$inflated) return 0;
+ $header = explode("\0", $inflated, 2)[0];
+ $parts = explode(' ', $header);
+ return isset($parts[1]) ? (int)$parts[1] : 0;
}
+ $data = $this->fromPack($sha);
+ return $data ? strlen($data) : 0;
+ }
+ public function eachRepository(callable $callback): void {
+ if (!is_dir($this->path)) return;
$repos = [];
- $globPath = $this->path . '/*.git';
-
- foreach (glob($globPath) as $path) {
+ foreach (glob($this->path . '/*.git') as $path) {
if (is_dir($path)) {
$name = basename($path, '.git');
- $displayName = urldecode($name);
- $repos[$name] = [
- 'path' => $path,
- 'name' => $displayName,
- 'safe_name' => $name
- ];
+ $repos[$name] = ['path' => $path, 'name' => urldecode($name), 'safe_name' => $name];
}
- }
-
- // Sort alphabetically (Business Logic)
- uasort($repos, function($a, $b) {
- return strcasecmp($a['name'], $b['name']);
- });
-
- // Pass each repo to the lambda
- foreach ($repos as $repo) {
- $callback($repo);
}
+ uasort($repos, fn($a, $b) => strcasecmp($a['name'], $b['name']));
+ foreach ($repos as $repo) $callback($repo);
}
- /**
- * Determines the primary branch (main, master, trunk, etc).
- */
public function getMainBranch(): ?array {
$branches = [];
- $this->eachBranch(function($name, $sha) use (&$branches) {
- $branches[$name] = $sha;
- });
-
- $priority = ['main', 'master', 'trunk', 'develop'];
- foreach ($priority as $branch) {
- if (isset($branches[$branch])) {
- return ['name' => $branch, 'hash' => $branches[$branch]];
- }
+ $this->eachBranch(function($name, $sha) use (&$branches) { $branches[$name] = $sha; });
+ foreach (['main', 'master', 'trunk', 'develop'] as $b) {
+ if (isset($branches[$b])) return ['name' => $b, 'hash' => $branches[$b]];
}
-
if (!empty($branches)) {
- $first = array_key_first($branches);
- return ['name' => $first, 'hash' => $branches[$first]];
+ $f = array_key_first($branches);
+ return ['name' => $f, 'hash' => $branches[$f]];
}
-
return null;
- }
-
- public function eachBranch(callable $callback): void {
- $this->scanRefs('refs/heads', $callback);
}
- public function eachTag(callable $callback): void {
- $this->scanRefs('refs/tags', $callback);
- }
+ public function eachBranch(callable $callback): void { $this->scanRefs('refs/heads', $callback); }
+ public function eachTag(callable $callback): void { $this->scanRefs('refs/tags', $callback); }
public function walk(string $refOrSha, callable $callback): void {
$sha = $this->resolve($refOrSha);
if (!$sha) return;
-
$data = $this->read($sha);
if (!$data) return;
-
if (preg_match('/^tree ([0-9a-f]{40})$/m', $data, $m)) {
- $treeSha = $m[1];
- $data = $this->read($treeSha);
+ $data = $this->read($m[1]);
if (!$data) return;
- } elseif (!$this->isTreeData($data)) {
- return;
- }
+ } elseif (!$this->isTreeData($data)) return;
$pos = 0;
while ($pos < strlen($data)) {
$space = strpos($data, ' ', $pos);
$null = strpos($data, "\0", $space);
if ($space === false || $null === false) break;
-
$mode = substr($data, $pos, $space - $pos);
$name = substr($data, $space + 1, $null - $space - 1);
$entrySha = bin2hex(substr($data, $null + 1, 20));
-
- $callback((object)[
- 'name' => $name,
- 'sha' => $entrySha,
- 'isDir' => ($mode === '40000'),
- 'mode' => $mode
- ]);
-
+ $callback((object)['name' => $name, 'sha' => $entrySha, 'isDir' => ($mode === '40000'), 'mode' => $mode]);
$pos = $null + 21;
}
}
private function isTreeData(string $data): bool {
if (strlen($data) < 25) return false;
if (preg_match('/^(40000|100644|100755|120000) /', $data)) {
$null = strpos($data, "\0");
- if ($null !== false && ($null + 21 <= strlen($data))) {
- return true;
- }
+ return ($null !== false && ($null + 21 <= strlen($data)));
}
return false;
}
public function history(string $refOrSha, int $limit, callable $callback): void {
$currentSha = $this->resolve($refOrSha);
$count = 0;
-
while ($currentSha && $count < $limit) {
$data = $this->read($currentSha);
if (!$data) break;
-
$message = (strpos($data, "\n\n") !== false) ? substr($data, strpos($data, "\n\n") + 2) : '';
preg_match('/^author (.*) <(.*)> (\d+)/m', $data, $auth);
-
- $callback((object)[
- 'sha' => $currentSha,
- 'message' => trim($message),
- 'author' => $auth[1] ?? 'Unknown',
- 'email' => $auth[2] ?? '',
- 'date' => isset($auth[3]) ? (int)$auth[3] : 0
- ]);
-
+ $callback((object)['sha' => $currentSha, 'message' => trim($message), 'author' => $auth[1] ?? 'Unknown', 'email' => $auth[2] ?? '', 'date' => (int)($auth[3] ?? 0)]);
$currentSha = preg_match('/^parent ([0-9a-f]{40})$/m', $data, $m) ? $m[1] : null;
$count++;
private function resolve(string $input): ?string {
if (preg_match('/^[0-9a-f]{40}$/', $input)) return $input;
-
if ($input === 'HEAD' && file_exists($h = "{$this->path}/HEAD")) {
$head = trim(file_get_contents($h));
return (strpos($head, 'ref: ') === 0) ? $this->resolve(substr($head, 5)) : $head;
}
-
foreach ([$input, "refs/heads/$input", "refs/tags/$input"] as $p) {
if (file_exists($f = "{$this->path}/$p")) return trim(file_get_contents($f));
}
-
if (file_exists($packed = "{$this->path}/packed-refs")) {
foreach (file($packed) as $line) {
if ($line[0] === '#' || $line[0] === '^') continue;
$parts = explode(' ', trim($line));
- if (count($parts) < 2) continue;
- if ($parts[1] === $input || $parts[1] === "refs/heads/$input" || $parts[1] === "refs/tags/$input") return $parts[0];
+ if (count($parts) >= 2 && ($parts[1] === $input || $parts[1] === "refs/heads/$input" || $parts[1] === "refs/tags/$input")) return $parts[0];
}
}
return null;
- }
-
- private function findTree(string $sha): ?string {
- $data = $this->read($sha);
- return ($data && preg_match('/^tree ([0-9a-f]{40})$/m', $data, $m)) ? $m[1] : null;
}
$packs = glob("{$this->objPath}/pack/*.idx");
if (!$packs) return null;
-
foreach ($packs as $idxFile) {
$f = @fopen($idxFile, 'rb');
if (!$f) continue;
-
fseek($f, 8 + (hexdec(substr($sha, 0, 2)) * 4));
$count = unpack('N', fread($f, 4))[1];
fseek($f, 8 + (255 * 4));
$total = unpack('N', fread($f, 4))[1];
-
fseek($f, 8 + (256 * 4));
$idx = -1;
for ($i = 0; $i < $total; $i++) {
if (bin2hex(fread($f, 20)) === $sha) { $idx = $i; break; }
}
if ($idx === -1) { fclose($f); continue; }
-
fseek($f, 8 + (256 * 4) + ($total * 20) + ($total * 4) + ($idx * 4));
$offset = unpack('N', fread($f, 4))[1];
fclose($f);
-
$pf = @fopen(str_replace('.idx', '.pack', $idxFile), 'rb');
if (!$pf) continue;
fseek($pf, $offset);
$header = ord(fread($pf, 1));
while ($header & 128) { $header = ord(fread($pf, 1)); }
-
$data = @gzuncompress(fread($pf, 1024 * 512));
fclose($pf);
return $data ?: null;
}
return null;
}
}
+
new/Views.php
echo '<div class="file-list">';
-
- // Initialize Renderer
$renderer = new HtmlFileRenderer($this->currentRepo['safe_name']);
foreach ($entries as $e) {
- // Pass 0 for timestamp as standard git tree walk doesn't provide it cheaply
+ /**
+ * View Orchestration:
+ * Ask Git (the data authority) for the size, then pass it to File (the display object).
+ */
+ $size = $e['isDir'] ? 0 : $this->git->getObjectSize($e['sha']);
+
$file = new File(
$e['name'],
$e['sha'],
$e['mode'],
- 0
+ 0,
+ $size
);
$content = '';
$this->git->stream($targetHash, function($d) use (&$content) { $content = $d; });
-
$this->renderBreadcrumbs($targetHash, 'File');
echo '<h2>' . substr($targetHash, 0, 7) . '</h2>';
Delta 43 lines added, 100 lines removed, 57-line decrease