Pygitweb
Gitweb reimplementation using Python, FastAPI, and Pygit2, ported from git/gitweb/gitweb.perl.
Config
Settings load from ~/.pygitweb/settings.json (or PYGITWEB_SETTINGS_CONFIG), PYGITWEB_*
environment variables, and optionally a .env file in the working directory via
pydantic-settings. See src/pygitweb/config.py and settings.schema.json for the full schema. Common ones:
PYGITWEB_PROJECTROOT— absolute path to directory containing git repositories (default:$HOME)PYGITWEB_PROJECTS_LIST— directory to scan, or path to a project-list file (default:PROJECTROOT)PYGITWEB_EXPORT_OK— filename that must exist to allow export (e.g.git-daemon-export-ok); empty = no checkPYGITWEB_SITE_NAME— site name in titles (default:PyGitWeb)PYGITWEB_GIT— path to the git executable (default:git)PYGITWEB_AUTH—0/false/offdisables auth;1/true/onenables it. If unset or empty, auth defaults to on.PYGITWEB_AUTH_CONFIG— path to the auth JSON file (default:~/.pygitweb/auth.json). Generated to a reasonable default if not present.PYGITWEB_SETTINGS_CONFIG— path to the settings JSON file (default:~/.pygitweb/settings.json). Created from the environment on first run if missing.PYGITWEB_ENABLED_PLUGINS— installed plugins to load. The settings page can change this list and reload plugins immediately (POST /settings/pygitweb/plugins).PYGITWEB_API_KEY_MAX_COUNT— maximum Smart HTTP API keys each local user may hold (default:2).PYGITWEB_API_KEY_EXPIRY_DAYS— lifetime of a newly issued API key, in days.0means the key does not expire (the default).- Browser login:
GET /login(optional?next=/path),POST /loginwith form fieldsusername,password,next; sets an HttpOnly cookie with an opaque in-process session id (also accepted by protected routes viaAuthorization: Bearer). Sessions are wiped when the process exits.GET /logout?next=/revokes the session and clears the cookie. - Account and Smart HTTP keys (auth required):
GET /useris the account page.POST /user/keyswith JSON{"id": "...", "readonly": false}issues a key and returns the plaintext secret once; only its scrypt hash is stored.readonly: trueallows fetch but not push.DELETE /user/keys?id=<id>revokes one key. Clone and push attempts are appended to~/.pygitweb/smart_http.log.
Routes
Load the /docs page for a detailed view of routes (below the readme).
No-project routes
GET /: project listGET /index: plain text project index (path, owner)GET /opml: OPML feed listGET /project/{name}: Project dispatch (See actions table)
Hook management routes
GET /project/{name}/hooks: JSON{hooks: [...samples...], bundles: [...]}with current status (installed,not_installed,different) for every pygittools sample and bundlePOST /project/{name}/hook?name=<sample-or-bundle>&op=<add|remove|check>: install, remove, or check one sample or bundle.namemay be a sample filename (e.g.post-receive.notify) or a bundle name (e.g.update, which expands topost-commit.notify+post-receive.notify).add/removerefuse to clobber a custom hook with different content (returns409)
Actions
| Action | Query parameters | URL | Description |
|---|---|---|---|
| summary | (default) or a |
GET /project/{project} |
Project summary (description, owner, HEAD, tree link). |
| tree | h, f |
GET /project/{project}?a=tree&h=...&f=... |
Directory listing (tree). |
| blob | h, f |
GET /project/{project}?a=blob&h=...&f=... |
File view (HTML). |
| blob_plain | h, f |
GET /project/{project}?a=blob_plain&h=...&f=... |
Raw file download. |
| log | h, updates, pf |
GET /project/{project}?a=log&h=... |
Commit log. Supports long-poll subscribe. |
| shortlog | h, updates, pf |
GET /project/{project}?a=shortlog&h=... |
Shortlog. Supports long-poll subscribe. |
| history | h, f, updates, pf |
GET /project/{project}?a=history&h=...&f=... |
History of a file or path. Supports long-poll subscribe. |
| heads | updates, pf |
GET /project/{project}?a=heads |
List branch heads. Supports long-poll subscribe. |
| tags | updates, pf |
GET /project/{project}?a=tags |
List all tags. Supports long-poll subscribe. |
| tag | h |
GET /project/{project}?a=tag&h=... |
Single tag view (tag ref or hash). |
| commit | h |
GET /project/{project}?a=commit&h=... |
Commit information. |
| commitdiff | h |
GET /project/{project}?a=commitdiff&h=... |
Commit diff (unified diff rendered with diff2html). |
| patch | h |
GET /project/{project}?a=patch&h=... |
Single-commit patch (plain text). |
| patches | h, hb |
GET /project/{project}?a=patches&h=...&hb=... |
Multi-commit patches for range hb..h (plain text). |
| blobdiff | h, hb, f, fp |
GET /project/{project}?a=blobdiff&h=...&hb=...&f=...&fp=... |
Blob diff (two versions of a file) rendered with diff2html. |
| blobpatch | h, hb, f, fp |
GET /project/{project}?a=blobpatch&h=...&hb=...&f=...&fp=... |
Blob diff as plain unified diff. |
| remotes | — | GET /project/{project}?a=remotes |
List repo remotes. |
| object | h |
GET /project/{project}?a=object&h=... |
Show object by type (commit, tree, tag, or blob). |
| blame_raw | h, f |
GET /project/{project}?a=blame_raw&h=...&f=... |
File blame as JSON: array of {commit_id, commit_msg, commit_author, time, line_range}. Contiguous lines from the same commit are merged. |
| blame | h, f |
GET /project/{project}?a=blame&h=...&f=... |
File blame HTML view (syntax-highlighted source, line numbers, commit links per line range; hover shows author and date). |
| blame_incremental | — | TODO | |
| blame_data | — | TODO | |
| rss | — | TODO | |
| atom | — | TODO | |
| search | patterns, paths, globs, heading, sort, max_count, multiline |
GET /project/{project}?a=search&patterns=... |
Ripgrep search (via python-ripgrep); returns JSON. paths are relative to the project tree and cannot escape it. |
| search (page) | — | GET /project/{project}/search |
Per-project search UI page. |
| search_help | — | TODO |
Blog, boards, and tasks
GET /project/{project}/blog/: markdown blog page whenarticles/manifest.jsonexists atHEAD. Otherwise the summary link stays disabled.GET /project/{project}/blog/raw/{path}: raw blob fromHEADfor the blog viewer (markdown, manifest, and assets). Paths that escape the tree return404.POST /tasks/move?project=<name>&board=<source>&task=<ref>&target=<board>: move a task onto another board, such as a sprint board. Column headers on the board page show a running points total.pointson create and update accepts0,1,2,3,5,8,13, or21; any other value is stored as unestimated.POST /tasks/archive?project=<name>&board=<source>&task=<ref>: move a task to the archive board.POST /tasks/delete?project=<name>&board=<source>&task=<ref>: delete a task.
Query parameter short names (CGI mapping)
p→ projecta→ actionf→ file_namefp→ file_parenth→ hashhp→ hash_parenthb→ hash_basehpb→ hash_parent_basepg→ pageo→ orders→ searchtextst→ searchtypesf→ snapshot_formatopt→ extra_optionssr→ search_use_regexpby_tag→ ctagds→ diff_stylepf→ project_filterupdates→ long-poll subscription flag (trueparks the request up to 30s; returns200 OKwith an empty body on timeout/shutdown, or the action data when the queue is notified)
Live updates (long polling)
Supported actions (history, log, shortlog, heads, tags) accept updates=true to
subscribe to the project's change queue. The request is held for up to 30 seconds:
- A
POST /_internal/notify?project=<name>(typically from a server-side git hook) wakes matching subscribers, who then receive the freshly-rendered action response. - If no notification arrives in 30 seconds (or the server is shutting down), the response is
200 OKwith an empty body. - Combine with
pf=<prefix>to subscribe to every project under a path prefix instead of just the URL project (the action is still rendered for the URL project).
The notify endpoint is intended for loopback use by pygittools post-receive hooks (see
pygittools/hook_samples/post-receive.notify). The server enforces loopback-only access and
rejects unknown projects; production deployments should still restrict it at the reverse proxy.
Hook management
The project summary page exposes a Hooks row that lists installed hooks and lets you
install or remove pygittools samples, including the update bundle: post-receive.notify
(feeds the change queue when refs are pushed) and post-commit.notify (feeds the change
queue when a working clone of this repo records a local commit). Either is sufficient to
wake long-poll subscribers; installing both covers server-side and client-side commit paths.
The same operations are available programmatically via
POST /project/{name}/hook?name=<sample-or-bundle>&op=<...>. Bundle status is
INSTALLED only when every member is installed, DIFFERENT if any member's path holds
a custom hook (the bundle then refuses to install or remove anything to preserve the
custom hook), otherwise NOT_INSTALLED. When PYGITWEB_AUTH is enabled, add and remove
require a valid access token (Authorization: Bearer … from POST /token, or the cookie from POST /login); check is always allowed.
Metadata
Release files for pygitweb 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pygitweb-0.3.0.tar.gz | 707.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pygitweb-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.5 MB
Release files / pygitweb-0.3.0.tar.gz
| Download URL | pygitweb-0.3.0.tar.gz |
|---|---|
| Size | 707.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
eec7845bdba7644a9d94c42ffd23cb8d8b627b5c307ddb35363f185d1086cac4
|
|
BLAKE2b-256 checksum How to use checksums |
7242c1b11fd4ea94424f7030147189ef8f03ade50e7e82b5884488c6b7487091
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / pygitweb-0.3.0-py3-none-any.whl
| Download URL | pygitweb-0.3.0-py3-none-any.whl |
|---|---|
| Size | 774.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e1bc92a144bd2054813b6c6067aad7f809f88c899cf8f8486c19709f0c2773cf
|
|
BLAKE2b-256 checksum How to use checksums |
b557e66976a5c558044278bf822b85f3bbec0d4c5ebd0b32bf9d6a80f85d0f9a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|