From c5219e8fd2dfdc1655306bce524d0aeb345b5259 Mon Sep 17 00:00:00 2001 From: Oleksiy Syvokon Date: Wed, 24 Sep 2025 13:58:39 +0300 Subject: [PATCH] agent: Clean up git exclusions after emergency (#38775) In some rare cases, the auto-generated block gets stuck in `.git/info/exclude`. We now auto-clean it. Closes #38374 Release Notes: - Remove auto-generated block from git excludes if it gets stuck there. --- crates/git/src/repository.rs | 46 +++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 29e2dab240e83da8d4343a370970ec0cc2256601..88bfc41dfee4184a733e733eea77817c32f796e0 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -242,8 +242,20 @@ pub struct GitExcludeOverride { } impl GitExcludeOverride { + const START_BLOCK_MARKER: &str = "\n\n# ====== Auto-added by Zed: =======\n"; + const END_BLOCK_MARKER: &str = "\n# ====== End of auto-added by Zed =======\n"; + pub async fn new(git_exclude_path: PathBuf) -> Result { - let original_excludes = smol::fs::read_to_string(&git_exclude_path).await.ok(); + let original_excludes = + smol::fs::read_to_string(&git_exclude_path) + .await + .ok() + .map(|content| { + // Auto-generated lines are normally cleaned up in + // `restore_original()` or `drop()`, but may stuck in rare cases. + // Make sure to remove them. + Self::remove_auto_generated_block(&content) + }); Ok(GitExcludeOverride { git_exclude_path, @@ -260,9 +272,10 @@ impl GitExcludeOverride { }); let mut content = self.original_excludes.clone().unwrap_or_default(); - content.push_str("\n\n# ====== Auto-added by Zed: =======\n"); + + content.push_str(Self::START_BLOCK_MARKER); content.push_str(self.added_excludes.as_ref().unwrap()); - content.push('\n'); + content.push_str(Self::END_BLOCK_MARKER); smol::fs::write(&self.git_exclude_path, content).await?; Ok(()) @@ -279,6 +292,33 @@ impl GitExcludeOverride { Ok(()) } + + fn remove_auto_generated_block(content: &str) -> String { + let start_marker = Self::START_BLOCK_MARKER; + let end_marker = Self::END_BLOCK_MARKER; + let mut content = content.to_string(); + + let start_index = content.find(start_marker); + let end_index = content.rfind(end_marker); + + if let (Some(start), Some(end)) = (start_index, end_index) { + if end > start { + content.replace_range(start..end + end_marker.len(), ""); + } + } + + // Older versions of Zed didn't have end-of-block markers, + // so it's impossible to determine auto-generated lines. + // Conservatively remove the standard list of excludes + let standard_excludes = format!( + "{}{}", + Self::START_BLOCK_MARKER, + include_str!("./checkpoint.gitignore") + ); + content = content.replace(&standard_excludes, ""); + + content + } } impl Drop for GitExcludeOverride {