3K2FVFHEU5R4HBCGYR5QTAJ33MZ3GVMCGHWT2GFNJPLE74AV53LAC
/// Filter log output, showing only log entries that touched the specified
/// files. Accepted as a list of paths relative to your current directory.
#[clap(last = true)]
filters: Vec<String>,
}
/// The user gives a list of patterns to say they only want to see the log
/// entries that affected those files. The patterns are given relative to the directory
/// they're calling from, not relative to the repository root, but we need to then
/// strip the repository path off the canonicalized filter path so
/// that we can match the file paths as they appear in the pijul change files, which
/// are recorded relative to the repository's root.
fn mk_filters(repo_path: &Path, mut pats: Vec<String>) -> Result<Vec<String>, anyhow::Error> {
for pat in &mut pats {
let canon_path = match Path::new(pat).canonicalize() {
Err(e) if matches!(e.kind(), std::io::ErrorKind::NotFound) => return Err(
anyhow::Error::msg(format!("pijul log couldn't find a file or directory corresponding to `{}`", pat))
),
Err(e) => return Err(e.into()),
Ok(p) => p
};
match canon_path.strip_prefix(repo_path) {
Err(_) => return Err(anyhow::Error::msg(
format!("pijul log couldn't assemble file prefix for pattern `{}`", pat)
)),
Ok(stripped) => { *pat = format!("{}", stripped.display()); },
};
}
debug!("log filters: {:#?}\n", pats);
Ok(pats)
/// Figure out whether a given change touched one of the files
/// the user is filtering for.
fn filter_hit(
filters: &[String],
h: libpijul::Hash,
changes: &filesystem::FileSystem
) -> Result<bool, anyhow::Error> {
match changes.filename(&h).to_str() {
None => return Err(anyhow::Error::msg(format!("internal error: changefile had non-utf8 name"))),
Some(changefile_name) => {
let changefile = libpijul::change::ChangeFile::open(h, changefile_name)?;
if changefile.hashed().changes.iter().any(|hunk| {
filters.iter().any(|f| hunk.path().starts_with(f) || hunk.path() == f)
}) {
return Ok(true)
}
Ok(false)
}
}
}
writeln!(stdout, "Change {}", h.to_base32())?;
writeln!(stdout, "Author: {:?}", header.authors)?;
writeln!(stdout, "Date: {}", header.timestamp)?;
if states {
writeln!(stdout, "State: {}", mrk.to_base32())?;
}
writeln!(stdout, "\n {}\n", header.message)?;
if self.descriptions {
if let Some(ref descr) = header.description {
writeln!(stdout, "\n {}\n", descr)?;
if log_filters.is_empty() || filter_hit(log_filters.as_slice(), h, &changes)? {
writeln!(stdout, "Change {}", h.to_base32())?;
writeln!(stdout, "Author: {:?}", header.authors)?;
writeln!(stdout, "Date: {}", header.timestamp)?;
if states {
writeln!(stdout, "State: {}", mrk.to_base32())?;
}
writeln!(stdout, "\n {}\n", header.message)?;
if self.descriptions {
if let Some(ref descr) = header.description {
writeln!(stdout, "\n {}\n", descr)?;
}