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

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

Use git-upload-pack to diagnose fetch-side repository serving

You will verify what git-upload-pack does, test a repository path without changing the repository, and recognise the options that matter when a fetch or clone fails. Allow about ten minutes. You need Git installed and read access to a repository that 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 machine before relying on an option, because the installed version is the behaviour that 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

If the second command prints nothing or exits unsuccessfully, 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 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. That default is handy for a local working tree and easy to overlook when a service points at the wrong path.

First inspect the path 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 a path you cannot read, will fail before a useful fetch can begin. Fix the path or its permissions rather than adding 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 directly to a terminal: 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

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

The file under /tmp is only a diagnostic capture. Remove it when you have finished reviewing it, and do not publish it as a log without considering whether branch names or other repository information should be exposed.

4. Use strict mode when the path must be exact

--strict disables the fallback to <directory>/.git. This is useful for a service configuration where a path must name the Git directory itself. It also 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 the path and Git build. The useful distinction is that strict mode refuses to search below the argument. If a remote service is already working, do not change its path casually: changing the argument can disrupt every fetch and clone using it. Change the 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. A server 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, whereas 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. If a fetch is cut off, check both sides for long pauses, overloaded storage and proxy idle limits before simply 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 obtain 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. This is deliberate: a fetch operation can run commands from configuration and hooks in the source repository.

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. If the 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 that 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.