Allow */foo filters to match foo (compat)

This is buggy behaviour that exists in <=1.17, and this patch restores it*

Per gitignore rules, this glob:

    */somefile

Will match:

    foo/somefile
    bar/somefile

But not:

    somefile

However, so far we’ve accepted this. As this is a breaking change, this patch
introduces a hack which lets us accept this behaviour, but clearly marks it as
a compatibility exception. In 2.0, this will be gone.

Fixes #258

* It only restores it on unices, because the behaviour cannot be restored in
  any reasonable manner on Windows. Ah well. It's not for long anyway.
This commit is contained in:
Félix Saparelli 2022-02-22 02:30:38 +13:00
parent 03aef187bd
commit 72100217e8
2 changed files with 30 additions and 3 deletions

View File

@ -1,7 +1,7 @@
//! A simple filterer in the style of the watchexec v1 filter.
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use ignore::gitignore::{Gitignore, GitignoreBuilder};
use tracing::{debug, trace, trace_span};
@ -19,6 +19,7 @@ use crate::ignore::{IgnoreFile, IgnoreFilterer};
/// have an updatable configuration.
#[derive(Debug)]
pub struct GlobsetFilterer {
origin: PathBuf,
filters: Gitignore,
ignores: Gitignore,
ignore_files: IgnoreFilterer,
@ -74,6 +75,7 @@ impl GlobsetFilterer {
ignore_files.finish();
debug!(
?origin,
num_filters=%filters.num_ignores(),
num_neg_filters=%filters.num_whitelists(),
num_ignores=%ignores.num_ignores(),
@ -83,6 +85,7 @@ impl GlobsetFilterer {
"globset filterer built");
Ok(Self {
origin: origin.into(),
filters,
ignores,
ignore_files,
@ -135,6 +138,24 @@ impl Filterer for GlobsetFilterer {
trace!("allowed by globset filters");
return true;
}
// Watchexec 1.x bug, TODO remove at 2.0
#[cfg(unix)]
if let Ok(based) = path.strip_prefix(&self.origin) {
let rebased = {
let mut b = self.origin.clone().into_os_string();
b.push(PathBuf::from(String::from(MAIN_SEPARATOR)));
b.push(PathBuf::from(String::from(MAIN_SEPARATOR)));
b.push(based.as_os_str());
b
};
trace!(?rebased, "testing on rebased path, 1.x bug compat (#258)");
if self.filters.matched(rebased, is_dir).is_ignore() {
trace!("allowed by globset filters, 1.x bug compat (#258)");
return true;
}
}
}
if !self.extensions.is_empty() {

View File

@ -351,11 +351,14 @@ async fn multipath_allow_on_any_one_pass() {
async fn extensions_and_filters_glob() {
let filterer = filt(&["*/justfile"], &[], &["md", "css"]).await;
filterer.file_does_pass("justfile");
filterer.file_does_pass("foo/justfile");
filterer.file_does_pass("bar.md");
filterer.file_does_pass("qux.css");
filterer.file_doesnt_pass("nope.py");
// Watchexec 1.x buggy behaviour, should not pass
#[cfg(unix)]
filterer.file_does_pass("justfile");
}
#[tokio::test]
@ -372,10 +375,13 @@ async fn extensions_and_filters_slash() {
async fn leading_single_glob_file() {
let filterer = filt(&["*/justfile"], &[], &[]).await;
filterer.file_does_pass("justfile");
filterer.file_does_pass("foo/justfile");
filterer.file_doesnt_pass("notfile");
filterer.file_doesnt_pass("not/thisfile");
// Watchexec 1.x buggy behaviour, should not pass
#[cfg(unix)]
filterer.file_does_pass("justfile");
}
#[tokio::test]