Debug windows CI failure

Richard Feldman created

Change summary

crates/git/src/repository.rs | 97 +++++++++++++++++++++++++++++++++----
1 file changed, 85 insertions(+), 12 deletions(-)

Detailed changes

crates/git/src/repository.rs 🔗

@@ -1722,16 +1722,28 @@ impl GitBinary {
     async fn resolve_git_dir(&self) -> Result<PathBuf> {
         let dot_git = self.working_directory.join(".git");
 
+        eprintln!(
+            "resolve_git_dir: working_directory = {:?}",
+            self.working_directory
+        );
+        eprintln!("resolve_git_dir: dot_git = {:?}", dot_git);
+        eprintln!("resolve_git_dir: dot_git.exists() = {}", dot_git.exists());
+        eprintln!("resolve_git_dir: dot_git.is_dir() = {}", dot_git.is_dir());
+        eprintln!("resolve_git_dir: dot_git.is_file() = {}", dot_git.is_file());
+
         if dot_git.is_dir() {
             // Regular repository - .git is a directory
+            eprintln!("resolve_git_dir: Regular repo, returning {:?}", dot_git);
             Ok(dot_git)
         } else if dot_git.is_file() {
             // Worktree - .git is a file containing the path to the actual git directory
             let contents = smol::fs::read_to_string(&dot_git).await?;
+            eprintln!("resolve_git_dir: .git file contents: {:?}", contents);
 
             // The file contains a line like: "gitdir: /path/to/actual/.git/worktrees/name"
             if let Some(gitdir_line) = contents.lines().find(|line| line.starts_with("gitdir:")) {
                 let gitdir_path = gitdir_line.trim_start_matches("gitdir:").trim();
+                eprintln!("resolve_git_dir: gitdir_path from file: {:?}", gitdir_path);
 
                 // The path may be relative or absolute
                 let git_dir = if Path::new(gitdir_path).is_absolute() {
@@ -1745,8 +1757,17 @@ impl GitBinary {
                         .join(gitdir_path)
                 };
 
+                eprintln!(
+                    "resolve_git_dir: git_dir before canonicalize: {:?}",
+                    git_dir
+                );
+
                 // Canonicalize the path to resolve any .. or . components
                 let git_dir = smol::fs::canonicalize(&git_dir).await.map_err(|e| {
+                    eprintln!(
+                        "resolve_git_dir: Failed to canonicalize {:?}: {}",
+                        git_dir, e
+                    );
                     anyhow!(
                         "Failed to canonicalize git directory path {:?}: {}",
                         git_dir,
@@ -1754,7 +1775,11 @@ impl GitBinary {
                     )
                 })?;
 
+                eprintln!("resolve_git_dir: git_dir after canonicalize: {:?}", git_dir);
+                eprintln!("resolve_git_dir: git_dir.exists() = {}", git_dir.exists());
+
                 if git_dir.exists() {
+                    eprintln!("resolve_git_dir: Returning worktree git_dir: {:?}", git_dir);
                     Ok(git_dir)
                 } else {
                     Err(anyhow!(
@@ -1811,6 +1836,7 @@ impl GitBinary {
         F: for<'a> FnOnce(&'a Self) -> BoxFuture<'a, Result<R>>,
     {
         let index_file_path = self.path_for_index_id(Uuid::new_v4()).await?;
+        eprintln!("with_temp_index: index_file_path = {:?}", index_file_path);
 
         let delete_temp_index = util::defer({
             let index_file_path = index_file_path.clone();
@@ -1824,12 +1850,18 @@ impl GitBinary {
             }
         });
 
+        // Ensure the parent directory exists for the temp index file
+        if let Some(parent) = index_file_path.parent() {
+            smol::fs::create_dir_all(parent).await?;
+        }
+
         // Copy the default index file so that Git doesn't have to rebuild the
-        // whole index from scratch. This might fail if this is an empty repository.
+        // whole index from scratch. This might fail if this is an empty repository
+        // or a worktree (where the index is not in the worktree's git dir).
+        // We just ignore the error and let git create a fresh index if needed.
         let git_dir = self.resolve_git_dir().await?;
-        smol::fs::copy(git_dir.join("index"), &index_file_path)
-            .await
-            .ok();
+        let index_source = git_dir.join("index");
+        smol::fs::copy(&index_source, &index_file_path).await.ok();
 
         self.index_file_path = Some(index_file_path.clone());
         let result = f(self).await;
@@ -1869,15 +1901,32 @@ impl GitBinary {
     where
         S: AsRef<OsStr>,
     {
-        let mut command = self.build_command(args);
+        let args_vec: Vec<_> = args
+            .into_iter()
+            .map(|s| s.as_ref().to_string_lossy().to_string())
+            .collect();
+        let mut command = self.build_command(args_vec.iter().map(|s| s.as_str()));
         let output = command.output().await?;
-        anyhow::ensure!(
-            output.status.success(),
-            GitBinaryCommandError {
-                stdout: String::from_utf8_lossy(&output.stdout).to_string(),
-                status: output.status,
-            }
-        );
+        if !output.status.success() {
+            let working_dir = self.working_directory.display().to_string();
+            let stdout = String::from_utf8_lossy(&output.stdout).to_string();
+            let stderr = String::from_utf8_lossy(&output.stderr).to_string();
+            let git_binary = self.git_binary_path.display().to_string();
+            let index_file = self
+                .index_file_path
+                .as_ref()
+                .map(|p| p.display().to_string());
+            return Err(GitBinaryCommandError::new(
+                &git_binary,
+                &working_dir,
+                &args_vec.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
+                stdout,
+                stderr,
+                output.status,
+                index_file,
+            )
+            .into());
+        }
         Ok(String::from_utf8(output.stdout)?)
     }
 
@@ -1903,6 +1952,30 @@ struct GitBinaryCommandError {
     status: ExitStatus,
 }
 
+impl GitBinaryCommandError {
+    fn new(
+        git_binary: &str,
+        working_dir: &str,
+        args: &[&str],
+        stdout: String,
+        stderr: String,
+        status: ExitStatus,
+        index_file: Option<String>,
+    ) -> Self {
+        eprintln!("Git command failed:");
+        eprintln!("  Binary: {}", git_binary);
+        eprintln!("  Command: {} {}", git_binary, args.join(" "));
+        eprintln!("  Working dir: {}", working_dir);
+        if let Some(index) = &index_file {
+            eprintln!("  GIT_INDEX_FILE: {}", index);
+        }
+        eprintln!("  Exit status: {:?}", status);
+        eprintln!("  Stdout: {}", stdout);
+        eprintln!("  Stderr: {}", stderr);
+        Self { stdout, status }
+    }
+}
+
 async fn run_git_command(
     env: Arc<HashMap<String, String>>,
     ask_pass: AskPassDelegate,