Home / Alt manpages / git-upload-pack(1)

  • git-upload-pack(1)
  • User command
  • linux

Use git-upload-pack to Diagnose Fetch-Side Repository Serving

git-upload-pack is the plumbing command that actually serves a clone or fetch. Test it directly and you learn fast whether a failure is the repository path or the transport above it. Allow about ten minutes. You need Git installed and read access to a repository you are allowed to inspect.

1. Check the installed Git version

git-upload-pack is a Git plumbing command. A fetch-side client, such as git-fetch-pack behind git fetch or git clone, asks what the server has and which objects it needs; the upload side then packs and sends those objects back. It is not the command you normally type for an ordinary fetch.

The local manual page used for this guide is from Git 2.43.0, dated 2 July 2025. Check your own machine before relying on any option here, because the installed version is what actually matters when you are debugging a real service.

$ git --version
git version 2.43.0
$ command -v git-upload-pack
/usr/bin/git-upload-pack

Second command prints nothing, or fails? Install the Git package through your normal system management process. You do not need elevated privileges for the checks below when the repository and destination are readable and writable by your own account.

2. Understand the directory argument

The final argument is the repository to sync from. It can be a bare repository such as /srv/git/project.git, or a normal working tree such as /srv/project. By default, if the supplied directory is not itself recognised as a Git directory, upload-pack may try <directory>/.git. Handy for a local working tree, and easy to forget when a service points at the wrong path.

Inspect the path first, without invoking the protocol:

$ git -C /srv/git/project.git rev-parse --is-bare-repository
true
$ git -C /srv/project rev-parse --git-dir
/srv/project/.git

A path that is not a repository, or one you cannot read, fails before any useful fetch can begin. Fix the path or its permissions rather than reaching for a broad privilege escalation. A Git service account should have the smallest access needed to read the repositories it serves.

3. Advertise refs as a safe diagnostic

Use --advertise-refs to ask upload-pack to emit the initial reference advertisement. This is protocol data, not a human-readable report; do not send it straight to a terminal, since it contains packet framing and may contain binary or control characters.

$ git-upload-pack --advertise-refs /srv/git/project.git > /tmp/project-upload-pack.refs
$ test -s /tmp/project-upload-pack.refs && echo "advertisement received"
advertisement received
$ od -An -tc -N4 /tmp/project-upload-pack.refs
   0   1   0   d

Exact refs and capabilities depend on the repository and Git version. Those four characters at the start are packet framing, not a status message to read as ordinary text. A successful command and a non-empty file prove upload-pack could open the repository and produce an advertisement; they do not prove a complete fetch will succeed.

The file under /tmp is only a diagnostic capture. Remove it once you have reviewed it, and think before publishing it as a log: branch names and other repository information may be exposed.

4. Use strict mode when the path must be exact

--strict disables the fallback to <directory>/.git. Useful for a service configuration where a path must name the Git directory itself, and it explains a common surprise: a normal working-tree path can work without --strict and fail with it.

$ git-upload-pack --strict --advertise-refs /srv/project
fatal: '/srv/project' does not appear to be a git repository

For a normal working tree in strict mode, pass its Git directory explicitly:

$ git-upload-pack --strict --advertise-refs /srv/project/.git > /tmp/project-gitdir.refs
$ test -s /tmp/project-gitdir.refs && echo "strict advertisement received"
strict advertisement received

The exact fatal message can vary slightly with path and Git build. The useful distinction: strict mode refuses to search below the argument. Remote service already working? Do not change its path casually, since altering the argument can disrupt every fetch and clone using it. Change service configuration only during a planned maintenance window, then test a real read-only clone or fetch from a test account.

5. Leave protocol options to their transport

--stateless-rpc makes one read-write cycle on standard input and standard output, then exits. That matches HTTP POST handling, where a backend reads one request, writes one response, and must stop. It is not a replacement for the normal SSH or local transport invocation.

--http-backend-info-refs is for git-http-backend when it serves an info/refs?service=git-upload-pack request. If a web server runs that backend, configure the web server and backend according to the transport documentation; running this flag by hand is unlikely to repair an HTTP clone, because the surrounding request and response handling is part of the protocol.

GIT_PROTOCOL is an internal protocol-handshake variable. An administrator may need to arrange for a transport to pass it through. If protocol version negotiation behaves differently over two transports, compare the transport configuration before forcing environment variables globally.

6. Set an inactivity timeout carefully

--timeout=<n> interrupts a transfer after <n> seconds of inactivity. It is not a maximum duration for the whole fetch: a large repository can transfer for longer than the number while still making progress, while a stalled connection can hit the limit sooner.

$ git-upload-pack --timeout=60 /srv/git/project.git

This command expects the other side of the Git protocol on standard input and output, so starting it in a shell with no client attached is not a useful end-to-end test. Set the timeout in the component that launches upload-pack, and test with the real transport. Fetch cut off? Check both sides for long pauses, overloaded storage and proxy idle limits before just increasing the number.

7. Treat partial-clone behaviour as a trust boundary

For a partial repository created with a filter, the server-side upload-pack might need to fetch missing objects from its upstream. The installed manual says upload-pack refuses that lazy fetch by default and internally sets GIT_NO_LAZY_FETCH=1. That is deliberate: a fetch operation can run commands from configuration and hooks in the source repository.

Warning

You can explicitly set GIT_NO_LAZY_FETCH=0 only when you trust the repository and its upstream. That is a security-sensitive exception, not a general fix for missing objects:

$ GIT_NO_LAZY_FETCH=0 git-upload-pack /srv/git/partial-project.git

Keep the default when serving an untrusted or merely unfamiliar repository. Request needs objects that are not present locally? Repair or complete the repository through an administrative process you control, rather than enabling lazy fetching on every service invocation.

Done means

  • git --version identifies the Git behaviour you are diagnosing.
  • The directory argument resolves to the intended bare repository or Git directory.
  • A ref advertisement was captured away from the terminal and returned successfully.
  • You understand strict mode rejects a working-tree path unless its Git directory is supplied.
  • Timeout, HTTP and partial-clone options are configured only where their transport and trust assumptions are understood.