xtask/
fs_helpers.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Helper functions to traverse + enumerate the project's filesystem, used by
5//! multiple task implementations.
6
7use std::collections::BTreeSet;
8use std::path::PathBuf;
9
10use crate::shell::XtaskShell;
11
12/// Return a list of all files that are currently git diffed, including
13/// those which have been staged, but not yet been committed.
14pub fn git_diffed(in_git_hook: bool) -> anyhow::Result<Vec<PathBuf>> {
15    let sh = XtaskShell::new()?;
16
17    let files = sh
18        .cmd("git")
19        .args(["diff", "--diff-filter", "MAR", "--name-only"])
20        .output()?
21        .stdout;
22    let files_cached = sh
23        .cmd("git")
24        .args(["diff", "--diff-filter", "MAR", "--name-only", "--cached"])
25        .output()?
26        .stdout;
27
28    let files = String::from_utf8_lossy(&files);
29    let files_cached = String::from_utf8_lossy(&files_cached);
30
31    // don't include unstaged files when running in a hook context
32    let files: Box<dyn Iterator<Item = _>> = if in_git_hook {
33        Box::new(files_cached.lines())
34    } else {
35        Box::new(files_cached.lines().chain(files.lines()))
36    };
37
38    let mut all_files = files.map(PathBuf::from).collect::<Vec<_>>();
39
40    all_files.sort();
41    all_files.dedup();
42    Ok(all_files)
43}
44
45/// Return files tracked by git (excluding those from .gitignore), including
46/// those which have not yet been staged / committed.
47pub fn git_ls_files() -> anyhow::Result<Vec<PathBuf>> {
48    let sh = XtaskShell::new()?;
49
50    macro_rules! as_set {
51        ($($arg:literal),+) => {{
52            let output = sh.cmd("git").args([$($arg),+]).output()?.stdout;
53            let output = String::from_utf8_lossy(&output).to_string();
54            output
55                .split('\n')
56                .map(PathBuf::from)
57                .collect::<BTreeSet<_>>()
58        }};
59    }
60
61    // "extra" corresponds to files not-yet committed to git
62    let all = as_set!("ls-files");
63    let extra = as_set!("ls-files", "--others", "--exclude-standard");
64    let deleted = as_set!("ls-files", "--deleted");
65
66    let mut allow_list = all;
67    allow_list.extend(extra);
68    allow_list = allow_list.difference(&deleted).cloned().collect();
69
70    // Vec is returned in sorted order because of BTreeSet iteration order
71    Ok(allow_list.into_iter().collect())
72}