1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2024-12-12 19:18:24 +02:00

Support .jj as well as .git

- Allow `.jj` dirs to count as vcs directories.
- Simplify the control flow around resolving info/exclude
This commit is contained in:
Matt Kulukundis 2024-06-22 05:22:18 -04:00
parent c9ebcbd8ab
commit bb1f30f88e

View File

@ -212,7 +212,7 @@ impl Ignore {
igtmp.absolute_base = Some(absolute_base.clone()); igtmp.absolute_base = Some(absolute_base.clone());
igtmp.has_git = igtmp.has_git =
if self.0.opts.require_git && self.0.opts.git_ignore { if self.0.opts.require_git && self.0.opts.git_ignore {
parent.join(".git").exists() parent.join(".git").exists() || parent.join(".jj").exists()
} else { } else {
false false
}; };
@ -244,15 +244,6 @@ impl Ignore {
/// Like add_child, but takes a full path and returns an IgnoreInner. /// Like add_child, but takes a full path and returns an IgnoreInner.
fn add_child_path(&self, dir: &Path) -> (IgnoreInner, Option<Error>) { fn add_child_path(&self, dir: &Path) -> (IgnoreInner, Option<Error>) {
let git_type = if self.0.opts.require_git
&& (self.0.opts.git_ignore || self.0.opts.git_exclude)
{
dir.join(".git").metadata().ok().map(|md| md.file_type())
} else {
None
};
let has_git = git_type.map(|_| true).unwrap_or(false);
let mut errs = PartialErrorBuilder::default(); let mut errs = PartialErrorBuilder::default();
let custom_ig_matcher = if self.0.custom_ignore_filenames.is_empty() { let custom_ig_matcher = if self.0.custom_ignore_filenames.is_empty() {
Gitignore::empty() Gitignore::empty()
@ -290,10 +281,28 @@ impl Ignore {
errs.maybe_push(err); errs.maybe_push(err);
m m
}; };
let mut git_dir = dir.join(".git");
let mut git_type: Option<FileType> = None;
if self.0.opts.require_git {
git_type = git_dir.metadata().ok().map(|md| md.file_type());
if git_type.is_none() {
let jj_internal_git_dir = dir.join(".jj/repo/store/git");
let jj_type = jj_internal_git_dir
.metadata()
.ok()
.map(|md| md.file_type());
if jj_type.is_some() {
git_dir = jj_internal_git_dir;
git_type = jj_type;
}
}
}
let gi_exclude_matcher = if !self.0.opts.git_exclude { let gi_exclude_matcher = if !self.0.opts.git_exclude {
Gitignore::empty() Gitignore::empty()
} else { } else {
match resolve_git_commondir(dir, git_type) { match resolve_git_commondir(git_dir, git_type) {
Ok(git_dir) => { Ok(git_dir) => {
let (m, err) = create_gitignore( let (m, err) = create_gitignore(
&dir, &dir,
@ -325,7 +334,7 @@ impl Ignore {
git_global_matcher: self.0.git_global_matcher.clone(), git_global_matcher: self.0.git_global_matcher.clone(),
git_ignore_matcher: gi_matcher, git_ignore_matcher: gi_matcher,
git_exclude_matcher: gi_exclude_matcher, git_exclude_matcher: gi_exclude_matcher,
has_git, has_git: git_type.is_some(),
opts: self.0.opts, opts: self.0.opts,
}; };
(ig, errs.into_error_option()) (ig, errs.into_error_option())
@ -829,24 +838,22 @@ pub(crate) fn create_gitignore<T: AsRef<OsStr>>(
/// ///
/// Some I/O errors are ignored. /// Some I/O errors are ignored.
fn resolve_git_commondir( fn resolve_git_commondir(
dir: &Path, git_dir: PathBuf,
git_type: Option<FileType>, git_type: Option<FileType>,
) -> Result<PathBuf, Option<Error>> { ) -> Result<PathBuf, Option<Error>> {
let git_dir_path = || dir.join(".git");
let git_dir = git_dir_path();
if !git_type.map_or(false, |ft| ft.is_file()) { if !git_type.map_or(false, |ft| ft.is_file()) {
return Ok(git_dir); return Ok(git_dir);
} }
let file = match File::open(git_dir) { let file = match File::open(&git_dir) {
Ok(file) => io::BufReader::new(file), Ok(file) => io::BufReader::new(file),
Err(err) => { Err(err) => {
return Err(Some(Error::Io(err).with_path(git_dir_path()))); return Err(Some(Error::Io(err).with_path(git_dir)));
} }
}; };
let dot_git_line = match file.lines().next() { let dot_git_line = match file.lines().next() {
Some(Ok(line)) => line, Some(Ok(line)) => line,
Some(Err(err)) => { Some(Err(err)) => {
return Err(Some(Error::Io(err).with_path(git_dir_path()))); return Err(Some(Error::Io(err).with_path(git_dir)));
} }
None => return Err(None), None => return Err(None),
}; };
@ -943,6 +950,19 @@ mod tests {
assert!(ig.matched("baz", false).is_none()); assert!(ig.matched("baz", false).is_none());
} }
#[test]
fn gitignore_with_jj() {
let td = tmpdir();
mkdirp(td.path().join(".jj/repo/store/git"));
wfile(td.path().join(".gitignore"), "foo\n!bar");
let (ig, err) = IgnoreBuilder::new().build().add_child(td.path());
assert!(err.is_none());
assert!(ig.matched("foo", false).is_ignore());
assert!(ig.matched("bar", false).is_whitelist());
assert!(ig.matched("baz", false).is_none());
}
#[test] #[test]
fn gitignore_no_git() { fn gitignore_no_git() {
let td = tmpdir(); let td = tmpdir();