- TypeScript 99.6%
- HTML 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
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. |
||
| nginx | ||
| public | ||
| src | ||
| systemd | ||
| .gitignore | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| post-update.sh | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
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 localmasterif the remote ref is missing. Any branch can be the root (picker in the header). Whenorigin/masteris root, localmastershows 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 noforgejofield), 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 namedforgejo. 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 diffomits 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 buildwriteswww/. nginx can serve those files. The Node process also serves them onhttp://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 documentsreposandtree:<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: BearerorX-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
reposandtree. 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/withUpgradeto127.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:
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