From 457e296d90e2f7c2808e216f2ef0608b70f76553 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Wed, 7 Jan 2026 21:35:56 +0000 Subject: Add Git protocol v2 support to fix modern git client compatibility Modern git clients (2.51.0+) default to protocol v2 and send the Git-Protocol header. The server must pass this to git processes via the GIT_PROTOCOL environment variable for proper negotiation. Changes: - Extract Git-Protocol header in HTTP layer (src/http/mod.rs) - Pass git_protocol parameter through all handler functions - Set GIT_PROTOCOL env var when spawning git subprocesses - Update all tests to pass None for backward compatibility This fixes hangs/timeouts when modern git clients connect to the server. Fixes issue discovered in work/2025-01-07-pr-clone-tag-sync-investigation.md --- src/git/subprocess.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/git/subprocess.rs') diff --git a/src/git/subprocess.rs b/src/git/subprocess.rs index 2d9a981..acee726 100644 --- a/src/git/subprocess.rs +++ b/src/git/subprocess.rs @@ -22,10 +22,12 @@ impl GitSubprocess { /// * `service` - The Git service (upload-pack or receive-pack) /// * `repo_path` - Path to the bare Git repository /// * `advertise` - If true, run with --advertise-refs flag + /// * `git_protocol` - Optional Git protocol version (e.g., "version=2") pub fn spawn( service: GitService, repo_path: impl AsRef, advertise: bool, + git_protocol: Option<&str>, ) -> std::io::Result { let repo_path = repo_path.as_ref(); @@ -52,6 +54,12 @@ impl GitSubprocess { cmd.stdout(Stdio::piped()); cmd.stderr(Stdio::piped()); + // Set GIT_PROTOCOL environment variable if provided + // This enables Git protocol v2 support for modern git clients + if let Some(protocol) = git_protocol { + cmd.env("GIT_PROTOCOL", protocol); + } + let child = cmd.spawn()?; Ok(Self { child }) @@ -118,7 +126,7 @@ mod tests { #[tokio::test] async fn test_spawn_upload_pack_advertise() { let repo = create_bare_repo(); - let mut proc = GitSubprocess::spawn(GitService::UploadPack, repo.path(), true) + let mut proc = GitSubprocess::spawn(GitService::UploadPack, repo.path(), true, None) .expect("Failed to spawn git"); // Should have spawned successfully @@ -132,7 +140,7 @@ mod tests { #[tokio::test] async fn test_spawn_receive_pack() { let repo = create_bare_repo(); - let mut proc = GitSubprocess::spawn(GitService::ReceivePack, repo.path(), false) + let mut proc = GitSubprocess::spawn(GitService::ReceivePack, repo.path(), false, None) .expect("Failed to spawn git"); assert!(proc.stdout().is_some()); -- cgit v1.2.3