Skip to main content

aqc_git_helpers/
queries.rs

1//! Pure queries over worktree changes.
2
3use std::path::Path;
4
5use crate::error::GitError;
6use crate::git::worktree_changes;
7use crate::status::{PorcelainOptions, WorktreeChange};
8
9/// True when [`worktree_changes`] is empty (respecting `options`).
10///
11/// # Errors
12///
13/// Same as [`worktree_changes`].
14pub fn is_worktree_clean(
15    repo_root: impl AsRef<Path>,
16    options: PorcelainOptions,
17) -> Result<bool, GitError> {
18    Ok(worktree_changes(repo_root, options)?.is_empty())
19}
20
21/// The subset of `changes` whose `path` (or `old_path` for renames) equals an
22/// entry in `paths` or lies under one (`"<entry>/"` prefix — directory
23/// boundary, never substring).
24#[must_use]
25pub fn changes_affecting_paths(changes: &[WorktreeChange], paths: &[&str]) -> Vec<WorktreeChange> {
26    let hits = |candidate: &str| {
27        paths
28            .iter()
29            .any(|p| candidate == *p || candidate.starts_with(&format!("{p}/")))
30    };
31    changes
32        .iter()
33        .filter(|c| hits(&c.path) || c.old_path.as_deref().is_some_and(hits))
34        .cloned()
35        .collect()
36}
37
38/// Convenience: [`worktree_changes`] then [`changes_affecting_paths`].
39///
40/// # Errors
41///
42/// Same as [`worktree_changes`].
43pub fn dirty_paths(
44    repo_root: impl AsRef<Path>,
45    paths: &[&str],
46    options: PorcelainOptions,
47) -> Result<Vec<WorktreeChange>, GitError> {
48    let changes = worktree_changes(repo_root, options)?;
49    Ok(changes_affecting_paths(&changes, paths))
50}