mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2024-12-07 11:13:17 +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]'
|
||||
$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
|
||||
"--no-ignore-exclude[don't respect local exclude (ignore) files]"
|
||||
$no'--ignore-exclude[respect local exclude (ignore) files]'
|
||||
|
@ -83,6 +83,7 @@ pub(super) const FLAGS: &[&dyn Flag] = &[
|
||||
&IgnoreCase,
|
||||
&IgnoreFile,
|
||||
&IgnoreFileCaseInsensitive,
|
||||
&IgnoreNestedGit,
|
||||
&IncludeZero,
|
||||
&InvertMatch,
|
||||
&JSON,
|
||||
@ -3238,6 +3239,62 @@ fn test_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
|
||||
#[derive(Debug)]
|
||||
struct IncludeZero;
|
||||
|
@ -59,6 +59,7 @@ pub(crate) struct HiArgs {
|
||||
hyperlink_config: grep::printer::HyperlinkConfig,
|
||||
ignore_file_case_insensitive: bool,
|
||||
ignore_file: Vec<PathBuf>,
|
||||
ignore_nested_git: bool,
|
||||
include_zero: bool,
|
||||
invert_match: bool,
|
||||
is_terminal_stdout: bool,
|
||||
@ -275,6 +276,7 @@ impl HiArgs {
|
||||
hyperlink_config,
|
||||
ignore_file: low.ignore_file,
|
||||
ignore_file_case_insensitive: low.ignore_file_case_insensitive,
|
||||
ignore_nested_git: low.ignore_nested_git,
|
||||
include_zero: low.include_zero,
|
||||
invert_match: low.invert_match,
|
||||
is_terminal_stdout: state.is_terminal_stdout,
|
||||
@ -893,7 +895,8 @@ impl HiArgs {
|
||||
.git_ignore(!self.no_ignore_vcs)
|
||||
.git_exclude(!self.no_ignore_vcs && !self.no_ignore_exclude)
|
||||
.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 {
|
||||
builder.add_custom_ignore_filename(".rgignore");
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ pub(crate) struct LowArgs {
|
||||
pub(crate) iglobs: Vec<String>,
|
||||
pub(crate) ignore_file: Vec<PathBuf>,
|
||||
pub(crate) ignore_file_case_insensitive: bool,
|
||||
pub(crate) ignore_nested_git: bool,
|
||||
pub(crate) include_zero: bool,
|
||||
pub(crate) invert_match: bool,
|
||||
pub(crate) line_number: Option<bool>,
|
||||
|
@ -46,6 +46,7 @@ enum IgnoreMatchInner<'a> {
|
||||
Gitignore(&'a gitignore::Glob),
|
||||
Types(types::Glob<'a>),
|
||||
Hidden,
|
||||
NestedRepo,
|
||||
}
|
||||
|
||||
impl<'a> IgnoreMatch<'a> {
|
||||
@ -64,6 +65,9 @@ impl<'a> IgnoreMatch<'a> {
|
||||
fn hidden() -> IgnoreMatch<'static> {
|
||||
IgnoreMatch(IgnoreMatchInner::Hidden)
|
||||
}
|
||||
fn nested_repo() -> IgnoreMatch<'static> {
|
||||
IgnoreMatch(IgnoreMatchInner::NestedRepo)
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for the ignore matcher, shared between the matcher itself and the
|
||||
@ -84,6 +88,8 @@ struct IgnoreOptions {
|
||||
git_exclude: bool,
|
||||
/// Whether to ignore files case insensitively
|
||||
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
|
||||
/// git-related ignore rules.
|
||||
require_git: bool,
|
||||
@ -342,6 +348,7 @@ impl Ignore {
|
||||
|| opts.git_global
|
||||
|| opts.git_ignore
|
||||
|| opts.git_exclude
|
||||
|| opts.ignore_nested_git_repo
|
||||
|| has_custom_ignore_files
|
||||
|| has_explicit_ignores
|
||||
}
|
||||
@ -422,6 +429,14 @@ impl Ignore {
|
||||
mut m_gi_exclude,
|
||||
mut m_explicit,
|
||||
) = (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 =
|
||||
!self.0.opts.require_git || self.parents().any(|ig| ig.0.has_git);
|
||||
let mut saw_git = false;
|
||||
@ -599,6 +614,7 @@ impl IgnoreBuilder {
|
||||
git_ignore: true,
|
||||
git_exclude: true,
|
||||
ignore_case_insensitive: false,
|
||||
ignore_nested_git_repo: false,
|
||||
require_git: true,
|
||||
},
|
||||
}
|
||||
@ -773,6 +789,17 @@ impl IgnoreBuilder {
|
||||
self.opts.ignore_case_insensitive = yes;
|
||||
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.
|
||||
@ -887,6 +914,10 @@ mod tests {
|
||||
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) {
|
||||
std::fs::create_dir_all(path).unwrap();
|
||||
}
|
||||
@ -1132,6 +1163,41 @@ mod tests {
|
||||
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]
|
||||
fn absolute_parent() {
|
||||
let td = tmpdir();
|
||||
|
@ -811,6 +811,14 @@ impl WalkBuilder {
|
||||
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.
|
||||
///
|
||||
/// If a compare function is set, the resulting iterator will return all
|
||||
|
Loading…
Reference in New Issue
Block a user