motiejus/stagit2

single-page version of stagit
git clone https://git.jakstys.lt/motiejus/stagit2.git
Log | Tree | Refs | README

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.