Use Git's fd:: Transport When a Script Owns the Connection
You will finish with the correct URL shape for making Git use file descriptors supplied by another program. This is useful when a supervisor has already opened a socket or created pipes to a Git service, and you need git fetch, git push or git archive to use that stream. The examples match the installed Git 2.43.0 manpage, from git-man package version 1:2.43.0-1ubuntu7.3.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes if you already have a supervising process. You need Git and a process that owns the relevant descriptors. This guide does not create a network connection, start git-upload-pack, or change a repository. There is no elevated-privilege step.
1. Check the local Git contract
Start by confirming the Git version and reading the installed helper documentation. These are ordinary, read-only commands:
$ git --version
git version 2.43.0
$ man git-remote-fd
The helper is intended for programs and scripts calling Git. It is not a replacement for an ordinary repository URL such as an SSH or HTTPS remote. Its URL begins with fd:: and names an already-open descriptor.
Checkpoint: if your design does not already have a socket or a pair of pipes connected to the remote Git service, stop here. git-remote-fd cannot discover or open the service for you.
2. Choose the descriptor form
Use one descriptor when it is a bidirectional socket. The number is the file descriptor as seen by the Git process:
fd::17
Use two descriptors when the connection is represented by separate pipes. The first is the inbound pipe that Git reads, and the second is the outbound pipe that Git writes:
fd::7,8
Do not swap the pair merely because the numbers look more convenient. With the two-descriptor form, infd is for data coming into Git and outfd is for data Git sends to the remote service.
These descriptors must be inherited by the Git process or passed to it using your supervisor's normal descriptor-passing mechanism. The URL does not open descriptor 17, 7 or 8, and it does not make a closed descriptor valid.
3. Account for the completed handshake
The helper assumes that transport handshaking has already happened before Git starts using it. For a Git protocol that needs a service request, the supervising program must have performed that exchange first. Starting Git with an unprepared stream can look like a Git protocol failure even when the descriptor numbers are correct.
Keep the roles separate:
- The supervisor opens the socket or pipes and performs any required preliminary exchange.
- Git consumes and produces the smart transport stream through the descriptors.
git-remote-fdreflects that stream; it does not negotiate a new connection.
Checkpoint: before invoking Git, verify in the supervisor that each descriptor is open, connected to the intended service, and positioned at the point where Git should begin its exchange. Do this in the supervisor's own diagnostics, because the helper has no URL-based fallback.
4. Use a descriptor in a Git operation
For a fetch, pass the descriptor URL where Git expects a remote URL and then name the refspec or branch:
$ git fetch fd::17 master
This tells Git to fetch master through the bidirectional socket on descriptor 17. The command will only work when the Git process really has descriptor 17 and the peer is speaking the expected Git service protocol.
For a push using separate pipes, provide both numbers:
$ git push fd::7,8 master
Here Git reads responses from descriptor 7 and writes requests to descriptor 8. The local repository and the remote service still decide whether the requested ref update is accepted. A successful descriptor setup does not grant permission to update a remote branch.
The same URL form can be used where Git accepts a remote URL for another operation, including git archive. Use the descriptor arrangement required by the operation and its remote Git service.
5. Add a display hint without changing the connection
You may append a slash and any string to make a descriptor URL clearer when a command or log displays it:
$ git fetch fd::17/build-cache master
$ git push fd::7,8/release master
The text after the slash is ignored by the helper. It is a label for people, not a path, repository name or extra protocol argument. Changing /build-cache to another label therefore does not select a different service.
Do not put secrets in this hint. A displayed URL may be copied into logs, error reports or process diagnostics.
6. Turn on focused diagnostics
When the stream does not behave as expected, set GIT_TRANSLOOP_DEBUG for the Git command:
$ GIT_TRANSLOOP_DEBUG=1 git fetch fd::17/build-cache master
The helper prints debugging information about reads and writes. Exact lines depend on what the peer sends, so treat this as a trace rather than a stable output format. Keep the trace private: transport data and protocol details can reveal repository or deployment information.
If the trace shows no useful exchange, check the descriptor inheritance and the completed handshake first. If it shows reads and writes followed by a Git error, investigate the remote service, requested ref and protocol data rather than changing descriptor numbers at random.
7. Recover from the common mistakes
A few failures are easy to misread:
- Bad descriptor: Git cannot use a number that was closed, not inherited, or allocated differently in the child process. Log the descriptor table at the point where Git is launched.
- Reversed pipes: if Git reads from the pipe it should write, the operation can block or fail with protocol noise. Recheck the supervisor's read and write ends.
- Handshake repeated or omitted: the helper expects the preliminary work to be complete. Coordinate ownership of the first bytes on the stream.
- Label treated as configuration: text after the slash is ignored. It cannot repair a wrong descriptor or choose a repository.
- Debugging treated as success: a trace proves activity, not a successful fetch or push. Check Git's exit status and the operation's result.
There is no undo command for these examples because the helper itself changes no persistent configuration. If a push did update a remote ref, recover it using your normal Git branch and remote recovery procedure; closing the descriptors does not undo a completed ref update.
Done means
- You know whether the connection is one bidirectional socket or two directional pipes.
- The descriptors are open and inherited by the Git process.
- Any required transport handshake is complete before Git starts.
- Your URL uses
fd::infdorfd::infd,outfd, with an optional display hint only. - A fetch, push or archive command returns the result you expected, with
GIT_TRANSLOOP_DEBUGavailable for a private diagnostic trace.