Home / Alt manpages / gitprotocol-v2(5)

  • gitprotocol-v2(5)
  • File format
  • linux

Make Git Wire Protocol v2 Predictable

Git wire protocol v2 is already the default almost everywhere, but "default" is not the same as "actually negotiated". You will end up with v2 enabled for the scope you choose, a trace that proves which version got used, and a real explanation when a remote falls back. The commands below were checked with Git 2.43.0, the installed version on this machine.

Allow about 15 minutes. You need Git and a repository with a reachable remote. These are ordinary user commands: nothing here needs sudo, and no command changes a server or rewrites repository history.

1. Understand what protocol v2 changes

Protocol v2 is a request and response wire format for Git transports. A client first asks for version 2 and gets back a capability advertisement, then it can request commands such as ls-refs or fetch. Reference advertisement is no longer bundled automatically into the first exchange, so ls-refs requests exactly the refs the client needs.

This is a transport detail. It changes none of your branch names, object IDs, authentication method or repository contents, and it is no guarantee of a faster transfer either: the server, network and operation still decide that.

Git 2.43.0 documents protocol version 2 as the default when protocol.version is unset. The client falls back to version 0 if the server does not support the requested version, and that default is not proof a particular remote accepted v2, so verify the exchange when it actually matters.

2. Check the Git version and current setting

Start by recording the client version and any explicit setting:

$ git --version
git version 2.43.0
$ git config --show-origin --get protocol.version

The second command may print nothing, which only means no explicit value turned up in the normal Git configuration files, not that protocol v2 is unavailable. Ask Git what it would use without changing configuration:

$ git config --get protocol.version || printf '%s
' 'unset; Git default applies'

Checkpoint

Keep the version output. A trace from a different Git executable or wrapper can otherwise send you looking at the wrong client entirely.

3. Pin protocol v2 for one operation

Use -c when testing a remote, or when one job needs an explicit setting without touching persistent files:

$ git -c protocol.version=2 ls-remote https://git.example.invalid/team/project.git

Replace the URL with a real remote you are authorised to read. ls-remote asks for references and creates or updates no local branch. It may still contact your credential helper, proxy or server, so do not paste secrets into the URL.

If the server accepts v2, Git uses the protocol's ls-refs command for this operation. If it does not, Git's documented fallback is version 0, and a successful command on its own does not tell those two cases apart.

4. Verify the negotiated version

Enable packet tracing only for this diagnostic command:

$ GIT_TRACE_PACKET=1 git -c protocol.version=2 ls-remote https://git.example.invalid/team/project.git

Look in the diagnostic output for a server line containing version 2, followed by capabilities such as ls-refs. A local Git 2.43.0 file-transport check produced output shaped like this:

packet:  upload-pack> version 2
packet:  upload-pack> agent=git/2.43.0
packet:  upload-pack> ls-refs=unborn
packet:    ls-remote> command=ls-refs

Warning

Trace output can expose ref names, server details and protocol metadata. Treat it as diagnostic data, not a log to publish or attach without review. It is sent to standard error, while the normal ref listing stays on standard output.

Checkpoint

If you cannot find version 2, do not call the result v2 merely because the command succeeded. Check the remote URL, the selected Git binary, and whether a wrapper or proxy changed the request.

5. Choose a persistent scope

Once the one-operation test works, set the value only where you need it. For every repository:

$ git config --global protocol.version 2
$ git config --global --get protocol.version
2

For one repository, drop --global while inside it:

$ git config protocol.version 2
$ git config --get protocol.version
2

The repository setting suits testing a single remote. The global setting reaches every future operation for your user, scripts and other repositories included, so it is a much broader change, though still only a client preference, never a server configuration.

Recovery

To undo either change, remove the setting at the same scope:

$ git config --global --unset protocol.version

For a repository-local setting, run git config --unset protocol.version there instead. The command exits non-zero if no value existed, which is harmless when the setting was already absent. Re-run the command from step 2 to confirm the intended scope is actually gone.

6. Account for transport differences

For HTTPS, Git requests v2 through the Git-Protocol: version=2 HTTP header. A Git HTTP backend must pass that into GIT_PROTOCOL, and a reverse proxy or hosting service that drops the header can make even a capable client fall back.

For SSH and file transports, the client uses the GIT_PROTOCOL environment variable directly. Git normally arranges this itself for its own transport operations, but an SSH server or forced command may need to let the variable through. Do not add arbitrary environment forwarding to a shared SSH service without reviewing its server policy and trust boundary first.

For the git:// transport, the request carries version=2 as an extra protocol parameter, and the server still decides whether it can answer with v2. These transport-specific mechanisms are exactly why copying an HTTP header into an SSH configuration, or the reverse, is not a reliable fix.

7. Troubleshoot a fallback safely

First reproduce the smallest read-only operation with tracing:

$ GIT_TRACE=1 GIT_TRACE_PACKET=1 git -c protocol.version=2 ls-remote origin

Use origin only if that is really the remote name in this repository; check it with:

$ git remote -v

If the trace shows version 0 or no version 2 advertisement, work through these in order:

  • Confirm the binary. Make sure the command is using the Git version you checked, not another installation earlier in a service account's PATH.
  • Confirm the transport. Check the remote URL actually uses the transport you think it does.
  • Check the middle. An HTTP proxy, SSH forced command or Git server may be stripping the version request.
  • Check the server. Confirm it is new enough, and configured, to support protocol v2 at all.

Warning

Do not force an undocumented capability or edit packet bytes by hand. Clients must ignore unknown capability keys, and command requests must use capabilities the server actually advertised. If the fallback is consistent and the operation still works, leave it in place while the server owner investigates. If the operation fails, capture the trace after redacting URLs, repository names and credentials, then hand it to the remote administrator.

Done means

  • Identified the client that git --version actually reports.
  • Chose one-operation, repository-local or global configuration deliberately, not by accident.
  • Confirmed version 2 in a traced ls-remote against a remote that accepts it.
  • Understood that an otherwise successful operation may have fallen back to version 0.
  • Treated any packet trace as sensitive diagnostic output and reviewed it before sharing.