Skip to content

feat(github): support git clone over smart HTTP#174

Open
MaxLeiter wants to merge 3 commits into
vercel-labs:mainfrom
MaxLeiter:maxleiter/github-git-http
Open

feat(github): support git clone over smart HTTP#174
MaxLeiter wants to merge 3 commits into
vercel-labs:mainfrom
MaxLeiter:maxleiter/github-git-http

Conversation

@MaxLeiter

Copy link
Copy Markdown

Adds the read side of the git smart HTTP protocol to the GitHub emulator, so a
real git client can clone fixture repos:

git clone http://x-access-token:$TOKEN@localhost:4001/octocat/hello-world.git

Why

The emulator covers GitHub's REST surface, but anything exercising an actual
clone/fetch (CI harnesses, agents that do real repo work) currently needs a
separate fake git server next to emulate — splitting fixture state and token
auth across two processes. This closes that gap: the same seeded repos and the
same minted/seeded tokens now serve both REST and the git wire protocol.

How

  • GET /:owner/:repo/info/refs?service=git-upload-pack (ref advertisement) and
    POST /:owner/:repo/git-upload-pack (clone / full fetch), protocol v0.
  • Pure TypeScript object + packfile generation (git-objects.ts, git-data.ts) —
    no git binary or filesystem dependency; everything resolves through the
    in-memory store, and repos materialize with canonical git SHAs so REST git-data
    responses and served packfiles agree. Fixture SHAs are deterministic across
    restarts.
  • Repo fixtures gain a files map (path → content); auto_init repos serve
    their generated README.
  • Auth is stricter than the REST routes on purpose: a presented token must be
    one this instance seeded or minted (e.g. via
  • POST /app/installations/:id/access_tokens) — unknown tokens get a 401 rather
    than falling back to a default user, so clone tests can't pass vacuously.
  • Public repos clone anonymously; private repos require a token with access.
  • Request bodies are size-limited both compressed and inflated; fixture paths
    reject .., .git, and null-byte segments.
  • Out of scope / for follow up(rejected with clear errors): push (git-receive-pack), shallow
    clones, partial fetches.

MaxLeiter and others added 3 commits June 8, 2026 19:06
- Private-repo access in authorizeGitRequest now relies solely on
  canAccessRepo (user-id comparison): the login-string fast path would
  trust any token whose login collides with the owner's, even when the
  token maps to a different or nonexistent user.
- Empty-repo advertisements include symref=HEAD:refs/heads/<default_branch>,
  matching real GitHub, so clones initialize local HEAD from the server
  instead of the client's init.defaultBranch.
- identityLine throws on unparseable commit dates instead of silently
  minting epoch (1970) timestamps into sha-stable commits.

Found by an automated review sweep; adds tests for all three.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

@MaxLeiter is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant