mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2024-12-12 19:18:24 +02:00
Add option to ignore nested git repositories
This implements the suggestion made in #23 to provide an option to ignore nested git repositories. A nested git repository is identified by the presence of a .git file or directory. It's a directory in the regular case, but it's a file for git worktrees and git submodules. This option is disabled by default.
This commit is contained in:
parent
e9abbc1a02
commit
e886ad0615
@ -146,6 +146,10 @@ _rg() {
|
|||||||
'--ignore-file-case-insensitive[process ignore files case insensitively]'
|
'--ignore-file-case-insensitive[process ignore files case insensitively]'
|
||||||
$no'--no-ignore-file-case-insensitive[process ignore files case sensitively]'
|
$no'--no-ignore-file-case-insensitive[process ignore files case sensitively]'
|
||||||
|
|
||||||
|
+ '(ignore-nested-git)' # Ignore nested git repository option
|
||||||
|
'--ignore-nested-git[ignore nested git repositories]'
|
||||||
|
$no"--no-ignore-nested-git[don't ignore nested git repositories]"
|
||||||
|
|
||||||
+ '(ignore-exclude)' # Local exclude (ignore)-file options
|
+ '(ignore-exclude)' # Local exclude (ignore)-file options
|
||||||
"--no-ignore-exclude[don't respect local exclude (ignore) files]"
|
"--no-ignore-exclude[don't respect local exclude (ignore) files]"
|
||||||
$no'--ignore-exclude[respect local exclude (ignore) files]'
|
$no'--ignore-exclude[respect local exclude (ignore) files]'
|
||||||
|
@ -83,6 +83,7 @@ pub(super) const FLAGS: &[&dyn Flag] = &[
|
|||||||
&IgnoreCase,
|
&IgnoreCase,
|
||||||
&IgnoreFile,
|
&IgnoreFile,
|
||||||
&IgnoreFileCaseInsensitive,
|
&IgnoreFileCaseInsensitive,
|
||||||
|
&IgnoreNestedGit,
|
||||||
&IncludeZero,
|
&IncludeZero,
|
||||||
&InvertMatch,
|
&InvertMatch,
|
||||||
&JSON,
|
&JSON,
|
||||||
@ -3238,6 +3239,62 @@ fn test_ignore_file_case_insensitive() {
|
|||||||
assert_eq!(true, args.ignore_file_case_insensitive);
|
assert_eq!(true, args.ignore_file_case_insensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// --ignore-nested-git
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct IgnoreNestedGit;
|
||||||
|
|
||||||
|
impl Flag for IgnoreNestedGit {
|
||||||
|
fn is_switch(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn name_long(&self) -> &'static str {
|
||||||
|
"ignore-nested-git"
|
||||||
|
}
|
||||||
|
fn name_negated(&self) -> Option<&'static str> {
|
||||||
|
Some("no-ignore-nested-git")
|
||||||
|
}
|
||||||
|
fn doc_category(&self) -> Category {
|
||||||
|
Category::Filter
|
||||||
|
}
|
||||||
|
fn doc_short(&self) -> &'static str {
|
||||||
|
r"Ignore nested git repositories."
|
||||||
|
}
|
||||||
|
fn doc_long(&self) -> &'static str {
|
||||||
|
r"
|
||||||
|
Ignore any nested directory containing a \fB.git\fP file or directory.
|
||||||
|
This will prevent ripgrep from recursing into any git worktrees, submodules,
|
||||||
|
or regular repositories.
|
||||||
|
.sp
|
||||||
|
Note that this does not affect top-level directories.
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
||||||
|
args.ignore_nested_git = v.unwrap_switch();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[test]
|
||||||
|
fn test_ignore_nested_git() {
|
||||||
|
let args = parse_low_raw(None::<&str>).unwrap();
|
||||||
|
assert_eq!(false, args.ignore_nested_git);
|
||||||
|
|
||||||
|
let args = parse_low_raw(["--ignore-nested-git"]).unwrap();
|
||||||
|
assert_eq!(true, args.ignore_nested_git);
|
||||||
|
|
||||||
|
let args =
|
||||||
|
parse_low_raw(["--ignore-nested-git", "--no-ignore-nested-git"])
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(false, args.ignore_nested_git);
|
||||||
|
|
||||||
|
let args =
|
||||||
|
parse_low_raw(["--no-ignore-nested-git", "--ignore-nested-git"])
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(true, args.ignore_nested_git);
|
||||||
|
}
|
||||||
|
|
||||||
/// --include-zero
|
/// --include-zero
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct IncludeZero;
|
struct IncludeZero;
|
||||||
|
@ -59,6 +59,7 @@ pub(crate) struct HiArgs {
|
|||||||
hyperlink_config: grep::printer::HyperlinkConfig,
|
hyperlink_config: grep::printer::HyperlinkConfig,
|
||||||
ignore_file_case_insensitive: bool,
|
ignore_file_case_insensitive: bool,
|
||||||
ignore_file: Vec<PathBuf>,
|
ignore_file: Vec<PathBuf>,
|
||||||
|
ignore_nested_git: bool,
|
||||||
include_zero: bool,
|
include_zero: bool,
|
||||||
invert_match: bool,
|
invert_match: bool,
|
||||||
is_terminal_stdout: bool,
|
is_terminal_stdout: bool,
|
||||||
@ -275,6 +276,7 @@ impl HiArgs {
|
|||||||
hyperlink_config,
|
hyperlink_config,
|
||||||
ignore_file: low.ignore_file,
|
ignore_file: low.ignore_file,
|
||||||
ignore_file_case_insensitive: low.ignore_file_case_insensitive,
|
ignore_file_case_insensitive: low.ignore_file_case_insensitive,
|
||||||
|
ignore_nested_git: low.ignore_nested_git,
|
||||||
include_zero: low.include_zero,
|
include_zero: low.include_zero,
|
||||||
invert_match: low.invert_match,
|
invert_match: low.invert_match,
|
||||||
is_terminal_stdout: state.is_terminal_stdout,
|
is_terminal_stdout: state.is_terminal_stdout,
|
||||||
@ -893,7 +895,8 @@ impl HiArgs {
|
|||||||
.git_ignore(!self.no_ignore_vcs)
|
.git_ignore(!self.no_ignore_vcs)
|
||||||
.git_exclude(!self.no_ignore_vcs && !self.no_ignore_exclude)
|
.git_exclude(!self.no_ignore_vcs && !self.no_ignore_exclude)
|
||||||
.require_git(!self.no_require_git)
|
.require_git(!self.no_require_git)
|
||||||
.ignore_case_insensitive(self.ignore_file_case_insensitive);
|
.ignore_case_insensitive(self.ignore_file_case_insensitive)
|
||||||
|
.ignore_nested_git_repo(self.ignore_nested_git);
|
||||||
if !self.no_ignore_dot {
|
if !self.no_ignore_dot {
|
||||||
builder.add_custom_ignore_filename(".rgignore");
|
builder.add_custom_ignore_filename(".rgignore");
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,7 @@ pub(crate) struct LowArgs {
|
|||||||
pub(crate) iglobs: Vec<String>,
|
pub(crate) iglobs: Vec<String>,
|
||||||
pub(crate) ignore_file: Vec<PathBuf>,
|
pub(crate) ignore_file: Vec<PathBuf>,
|
||||||
pub(crate) ignore_file_case_insensitive: bool,
|
pub(crate) ignore_file_case_insensitive: bool,
|
||||||
|
pub(crate) ignore_nested_git: bool,
|
||||||
pub(crate) include_zero: bool,
|
pub(crate) include_zero: bool,
|
||||||
pub(crate) invert_match: bool,
|
pub(crate) invert_match: bool,
|
||||||
pub(crate) line_number: Option<bool>,
|
pub(crate) line_number: Option<bool>,
|
||||||
|
@ -46,6 +46,7 @@ enum IgnoreMatchInner<'a> {
|
|||||||
Gitignore(&'a gitignore::Glob),
|
Gitignore(&'a gitignore::Glob),
|
||||||
Types(types::Glob<'a>),
|
Types(types::Glob<'a>),
|
||||||
Hidden,
|
Hidden,
|
||||||
|
NestedRepo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IgnoreMatch<'a> {
|
impl<'a> IgnoreMatch<'a> {
|
||||||
@ -64,6 +65,9 @@ impl<'a> IgnoreMatch<'a> {
|
|||||||
fn hidden() -> IgnoreMatch<'static> {
|
fn hidden() -> IgnoreMatch<'static> {
|
||||||
IgnoreMatch(IgnoreMatchInner::Hidden)
|
IgnoreMatch(IgnoreMatchInner::Hidden)
|
||||||
}
|
}
|
||||||
|
fn nested_repo() -> IgnoreMatch<'static> {
|
||||||
|
IgnoreMatch(IgnoreMatchInner::NestedRepo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for the ignore matcher, shared between the matcher itself and the
|
/// Options for the ignore matcher, shared between the matcher itself and the
|
||||||
@ -84,6 +88,8 @@ struct IgnoreOptions {
|
|||||||
git_exclude: bool,
|
git_exclude: bool,
|
||||||
/// Whether to ignore files case insensitively
|
/// Whether to ignore files case insensitively
|
||||||
ignore_case_insensitive: bool,
|
ignore_case_insensitive: bool,
|
||||||
|
/// Whether to ignore nested git repositories.
|
||||||
|
ignore_nested_git_repo: bool,
|
||||||
/// Whether a git repository must be present in order to apply any
|
/// Whether a git repository must be present in order to apply any
|
||||||
/// git-related ignore rules.
|
/// git-related ignore rules.
|
||||||
require_git: bool,
|
require_git: bool,
|
||||||
@ -342,6 +348,7 @@ impl Ignore {
|
|||||||
|| opts.git_global
|
|| opts.git_global
|
||||||
|| opts.git_ignore
|
|| opts.git_ignore
|
||||||
|| opts.git_exclude
|
|| opts.git_exclude
|
||||||
|
|| opts.ignore_nested_git_repo
|
||||||
|| has_custom_ignore_files
|
|| has_custom_ignore_files
|
||||||
|| has_explicit_ignores
|
|| has_explicit_ignores
|
||||||
}
|
}
|
||||||
@ -422,6 +429,14 @@ impl Ignore {
|
|||||||
mut m_gi_exclude,
|
mut m_gi_exclude,
|
||||||
mut m_explicit,
|
mut m_explicit,
|
||||||
) = (Match::None, Match::None, Match::None, Match::None, Match::None);
|
) = (Match::None, Match::None, Match::None, Match::None, Match::None);
|
||||||
|
|
||||||
|
if is_dir
|
||||||
|
&& self.0.opts.ignore_nested_git_repo
|
||||||
|
&& path.join(".git").exists()
|
||||||
|
{
|
||||||
|
return Match::Ignore(IgnoreMatch::nested_repo());
|
||||||
|
}
|
||||||
|
|
||||||
let any_git =
|
let any_git =
|
||||||
!self.0.opts.require_git || self.parents().any(|ig| ig.0.has_git);
|
!self.0.opts.require_git || self.parents().any(|ig| ig.0.has_git);
|
||||||
let mut saw_git = false;
|
let mut saw_git = false;
|
||||||
@ -599,6 +614,7 @@ impl IgnoreBuilder {
|
|||||||
git_ignore: true,
|
git_ignore: true,
|
||||||
git_exclude: true,
|
git_exclude: true,
|
||||||
ignore_case_insensitive: false,
|
ignore_case_insensitive: false,
|
||||||
|
ignore_nested_git_repo: false,
|
||||||
require_git: true,
|
require_git: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -773,6 +789,17 @@ impl IgnoreBuilder {
|
|||||||
self.opts.ignore_case_insensitive = yes;
|
self.opts.ignore_case_insensitive = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enables ignoring nested git repositories.
|
||||||
|
///
|
||||||
|
/// This is disabled by default.
|
||||||
|
pub(crate) fn ignore_nested_git_repo(
|
||||||
|
&mut self,
|
||||||
|
yes: bool,
|
||||||
|
) -> &mut IgnoreBuilder {
|
||||||
|
self.opts.ignore_nested_git_repo = yes;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new gitignore matcher for the directory given.
|
/// Creates a new gitignore matcher for the directory given.
|
||||||
@ -887,6 +914,10 @@ mod tests {
|
|||||||
file.write_all(contents.as_bytes()).unwrap();
|
file.write_all(contents.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rmfile<P: AsRef<Path>>(path: P) {
|
||||||
|
std::fs::remove_file(path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn mkdirp<P: AsRef<Path>>(path: P) {
|
fn mkdirp<P: AsRef<Path>>(path: P) {
|
||||||
std::fs::create_dir_all(path).unwrap();
|
std::fs::create_dir_all(path).unwrap();
|
||||||
}
|
}
|
||||||
@ -1132,6 +1163,41 @@ mod tests {
|
|||||||
assert!(ig2.matched("bar", false).is_ignore());
|
assert!(ig2.matched("bar", false).is_ignore());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
#[test]
|
||||||
|
fn ignore_nested_git() {
|
||||||
|
let td = tmpdir();
|
||||||
|
let repo = td.path().join("foo");
|
||||||
|
mkdirp(&repo);
|
||||||
|
let dotgit_path = repo.join(".git");
|
||||||
|
wfile(&dotgit_path, "");
|
||||||
|
wfile(repo.join("bar"), "");
|
||||||
|
|
||||||
|
let (ig_default, err) =
|
||||||
|
IgnoreBuilder::new().build().add_child(td.path());
|
||||||
|
assert!(err.is_none());
|
||||||
|
|
||||||
|
let (ig_git, err) = IgnoreBuilder::new()
|
||||||
|
.ignore_nested_git_repo(true)
|
||||||
|
.build()
|
||||||
|
.add_child(td.path());
|
||||||
|
assert!(err.is_none());
|
||||||
|
|
||||||
|
// is_dir = false, so no check for .git child
|
||||||
|
assert!(ig_git.matched(&repo, false).is_none());
|
||||||
|
// on the same level as .git; it's expected that the parent directory wouldn't be recursed in this case
|
||||||
|
assert!(ig_git.matched(repo.join("bar"), false).is_none());
|
||||||
|
// is_dir = true and has .git child
|
||||||
|
assert!(ig_git.matched(&repo, true).is_ignore());
|
||||||
|
// but by default, don't ignore dir with .git child
|
||||||
|
assert!(ig_default.matched(&repo, true).is_none());
|
||||||
|
|
||||||
|
// also test with .git as a directory
|
||||||
|
rmfile(&dotgit_path);
|
||||||
|
mkdirp(&dotgit_path);
|
||||||
|
assert!(ig_git.matched(&repo, true).is_ignore());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn absolute_parent() {
|
fn absolute_parent() {
|
||||||
let td = tmpdir();
|
let td = tmpdir();
|
||||||
|
@ -811,6 +811,14 @@ impl WalkBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enables ignoring nested git repositories, including submodules.
|
||||||
|
///
|
||||||
|
/// This is disabled by default.
|
||||||
|
pub fn ignore_nested_git_repo(&mut self, yes: bool) -> &mut WalkBuilder {
|
||||||
|
self.ig_builder.ignore_nested_git_repo(yes);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a function for sorting directory entries by their path.
|
/// Set a function for sorting directory entries by their path.
|
||||||
///
|
///
|
||||||
/// If a compare function is set, the resulting iterator will return all
|
/// If a compare function is set, the resulting iterator will return all
|
||||||
|
Loading…
Reference in New Issue
Block a user