mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-12-27 02:12:53 +00:00
Ignore files matched in .gitignore
This doesn’t *completely* work: it seems to have trouble with ignored paths beginning with slashes, possibly amongst others. Also, .gitignore scanning could be made more efficient.
This commit is contained in:
parent
b95446d834
commit
827aa8bfc3
@ -230,7 +230,7 @@ impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
|
|||||||
details::Render { dir, files, colours, style, opts, filter: &self.options.filter, recurse: self.options.dir_action.recurse_options() }.render(self.git.as_ref(), self.ignore.as_ref(), self.writer)
|
details::Render { dir, files, colours, style, opts, filter: &self.options.filter, recurse: self.options.dir_action.recurse_options() }.render(self.git.as_ref(), self.ignore.as_ref(), self.writer)
|
||||||
}
|
}
|
||||||
Mode::GridDetails(ref opts) => {
|
Mode::GridDetails(ref opts) => {
|
||||||
grid_details::Render { dir, files, colours, style, grid: &opts.grid, details: &opts.details, filter: &self.options.filter, row_threshold: opts.row_threshold }.render(self.git.as_ref(), self.ignore.as_ref(), self.writer)
|
grid_details::Render { dir, files, colours, style, grid: &opts.grid, details: &opts.details, filter: &self.options.filter, row_threshold: opts.row_threshold }.render(self.git.as_ref(), self.writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,8 @@ impl Dir {
|
|||||||
/// Produce an iterator of IO results of trying to read all the files in
|
/// Produce an iterator of IO results of trying to read all the files in
|
||||||
/// this directory.
|
/// this directory.
|
||||||
pub fn files<'dir, 'ig>(&'dir self, dots: DotFilter, ignore: Option<&'ig IgnoreCache>) -> Files<'dir, 'ig> {
|
pub fn files<'dir, 'ig>(&'dir self, dots: DotFilter, ignore: Option<&'ig IgnoreCache>) -> Files<'dir, 'ig> {
|
||||||
|
if let Some(i) = ignore { i.discover_underneath(&self.path); }
|
||||||
|
|
||||||
Files {
|
Files {
|
||||||
inner: self.contents.iter(),
|
inner: self.contents.iter(),
|
||||||
dir: self,
|
dir: self,
|
||||||
@ -103,6 +105,10 @@ impl<'dir, 'ig> Files<'dir, 'ig> {
|
|||||||
let filename = File::filename(path);
|
let filename = File::filename(path);
|
||||||
if !self.dotfiles && filename.starts_with(".") { continue }
|
if !self.dotfiles && filename.starts_with(".") { continue }
|
||||||
|
|
||||||
|
if let Some(i) = self.ignore {
|
||||||
|
if i.is_ignored(path) { continue }
|
||||||
|
}
|
||||||
|
|
||||||
return Some(File::new(path.clone(), self.dir, filename)
|
return Some(File::new(path.clone(), self.dir, filename)
|
||||||
.map_err(|e| (path.clone(), e)))
|
.map_err(|e| (path.clone(), e)))
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,17 @@
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use fs::filter::IgnorePatterns;
|
use fs::filter::IgnorePatterns;
|
||||||
|
|
||||||
|
|
||||||
/// An **ignore cache** holds sets of glob patterns paired with the
|
/// An **ignore cache** holds sets of glob patterns paired with the
|
||||||
/// directories that they should be ignored underneath.
|
/// directories that they should be ignored underneath. Believe it or not,
|
||||||
|
/// that’s a valid English sentence.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct IgnoreCache {
|
pub struct IgnoreCache {
|
||||||
entries: Vec<(PathBuf, IgnorePatterns)>
|
entries: RwLock<Vec<(PathBuf, IgnorePatterns)>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IgnoreCache {
|
impl IgnoreCache {
|
||||||
@ -23,27 +25,35 @@ impl IgnoreCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_results)] // don’t do this
|
#[allow(unused_results)] // don’t do this
|
||||||
pub fn discover_underneath(&mut self, path: &Path) {
|
pub fn discover_underneath(&self, path: &Path) {
|
||||||
let mut path = Some(path);
|
let mut path = Some(path);
|
||||||
|
let mut entries = self.entries.write().unwrap();
|
||||||
|
|
||||||
while let Some(p) = path {
|
while let Some(p) = path {
|
||||||
|
if p.components().next().is_none() { break }
|
||||||
|
|
||||||
let ignore_file = p.join(".gitignore");
|
let ignore_file = p.join(".gitignore");
|
||||||
if ignore_file.is_file() {
|
if ignore_file.is_file() {
|
||||||
|
debug!("Found a .gitignore file: {:?}", ignore_file);
|
||||||
if let Ok(mut file) = File::open(ignore_file) {
|
if let Ok(mut file) = File::open(ignore_file) {
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
file.read_to_string(&mut contents).expect("Reading gitignore failed");
|
file.read_to_string(&mut contents).expect("Reading gitignore failed");
|
||||||
|
|
||||||
let (patterns, mut _errors) = IgnorePatterns::parse_from_iter(contents.lines());
|
let (patterns, mut _errors) = IgnorePatterns::parse_from_iter(contents.lines());
|
||||||
self.entries.push((p.into(), patterns));
|
entries.push((p.into(), patterns));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
debug!("Found no .gitignore file at {:?}", ignore_file);
|
||||||
|
}
|
||||||
|
|
||||||
path = p.parent();
|
path = p.parent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_ignored(&self, suspect: &Path) -> bool {
|
pub fn is_ignored(&self, suspect: &Path) -> bool {
|
||||||
self.entries.iter().any(|&(ref base_path, ref patterns)| {
|
let entries = self.entries.read().unwrap();
|
||||||
|
entries.iter().any(|&(ref base_path, ref patterns)| {
|
||||||
if let Ok(suffix) = suspect.strip_prefix(&base_path) {
|
if let Ok(suffix) = suspect.strip_prefix(&base_path) {
|
||||||
patterns.is_ignored_path(suffix)
|
patterns.is_ignored_path(suffix)
|
||||||
}
|
}
|
||||||
@ -55,7 +65,6 @@ impl IgnoreCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -6,7 +6,6 @@ use ansi_term::ANSIStrings;
|
|||||||
use term_grid as grid;
|
use term_grid as grid;
|
||||||
|
|
||||||
use fs::{Dir, File};
|
use fs::{Dir, File};
|
||||||
use fs::feature::ignore::IgnoreCache;
|
|
||||||
use fs::feature::git::GitCache;
|
use fs::feature::git::GitCache;
|
||||||
use fs::feature::xattr::FileAttributes;
|
use fs::feature::xattr::FileAttributes;
|
||||||
use fs::filter::FileFilter;
|
use fs::filter::FileFilter;
|
||||||
@ -112,16 +111,19 @@ impl<'a> Render<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render<W: Write>(self, git: Option<&GitCache>, ignore: Option<&'a IgnoreCache>, w: &mut W) -> IOResult<()> {
|
// This doesn’t take an IgnoreCache even though the details one does
|
||||||
if let Some((grid, width)) = self.find_fitting_grid(git, ignore) {
|
// because grid-details has no tree view.
|
||||||
|
|
||||||
|
pub fn render<W: Write>(self, git: Option<&GitCache>, w: &mut W) -> IOResult<()> {
|
||||||
|
if let Some((grid, width)) = self.find_fitting_grid(git) {
|
||||||
write!(w, "{}", grid.fit_into_columns(width))
|
write!(w, "{}", grid.fit_into_columns(width))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.give_up().render(git, ignore, w)
|
self.give_up().render(git, None, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_fitting_grid(&self, git: Option<&GitCache>, ignore: Option<&'a IgnoreCache>) -> Option<(grid::Grid, grid::Width)> {
|
pub fn find_fitting_grid(&self, git: Option<&GitCache>) -> Option<(grid::Grid, grid::Width)> {
|
||||||
let options = self.details.table.as_ref().expect("Details table options not given!");
|
let options = self.details.table.as_ref().expect("Details table options not given!");
|
||||||
|
|
||||||
let drender = self.details();
|
let drender = self.details();
|
||||||
|
23
xtests/git_2_ignore_recurse
Normal file
23
xtests/git_2_ignore_recurse
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mdeeply[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mignoreds[0m
|
||||||
|
|
||||||
|
/testcases/git2/deeply:
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m
|
||||||
|
|
||||||
|
/testcases/git2/deeply/nested:
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mdirectory[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mrepository[0m
|
||||||
|
|
||||||
|
/testcases/git2/deeply/nested/directory:
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m l8st
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m18[0m cassowary [34m 1 Jan 12:34[0m upd8d
|
||||||
|
|
||||||
|
/testcases/git2/deeply/nested/repository:
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m subfile
|
||||||
|
|
||||||
|
/testcases/git2/ignoreds:
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mmusic.m4a[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m
|
||||||
|
|
||||||
|
/testcases/git2/ignoreds/nested:
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mfunky chicken.m4a[0m
|
12
xtests/git_2_ignore_tree
Normal file
12
xtests/git_2_ignore_tree
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/[1;34mgit2[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m├──[0m [1;34mdeeply[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m│ └──[0m [1;34mnested[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m│ ├──[0m [1;34mdirectory[0m
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m│ │ ├──[0m l8st
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m18[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m│ │ └──[0m upd8d
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m│ └──[0m [1;34mrepository[0m
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m│ └──[0m subfile
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m└──[0m [1;34mignoreds[0m
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m ├──[0m [38;5;92mmusic.m4a[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m └──[0m [1;34mnested[0m
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m └──[0m [38;5;92mfunky chicken.m4a[0m
|
@ -223,6 +223,11 @@ $exa $testcases/git2/deeply/nested/directory $testcases/git/edits \
|
|||||||
COLUMNS=40 $exa $testcases/files -lG --git | diff -q - $results/files_lG_40 || exit 1 # that aren't under git
|
COLUMNS=40 $exa $testcases/files -lG --git | diff -q - $results/files_lG_40 || exit 1 # that aren't under git
|
||||||
|
|
||||||
|
|
||||||
|
# .gitignore
|
||||||
|
$exa $testcases/git2 --recurse --long --git-ignore 2>&1 | diff - $results/git_2_ignore_recurse
|
||||||
|
$exa $testcases/git2 --tree --long --git-ignore 2>&1 | diff - $results/git_2_ignore_tree
|
||||||
|
|
||||||
|
|
||||||
# Hidden files
|
# Hidden files
|
||||||
COLUMNS=80 $exa $testcases/hiddens 2>&1 | diff -q - $results/hiddens || exit 1
|
COLUMNS=80 $exa $testcases/hiddens 2>&1 | diff -q - $results/hiddens || exit 1
|
||||||
COLUMNS=80 $exa $testcases/hiddens -a 2>&1 | diff -q - $results/hiddens_a || exit 1
|
COLUMNS=80 $exa $testcases/hiddens -a 2>&1 | diff -q - $results/hiddens_a || exit 1
|
||||||
|
Loading…
Reference in New Issue
Block a user