~gitpocket/gitpocket

8
a289514c9abfecce583a90db2f01c87f2dc0fdf8
mainfeed-discovery
gitpocket/README.md -rw-r--r--7.2 KiB ViewLogBlameView rawPermalink e0cad84176 feat(pages): publish the pages branch of private repositories 8 hours ago

View source

gitpocket

gitpocket is a self hosted git server in one static Go binary that serves repositories over SSH and HTTPS with a web interface rendered on the server. It idles at about 25 MB resident so it fits comfortably on a 512 MB VPS next to the git processes it starts.

Quick start

git clone https://gitpocket.io/~gitpocket/gitpocket && cd gitpocket
useradd -r -m -d /srv/gitpocket -s /bin/sh git
printf 'GITPOCKET_HOST=git.example.org\nGITPOCKET_UID=%s\nGITPOCKET_GID=%s\n' $(id -u git) $(id -g git) > deploy/.env
docker compose -f deploy/compose.yaml up -d

The web interface is then on 127.0.0.1:3200 and wants a reverse proxy with TLS in front of it. Sign up and then make yourself an administrator with docker exec gitpocket gitpocket admin you. INSTALL.md covers the Docker and binary installs, nginx, SSH, mail, backups and upgrades in order.

Features

There is no wiki and no CI and neither is planned.

Design

Every read of git data calls the plumbing with a thirty second timeout so a large repository costs exactly what git says it costs and nothing is parsed out of packfiles in process. Ref tables, trees, summaries and languages are cached against the mtime of the refs, which means one stat after a push invalidates them and deleting the cache loses nothing.

Pull requests fetch the source into refs/pull/N/head of the target. Merges are merge-tree and commit-tree followed by an update-ref that names the old value so a push racing the merge button makes the merge fail instead of being overwritten. SQLite comes from the pure Go modernc port which is why a CGO_ENABLED=0 build needs nothing beside a system git.

Usage

git clone https://git.example.org/~you/repo
git clone [email protected]:~you/repo

SSH pushes need a key added under settings and HTTPS pushes take an access token or your password. To contribute to a repository you do not own you push your commits to refs/for/ followed by the target branch, which opens a pull request on that repository without any need for a fork.

git push origin HEAD:refs/for/main -o title="Fix the parser"
git push origin HEAD:refs/for/main/parser

Pushing again with the same topic updates that pull request and records a new revision, and the topic defaults to the target branch when the ref names none. Without -o title the first commit subject becomes the title and its body becomes the description, while -o description= overrides the body and -o draft=true opens the pull request as a draft. Anyone who is not the owner or a collaborator can only push to refs/for/ and those guest pushes are capped at 20 MB each and 30 an hour.

Pages

A repository named pages is served at https://you.git.example.org/ from its pages branch when one exists and from the default branch otherwise, and any other repository with a pages branch appears beneath it at /name/. Private repositories publish only their pages branch so the site is public while the source on every other branch stays private, which matches how GitHub treats Pages built from a private repository. Paths without an extension also try .html and directories serve their index.html, while a missing file serves the site's own 404.html with a 404 status when the site provides one. Symlinks and submodules are never followed and files over 32 MB are refused, accounts younger than a day have no site at all and admins can turn any site off from the admin page.

A site can also answer on a domain of your own once you claim it under settings, publish the TXT record _gitpocket-pages.<domain> with the token shown there and point the domain at the server through a CNAME to your subdomain or matching A and AAAA records. Verification checks both records and a daily recheck drops any domain whose TXT record has disappeared, while deploy/domains.sh on the host issues the certificate and writes the nginx server block for every verified domain.

The pages origin never sets or reads cookies and the main site only uses __Host- cookies, so a script running on a user site can neither read a session from the main host nor plant a cookie that the main host would later accept.

Mirrors

Ticking keep it as a read only mirror on the import page turns the import into a pull mirror that fetches the source again every 1, 4, 8 or 24 hours and refuses pushes, web edits and merges in the meantime. Push mirrors are added under the repository settings with a URL, a username and a token, and they push with --force --prune whenever the refs change before retrying after ten minutes if a push fails. Every fetch and push checks the URL again and resolves it once to a public address that stays pinned for the whole transfer, with redirects refused and https as the only protocol allowed. Tokens are sealed with AES-GCM under mirror.key in the data directory so that a copy of the database on its own does not reveal any of them.

Development

go test ./...

The tests drive the real handler through httptest and cover each registration mode, cross site posts, private repositories, moved repositories, every merge strategy and Markdown injection payloads. The refs/for/ test builds the binary and pushes through the smart HTTP handler with a real git client, which takes several seconds, so go test -short skips it when you only want the quick checks.

Licence

MIT. KaTeX is MIT as well and Source Sans 3 is under the OFL with both notices kept beside the files.