README.md (4006B) - Raw
1 # stagit2 2 3 Client-side git repository browser with [stagit](https://codemadness.org/stagit.html)'s 4 UX: a single `stagit.wasm` (Zig, no libgit2) renders repository index, 5 log, commit, tree, blob and refs pages in the browser, reading **bare git 6 repositories served as plain static files** (git's dumb HTTP layout) 7 lazily via HTTP Range requests. The server is any static file server that 8 honors `Range` — nothing else runs server-side, and repositories are used 9 **as-is**: multiple packs, cruft packs and loose objects are all handled, 10 no repacking ever required. 11 12 Site layout (e.g. git.jakstys.lt): 13 14 /index.html /stagit.js /stagit.wasm /style.css the frontend 15 /repositories.txt "motiejus/config.git" per line 16 /motiejus/config.git/... bare repos (dumb HTTP) 17 18 Routes (hash-based; the repo is the leading segments ending in ".git"): 19 20 # repository index 21 #owner/repo.git log of HEAD 22 #owner/repo.git/log/<ref>[/pN] log, paginated 23 #owner/repo.git/commit/<oid> commit with diffstat + full diff 24 #owner/repo.git/tree/<ish>[/<path>] directory listing 25 #owner/repo.git/blob/<ish>/<path> file view (line links: ?l17) 26 #owner/repo.git/refs branches and tags 27 28 ## How it works 29 30 The `.idx` pack indexes git already maintains are the random-access key: 31 oid lookups interpolation-search the sorted sha1 table through small 32 Range-fetched windows (all packs probed, largest first), objects inflate 33 from speculative pack spans, delta chains resolve recursively, and loose 34 objects are fetched individually. Log pages exploit pack layout: commits 35 sit contiguously at the pack front, so consecutive objects are hashed 36 speculatively into an oid→offset memo and parent lookups skip the idx 37 entirely. Every loaded object is SHA-1-verified against its oid. 38 39 Cold-page transfer on linux.git served as-is (8.7GB, 13 packs, 17.8M 40 objects, 1.3M commits, 7.7k refs): front page ~2.5MiB, tree ~2.1MiB, 41 commit ~3MiB, refs ~2.5MiB. Small repos: a few hundred KiB per page. 42 Warm in-session navigation is nearly free. 43 44 ## Server-side preparation 45 46 Per repository, once (the stock post-update hook already does this on 47 every push): 48 49 git -C repo.git update-server-info 50 51 That writes `info/refs` and `objects/info/packs` — plain text files, no 52 object data is touched. Then list the repo in `/repositories.txt`. 53 Optional per repo: `description` and `owner` files (shown on the index 54 page; `git-new-repo`-style descriptions work as-is). 55 56 ## Build 57 58 zig build --release=fast # zig 0.16 59 60 Outputs `zig-out/www/` (deployable frontend) and `zig-out/bin/stagit2` 61 (native test harness). 62 63 ## Testing 64 65 zig build test # unit tests 66 zig-out/bin/stagit2 /tmp/site "" "x.git/refs" # fetch simulator 67 node test/node-driver.mjs zig-out/www/stagit.wasm /tmp/site 68 node test/serve.mjs /tmp/site 8637 # then open a browser 69 70 The fetch simulator renders routes exactly like the browser would and 71 prints per-route round trips, request counts and bytes fetched. 72 73 ## Limitations 74 75 - SHA-1 repositories only; sha256 repos are rejected with a clear error. 76 - The log view shows Date/Message/Author (per-commit diffstat columns 77 would require fetching most of the pack; the commit page shows full 78 per-file counts instead). 79 - Tree listings show sizes only for objects already loaded this session. 80 - Rename detection covers exact (100%) renames only — content-modified 81 renames would need every added+deleted blob fetched. Diffs are plain 82 Myers, so hunk alignment can differ cosmetically from git's histogram. 83 - Refs pages of repos with >200 refs list tags without dates (each date 84 costs a commit fetch; linux.git has 7.7k tags). 85 - A concurrent repack can invalidate a browsing session (per-object sha1 86 verification catches it; reload recovers). 87 88 This was written by an LLM.