No description
  • TypeScript 99.6%
  • HTML 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Oliver Goodman b8c99c1dce Show commit files as a sparse path tree.
Single-child directory chains stay on one label so
a/b/c/file is not indented per segment. A real
branch gets a modest indent; status stays on the
file row.
2026-10-21 11:09:34 +10:00
nginx Add a local file and diff browser beside the tree. 2026-10-20 15:42:59 +10:00
public Add a git-branch favicon under /git/. 2026-10-16 11:41:54 +10:00
src Show commit files as a sparse path tree. 2026-10-21 11:09:34 +10:00
systemd Rebuild the GUI as a Vite shell and websocket. 2026-10-15 17:40:28 +10:00
.gitignore Rebuild the GUI as a Vite shell and websocket. 2026-10-15 17:40:28 +10:00
index.html Keep the branch favicon on nested viewer URLs. 2026-10-20 16:28:07 +10:00
package-lock.json Add a local file and diff browser beside the tree. 2026-10-20 15:42:59 +10:00
package.json Add a local file and diff browser beside the tree. 2026-10-20 15:42:59 +10:00
post-update.sh Add post-update.sh so update-repo rebuilds www. 2026-10-15 18:10:47 +10:00
README.md Make Forgejo optional via host config. 2026-10-21 10:39:18 +10:00
tsconfig.json Rebuild the GUI as a Vite shell and websocket. 2026-10-15 17:40:28 +10:00
vite.config.ts Rebuild the GUI as a Vite shell and websocket. 2026-10-15 17:40:28 +10:00

rebase-tree

Graphical default-branch tree and local file/diff browser for the git checkouts listed in ~/REPOSITORIES. Fresh implementation: live git on the server, a static Vite/React shell in the browser. It does not use bin/rebase-tree or ~/tools/rebase-tree.

What you see

The left list is every non-comment path in ~/REPOSITORIES, parsed the same way ~/bin/repos and the services repo_update reader do. The name on each row is the <name> from that repo’s origin URL (…/<name>.git).

Click a repository to open its default tree:

  • Nominated root defaults to origin/master, or local master if the remote ref is missing. Any branch can be the root (picker in the header). When origin/master is root, local master shows as a branch if it is ahead or diverging — and the other way around.
  • That root is a vertical line of dots down to the latest commits. Long stretches with nothing branching off are collapsed. Tap the gap to expand that sequence; tap again to hide it.
  • A side line is a branch. The number is how many unique first-parent commits that branch owns (the owned chain is first-class). Tap the branch to show those commits above it, oldest at the top; tap again to collapse.
  • A leading * marks a sibling or a branch coming off the root. A run without asterisks is a linear stack: each branch sits on the one above, most recent last.
  • A trailing amber * means the branch is not based at the head of its target.
  • Branches with 0 unique commits are merged. They are hidden by default; a control shows or hides them.
  • Click a commit SHA to open its local diff in this page. A branch-tip SHA opens that commit’s local diff too. Opening a local view never pushes. The browser back button returns to the tree.
  • Open in Forgejo is optional. With no ~/data/rebase-tree/config.json (or no forgejo field), the tree is local browsing only: no Forgejo window and no prompt to push. With a base URL in that file, a separate link beside the SHA still uses a window named forgejo. A branch-tip Forgejo link opens the branch page. If that object is not on origin, it asks before pushing the branch you are inspecting — it does not open a 404, and it does not push unless you say yes.
  • Header links: Tree, Files (working directory, or a selected commit/branch), and Working changes (staged, unstaged, and untracked, labelled separately). Untracked file contents are shown even though ordinary git diff omits them.

Routes

All of these are client routes under /git/. Refresh, bookmarks, and back/forward work. Direct URLs hit the Node SPA shell (or the /git/r/ nginx proxy); missing /git/api/ and /git/assets/ paths stay 404, not HTML.

URL View
/git/ Repository list
/git/r/<name> Branch tree (?repo=<name> still redirects here)
/git/r/<name>/commit/<sha> Local commit diff
/git/r/<name>/commit/<sha>/<path> One file in that commit
/git/r/<name>/changes Working changes
/git/r/<name>/changes/<path>?state=staged|unstaged|untracked One working diff
/git/r/<name>/files/<path> Working-tree file browser
/git/r/<name>/files/<path>?rev=<rev> File browser at a commit or branch

<rev> is a query value (origin/master, a SHA, HEAD). Viewer pages do not need the branch tree loaded; they still link back to it and to the same repo’s other views.

The viewer is read-only: no edit, stage, commit, or rebase controls. Diffs are unified or side-by-side. Files and diffs use @pierre/diffs behind a small rendering adapter (src/client/render/code-view.tsx). A file whose name has no extension is highlighted from its #! interpreter when that maps to a known language (#!/usr/bin/env python3, /bin/bash, and similar).

Commit diffs: a root commit is shown as adds against the empty tree; a merge is a first-parent diff. Added, deleted, and renamed files are listed. Binary and oversized files are named, not dumped as text. Repository content is JSON then code text — never served as executable HTML.

Architecture

  • Static page — npm run build writes www/. nginx can serve those files. The Node process also serves them on http://127.0.0.1:47331/git/ so the demo works without a reload.
  • One websocket — ws://127.0.0.1:47331/git/ws/. Git stays on the server. Tagged JSON envelopes, named documents repos and tree:<name>. Snapshot on watch and again on reconnect. No object proxies.
  • HTTP Git API — GET /git/api/… for status, directory listings, file contents, commit details, and diffs. Same API key as the socket (Authorization: Bearer or X-Rebase-Tree-Key), never in the URL. Git runs as argument arrays with --end-of-options / --, --no-ext-diff, and --no-textconv.
  • Client Model — owns the socket, API key, backoff, and resubscribe. React binds to repos and tree. Viewer pages fetch Git data over HTTP.
  • API key — first websocket frame and HTTP header, not the URL. File: ~/data/rebase-tree/admin.key (created 0600 on first start).

Nothing is installed at the demo.local web root. Vite base is /git/, so assets are /git/assets/…. Do not add a site-root /_next/ location.

After update-repo

An executable post-update.sh in this repo is run by services after a successful update-repo, the same way music-app’s hook is. It npm installs (needed on a fresh tree; node_modules/ is gitignored) then npm run build, which writes www/. PATH includes ~/.nvm/current/bin so the hook still finds npm when the daemon runs it as oag via su. A missing or non-executable script is skipped; a failing build fails the update.

Deploy on fangorn

cd ~/git/rebase-tree
npm install
npm run build

Install the unit from this repo (same User / WorkingDirectory style as ~/systemd/). Do not enable it from this checkout unless you mean to:

sudo cp ~/git/rebase-tree/systemd/rebase-tree.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now rebase-tree

Paste nginx/git.conf into the demo.local HTTPS server (and HTTP if you want the same path). Then nginx -t and reload — this repo does not touch nginx.

That snippet:

  • aliases /git/ to ~/git/rebase-tree/www/
  • proxies /git/ws/ with Upgrade to 127.0.0.1:47331
  • proxies /git/api/ (Git JSON) and /git/r/ (SPA shell for viewer URLs) to the same process

After pasting, nginx -t and reload. Direct viewer URLs 404 from nginx until /git/api/ and /git/r/ exist. Until then, use the process directly:

Shared-root alternative: copy www/ to ~/demo.local/git/ and drop the alias; keep the /git/ws/ location. Still nothing at /.

The live demo.local HTTPS server currently has location /git/ (HTTP proxy, no Upgrade) and no location /git/ws/. The browser opens wss://demo.local/git/ws/; nginx treats that as /git/ and the Node SPA fallback returns 200 HTML. Smallest fix: paste the /git/ws/ block from nginx/git.conf into the 443 server, nginx -t, reload. Other demo.local WS apps use Connection "upgrade" (this box has no $connection_upgrade map).

Until that location exists, use the process directly:

http://127.0.0.1:47331/git/

The browser prompts for ~/data/rebase-tree/admin.key once and stores it in localStorage.

Forgejo is optional. To enable Open in Forgejo on this host, write ~/data/rebase-tree/config.json (0600 is fine):

{
  "forgejo": "https://git.olivergoodman.com/"
}

Restart rebase-tree after editing it. Without that file, SHA clicks stay local.

Run locally

npm install
npm run build
npm start
npm test