Skip to main content
The git-upload-pack endpoint handles fetch and pull operations, allowing Git clients to retrieve objects and references from repositories. This endpoint implements Git Protocol Version 2.

Endpoint

POST /{owner}/{repo}/git-upload-pack
owner
string
required
Repository owner username or organization name
repo
string
required
Repository name (with or without .git suffix)

Authentication

This endpoint supports HTTP Basic Authentication. Public repositories allow anonymous read access, while private repositories require authentication.
Authorization: Basic base64(username:password)
Unauthenticated requests to private repositories receive:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Git"

Request

Headers

Content-Type
string
default:"application/x-git-upload-pack-request"
Content type for upload-pack requests
Authorization
string
HTTP Basic Authentication credentials (required for private repos)

Body

The request body contains pkt-line encoded commands. Two commands are supported:

ls-refs command

Lists references available in the repository.
command=ls-refs
<delimiter>
[peel]
[symrefs]
[ref-prefix <prefix>]
Arguments:
peel
boolean
Include peeled references (annotated tags)
symrefs
boolean
Include symbolic reference targets (e.g., HEAD)
ref-prefix
string
Filter references by prefix (can be specified multiple times)

fetch command

Fetches objects from the repository.
command=fetch
<delimiter>
want <oid>
[have <oid>]
[done]
[thin-pack]
[no-progress]
[include-tag]
[ofs-delta]
[sideband-all]
[shallow <oid>]
[deepen <depth>]
[deepen-relative]
[deepen-since <timestamp>]
[deepen-not <ref>]
[filter <filter-spec>]
Arguments:
want
string
required
Object ID (SHA-1) the client wants to fetch. Can be specified multiple times.
have
string
Object ID (SHA-1) the client already has. Can be specified multiple times.
done
boolean
Client has sent all have lines and is ready to receive packfile
thin-pack
boolean
Request thin pack format (delta references to objects not in pack)
no-progress
boolean
Suppress progress messages in side-band channel 2
include-tag
boolean
Include tags that reference wanted commits
ofs-delta
boolean
Support offset deltas in packfile
sideband-all
boolean
Multiplex all output through side-band
shallow
string
Mark commit as shallow boundary. Can be specified multiple times.
deepen
number
Deepen shallow clone by specified number of commits
deepen-relative
boolean
Deepen relative to current shallow boundary
deepen-since
number
Deepen to commits newer than timestamp
deepen-not
string
Deepen until specified ref is not reachable
filter
string
Partial clone filter specification (e.g., blob:none, tree:0)

Response

ls-refs response

status
number
HTTP status code (200 for success)
Content-Type
string
application/x-git-upload-pack-result
body
binary
Pkt-line encoded reference list
The response body contains pkt-line encoded references:
<oid> <ref-name> [symref-target:<target>]
<oid> <ref-name>^{} (peeled)
...
0000 (flush)

fetch response

status
number
HTTP status code (200 for success, 500 for pack errors)
Content-Type
string
application/x-git-upload-pack-result
body
binary
Pkt-line encoded response with optional packfile
The response structure depends on whether the client sent done:

Without done (negotiation phase)

acknowledgments
[NAK | ACK <oid> ...]
ready
<delimiter>

With done (packfile delivery)

packfile
remote: Counting objects: <count>, done.
remote: Compressing objects: 100% (<count>/<count>), done.
<side-band multiplexed packfile data>
remote: Total <count> (delta 0), reused <count> (delta 0)
0000 (flush)
The packfile is multiplexed through side-band-64k:
  • Channel 1: Packfile data
  • Channel 2: Progress messages (unless no-progress)
  • Channel 3: Error messages

Examples

List all references

curl -X POST https://gitflare.example.com/johndoe/myrepo/git-upload-pack \
  -H "Content-Type: application/x-git-upload-pack-request" \
  --data-binary @- << EOF
0014command=ls-refs
00010009peel
000csymrefs
0000
EOF

Fetch specific commit

curl -X POST https://gitflare.example.com/johndoe/myrepo/git-upload-pack \
  -u "username:password" \
  -H "Content-Type: application/x-git-upload-pack-request" \
  --data-binary @- << EOF
0011command=fetch
0001000ewant abc123...
0009done
000ethin-pack
000dno-progress
0000
EOF

Implementation details

The upload-pack endpoint is implemented across several components:

Route handler

apps/web/src/routes/$owner/$repo/git-upload-pack.ts:5 Handles authentication and forwards requests to the Durable Object:
export const Route = createFileRoute("/$owner/$repo/git-upload-pack")({
  server: {
    handlers: {
      POST: async ({ request, params }) => {
        // Verify authentication
        const isAuthorized = await verifyAuth({
          owner,
          repo: repoName,
          req: request,
          service: "upload-pack",
        });

        // Forward to Durable Object
        return stub.fetch("https://do/git-upload-pack", {
          method: "POST",
          body: request.body,
        });
      },
    },
  },
});

Durable Object handler

apps/web/src/do/repo.ts:100 Processes the request within the repository Durable Object:
if (pathname === "/git-upload-pack" && request.method === "POST") {
  const result = await this.uploadPack(data);
  return result;
}

Protocol implementation

apps/web/src/do/repo.ts:157 Handles command parsing and execution:
async uploadPack(data: Uint8Array) {
  const { command, args } = parseCommand(data);

  if (command === "ls-refs") {
    const { refs, symbolicHead } = await this.git.listRefs();
    return await buildLsRefsResponse(refs, args, symbolicHead, ...);
  }

  if (command === "fetch") {
    const fetchRequest = parseFetchRequest(data, args);
    const commonCommits = await this.git.findCommonCommits(
      fetchRequest.haves
    );

    if (fetchRequest.done && fetchRequest.wants.length > 0) {
      const objectsToPack = await this.git.collectObjectsForPack(
        fetchRequest.wants,
        fetchRequest.haves
      );
      packfileData = await this.git.packObjects(objectsToPack);
    }

    return await buildFetchResponse({
      commonCommits,
      packfileData,
      noProgress: fetchRequest.capabilities.noProgress,
      done: fetchRequest.done,
    });
  }
}

Core protocol functions

The protocol implementation uses these key functions:
  • parseCommand(): Parse command and arguments from pkt-line data (apps/web/src/git/protocol.ts:173)
  • parseFetchRequest(): Extract fetch parameters (apps/web/src/git/protocol.ts:243)
  • buildLsRefsResponse(): Build reference list response (apps/web/src/git/protocol.ts:322)
  • buildFetchResponse(): Build packfile response with side-band (apps/web/src/git/protocol.ts:434)

Git operations

apps/web/src/git/service.ts Provides Git operations using isomorphic-git:
  • listRefs(): List all references with OIDs (service.ts:33)
  • findCommonCommits(): Find commits client already has (service.ts:324)
  • collectObjectsForPack(): Walk object graph to collect objects (service.ts:213)
  • packObjects(): Create packfile from object list (service.ts:297)

Error responses

Unauthorized access

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Git"

Unauthorized

Pack operation failure

HTTP/1.1 500 Internal Server Error

ERR pack-objects failed: <error message>

Unsupported command

HTTP/1.1 400 Bad Request

Unsupported command

Git Protocol Overview

Learn about the Git Smart HTTP Protocol implementation

git-receive-pack

Push operations endpoint

Build docs developers (and LLMs) love