git: Skip directories in git status output (#23300)

Cole Miller created

The output of `git status --porcelain=v1` includes untracked
directories, i.e. directories that have no tracked files beneath. Since
we have our own way of computing a "summary" status for each directory
in a repo, this is not helpful for Zed; and it interferes with our
handling of nested repos. So just skip these lines in the output.

Closes #23133 

Release Notes:

- Fix project panel colors when one git repository is nested beneath
another

Change summary

crates/git/src/status.rs | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)

Detailed changes

crates/git/src/status.rs 🔗

@@ -421,15 +421,15 @@ impl GitStatus {
             .stdout(Stdio::piped())
             .stderr(Stdio::piped())
             .spawn()
-            .map_err(|e| anyhow!("Failed to start git status process: {}", e))?;
+            .map_err(|e| anyhow!("Failed to start git status process: {e}"))?;
 
         let output = child
             .wait_with_output()
-            .map_err(|e| anyhow!("Failed to read git blame output: {}", e))?;
+            .map_err(|e| anyhow!("Failed to read git status output: {e}"))?;
 
         if !output.status.success() {
             let stderr = String::from_utf8_lossy(&output.stderr);
-            return Err(anyhow!("git status process failed: {}", stderr));
+            return Err(anyhow!("git status process failed: {stderr}"));
         }
         let stdout = String::from_utf8_lossy(&output.stdout);
         let mut entries = stdout
@@ -440,6 +440,13 @@ impl GitStatus {
                     return None;
                 };
                 let path = &entry[3..];
+                // The git status output includes untracked directories as well as untracked files.
+                // We do our own processing to compute the "summary" status of each directory,
+                // so just skip any directories in the output, since they'll otherwise interfere
+                // with our handling of nested repositories.
+                if path.ends_with('/') {
+                    return None;
+                }
                 let status = entry[0..2].as_bytes().try_into().unwrap();
                 let status = FileStatus::from_bytes(status).log_err()?;
                 let path = RepoPath(Path::new(path).into());