Skip to content

Commit 207af04

Browse files
committed
fix(cli): skip deleted files in --changed and --staged
1 parent a392c06 commit 207af04

File tree

5 files changed

+118
-2
lines changed

5 files changed

+118
-2
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fix `--changed` and `--staged` flags throwing "No such file or directory" error when a file has been deleted or renamed in the working directory. The CLI now filters out files that no longer exist before processing.

crates/biome_cli/src/changed.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::CliDiagnostic;
22
use biome_configuration::Configuration;
33
use biome_fs::FileSystem;
4+
use camino::Utf8Path;
45
use std::ffi::OsString;
56

67
pub(crate) fn get_changed_files(
@@ -26,15 +27,25 @@ pub(crate) fn get_changed_files(
2627

2728
let changed_files = fs.get_changed_files(base)?;
2829

29-
let filtered_changed_files = changed_files.iter().map(OsString::from).collect::<Vec<_>>();
30+
// Filter out files that no longer exist (e.g., deleted or renamed in the working directory)
31+
let filtered_changed_files = changed_files
32+
.iter()
33+
.filter(|file| fs.path_is_file(Utf8Path::new(file)))
34+
.map(OsString::from)
35+
.collect::<Vec<_>>();
3036

3137
Ok(filtered_changed_files)
3238
}
3339

3440
pub(crate) fn get_staged_files(fs: &dyn FileSystem) -> Result<Vec<OsString>, CliDiagnostic> {
3541
let staged_files = fs.get_staged_files()?;
3642

37-
let filtered_staged_files = staged_files.iter().map(OsString::from).collect::<Vec<_>>();
43+
// Filter out files that no longer exist (e.g., deleted or renamed in the working directory)
44+
let filtered_staged_files = staged_files
45+
.iter()
46+
.filter(|file| fs.path_is_file(Utf8Path::new(file)))
47+
.map(OsString::from)
48+
.collect::<Vec<_>>();
3849

3950
Ok(filtered_staged_files)
4051
}

crates/biome_cli/tests/commands/lint.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3665,6 +3665,76 @@ fn should_error_if_unchanged_files_only_with_changed_flag() {
36653665
));
36663666
}
36673667

3668+
#[test]
3669+
fn should_skip_nonexistent_changed_files() {
3670+
let mut console = BufferConsole::default();
3671+
let mut fs = MemoryFileSystem::default();
3672+
3673+
// Git reports "deleted.js" as changed, but it doesn't exist in the working directory
3674+
fs.set_on_get_changed_files(Box::new(|| {
3675+
vec![String::from("exists.js"), String::from("deleted.js")]
3676+
}));
3677+
3678+
// Only "exists.js" exists in the file system
3679+
fs.insert(
3680+
Utf8Path::new("exists.js").into(),
3681+
r#"console.log('exists');"#.as_bytes(),
3682+
);
3683+
// Note: "deleted.js" is NOT inserted - simulating a deleted file
3684+
3685+
let (fs, result) = run_cli(
3686+
fs,
3687+
&mut console,
3688+
Args::from(["lint", "--changed", "--since=main"].as_slice()),
3689+
);
3690+
3691+
// Should succeed without "No such file or directory" error
3692+
assert!(result.is_ok(), "run_cli returned {result:?}");
3693+
3694+
assert_cli_snapshot(SnapshotPayload::new(
3695+
module_path!(),
3696+
"should_skip_nonexistent_changed_files",
3697+
fs,
3698+
console,
3699+
result,
3700+
));
3701+
}
3702+
3703+
#[test]
3704+
fn should_skip_nonexistent_staged_files() {
3705+
let mut console = BufferConsole::default();
3706+
let mut fs = MemoryFileSystem::default();
3707+
3708+
// Git reports "deleted.js" as staged, but it doesn't exist in the working directory
3709+
fs.set_on_get_staged_files(Box::new(|| {
3710+
vec![String::from("exists.js"), String::from("deleted.js")]
3711+
}));
3712+
3713+
// Only "exists.js" exists in the file system
3714+
fs.insert(
3715+
Utf8Path::new("exists.js").into(),
3716+
r#"console.log('exists');"#.as_bytes(),
3717+
);
3718+
// Note: "deleted.js" is NOT inserted - simulating a deleted file
3719+
3720+
let (fs, result) = run_cli(
3721+
fs,
3722+
&mut console,
3723+
Args::from(["lint", "--staged"].as_slice()),
3724+
);
3725+
3726+
// Should succeed without "No such file or directory" error
3727+
assert!(result.is_ok(), "run_cli returned {result:?}");
3728+
3729+
assert_cli_snapshot(SnapshotPayload::new(
3730+
module_path!(),
3731+
"should_skip_nonexistent_staged_files",
3732+
fs,
3733+
console,
3734+
result,
3735+
));
3736+
}
3737+
36683738
#[test]
36693739
fn linter_shows_the_default_severity_of_rule_on() {
36703740
let mut console = BufferConsole::default();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
source: crates/biome_cli/tests/snap_test.rs
3+
expression: redactor(content)
4+
---
5+
## `exists.js`
6+
7+
```js
8+
console.log('exists');
9+
```
10+
11+
# Emitted Messages
12+
13+
```block
14+
Checked 1 file in <TIME>. No fixes applied.
15+
```
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
source: crates/biome_cli/tests/snap_test.rs
3+
expression: redactor(content)
4+
---
5+
## `exists.js`
6+
7+
```js
8+
console.log('exists');
9+
```
10+
11+
# Emitted Messages
12+
13+
```block
14+
Checked 1 file in <TIME>. No fixes applied.
15+
```

0 commit comments

Comments
 (0)