Skip to main content

pgit_native/
fs.rs

1use std::path::Path;
2
3use anyhow::{Context, Result};
4use pgit_core::hash;
5
6/// Recursively copy `src` into `dest`, skipping `.git`.
7pub fn copy_dir(src: &Path, dest: &Path) -> Result<()> {
8    std::fs::create_dir_all(dest)
9        .with_context(|| format!("create {}", dest.display()))?;
10
11    for entry in std::fs::read_dir(src)
12        .with_context(|| format!("read {}", src.display()))?
13    {
14        let entry = entry.with_context(|| format!("read entry in {}", src.display()))?;
15        if entry.file_name() == ".git" {
16            continue;
17        }
18        let src_path  = entry.path();
19        let dest_path = dest.join(entry.file_name());
20        if src_path.is_dir() {
21            copy_dir(&src_path, &dest_path)?;
22        } else {
23            std::fs::copy(&src_path, &dest_path).with_context(|| {
24                format!("copy {} -> {}", src_path.display(), dest_path.display())
25            })?;
26        }
27    }
28    Ok(())
29}
30
31/// Collect all files under `root` as `(relative_path, bytes)` pairs,
32/// skipping `.git`.  Used for tree hashing.
33pub fn collect_tree(root: &Path) -> Result<Vec<(String, Vec<u8>)>> {
34    let mut entries = Vec::new();
35    collect_recursive(root, root, &mut entries)?;
36    Ok(entries)
37}
38
39fn collect_recursive(
40    root:    &Path,
41    current: &Path,
42    out:     &mut Vec<(String, Vec<u8>)>,
43) -> Result<()> {
44    for entry in std::fs::read_dir(current)
45        .with_context(|| format!("read {}", current.display()))?
46    {
47        let entry = entry?;
48        if entry.file_name() == ".git" {
49            continue;
50        }
51        let path = entry.path();
52        if path.is_dir() {
53            collect_recursive(root, &path, out)?;
54        } else {
55            let rel = path
56                .strip_prefix(root)
57                .unwrap()
58                .to_string_lossy()
59                .into_owned();
60            let bytes = std::fs::read(&path)
61                .with_context(|| format!("read {}", path.display()))?;
62            out.push((rel, bytes));
63        }
64    }
65    Ok(())
66}
67
68/// Compute the SHA-256 tree hash of a directory.
69pub fn hash_dir(root: &Path) -> Result<String> {
70    let mut entries = collect_tree(root)?;
71    Ok(hash::sha256_tree(&mut entries))
72}