From aba57fb9d2e335d1a77bdf40bbf7f896f1cd7cb9 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Mon, 31 Aug 2026 21:35:54 +0800 Subject: [PATCH 1/2] fix(worktree): fetch via system git instead of libgit2 Found by live acceptance: PR worktree provisioning failed with libgit2 'no TLS stream available' on a machine behind a local proxy, and libgit2 fetch has no credential-helper integration for private repositories. The network fetch now shells out to the configured tools.git (honors credential helpers and proxies); libgit2 keeps the local operations (ref lookup, worktree add). --- CHANGELOG.md | 10 ++++++++++ src/group/pr_agent.rs | 1 + src/worktree.rs | 26 ++++++++++++++++++++------ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 277d05d..5748008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to Braid are recorded here. The project follows Semantic Versioning once release artifacts are published. +## [0.3.1] - unreleased + +### Fixed + +- PR worktree provisioning fetched through libgit2, which ignores the + operator's credential helpers and proxy configuration and failed on real + networks ("no TLS stream available"). The fetch now uses the configured + system `git` executable; libgit2 remains for local reference/worktree + operations. + ## [0.3.0] - 2026-08-31 ### Added diff --git a/src/group/pr_agent.rs b/src/group/pr_agent.rs index e0d1ad9..369e628 100644 --- a/src/group/pr_agent.rs +++ b/src/group/pr_agent.rs @@ -440,6 +440,7 @@ pub(crate) fn provision_pr_agent_worktree( target: &target, repository: &config.github.repository, remote: "origin", + git: &config.tools.git, head_ref: &prepared.head_ref, local_branch: &local_branch, })?; diff --git a/src/worktree.rs b/src/worktree.rs index af95243..04e72ea 100644 --- a/src/worktree.rs +++ b/src/worktree.rs @@ -27,6 +27,10 @@ pub struct WorktreeRequest<'a> { pub target: &'a Path, pub repository: &'a str, pub remote: &'a str, + /// System `git` executable used for the network fetch: it honors the + /// operator's credential helpers and proxy configuration, which libgit2 + /// does not. + pub git: &'a Path, pub head_ref: &'a str, pub local_branch: &'a str, } @@ -62,13 +66,23 @@ pub fn provision(request: &WorktreeRequest<'_>) -> Result Date: Mon, 31 Aug 2026 21:45:27 +0800 Subject: [PATCH 2/2] fix(worktree): add worktree via system git as well libgit2's worktree add rejects remote-tracking references ('reference is not a branch'); the system git creates the generation-scoped local branch and the worktree in one step (-B for idempotent retries). git2 remains for local inspection only. --- src/worktree.rs | 76 ++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/worktree.rs b/src/worktree.rs index 04e72ea..3b8cff3 100644 --- a/src/worktree.rs +++ b/src/worktree.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use git2::{ErrorClass, ErrorCode, Repository, WorktreeAddOptions}; +use git2::{ErrorClass, ErrorCode, Repository}; use thiserror::Error; #[derive(Debug, Error)] @@ -66,41 +66,45 @@ pub fn provision(request: &WorktreeRequest<'_>) -> Result Result<(), WorktreeError> { + let output = std::process::Command::new(request.git) + .arg("-C") + .arg(&source) + .args(args) + .output() + .map_err(|source_err| WorktreeError::Io { + path: source.clone(), + source: source_err, + })?; + if !output.status.success() { + return Err(WorktreeError::Git(format!( + "git {} failed: {}", + args.first().unwrap_or(&""), + String::from_utf8_lossy(&output.stderr).trim() + ))); + } + Ok(()) + }; + let remote_ref = format!("refs/remotes/{}/{}", request.remote, request.head_ref); + git(&[ + "fetch", + request.remote, + &format!("+refs/heads/{0}:{1}", request.head_ref, remote_ref), + ])?; + // libgit2's worktree add rejects remote-tracking references + // ("reference is not a branch"); the system git creates the + // generation-scoped local branch and the worktree in one step. + git(&[ + "worktree", + "add", + request + .target + .to_str() + .ok_or_else(|| WorktreeError::Git("worktree target path is not UTF-8".into()))?, + "-B", + request.local_branch, + &remote_ref, + ])?; Ok::<(), WorktreeError>(()) })?; verify_existing(request, &source)