pkgcruft_git/
git.rs

1use std::io::{self, Write};
2use std::path::Path;
3
4use git2::{Diff, Oid};
5
6use crate::Error;
7
8/// Clone a git repo into a path.
9pub(crate) fn clone<P: AsRef<Path>>(uri: &str, path: P) -> Result<(), git2::Error> {
10    let path = path.as_ref();
11    let mut cb = git2::RemoteCallbacks::new();
12
13    // show transfer progress
14    cb.transfer_progress(|stats| {
15        if stats.received_objects() == stats.total_objects() {
16            print!("Resolving deltas {}/{}\r", stats.indexed_deltas(), stats.total_deltas());
17        } else if stats.total_objects() > 0 {
18            print!(
19                "Received {}/{} objects ({}) in {} bytes\r",
20                stats.received_objects(),
21                stats.total_objects(),
22                stats.indexed_objects(),
23                stats.received_bytes()
24            );
25        }
26        io::stdout().flush().unwrap();
27        true
28    });
29
30    let mut fo = git2::FetchOptions::new();
31    fo.remote_callbacks(cb);
32
33    let mut builder = git2::build::RepoBuilder::new();
34    builder.fetch_options(fo);
35    builder.clone(uri, path)?;
36
37    Ok(())
38}
39
40/// Try converting pre-receive hook references into a diff.
41pub fn diff<'a>(
42    repo: &'a git2::Repository,
43    old_ref: &str,
44    new_ref: &str,
45) -> crate::Result<Diff<'a>> {
46    // parse old reference and get related tree
47    let old_oid: Oid = old_ref
48        .parse()
49        .map_err(|_| Error::InvalidPushRequest(format!("invalid old ref: {old_ref}")))?;
50    let old_commit = repo
51        .find_commit(old_oid)
52        .map_err(|_| Error::InvalidPushRequest(format!("nonexistent old ref: {old_ref}")))?;
53    let old_tree = old_commit.tree().unwrap();
54
55    // parse new reference and get related tree
56    let new_oid: Oid = new_ref
57        .parse()
58        .map_err(|_| Error::InvalidPushRequest(format!("invalid new ref: {new_ref}")))?;
59    let new_commit = repo
60        .find_commit(new_oid)
61        .map_err(|_| Error::InvalidPushRequest(format!("nonexistent new ref: {new_ref}")))?;
62    let new_tree = new_commit.tree().unwrap();
63
64    repo.diff_tree_to_tree(Some(&old_tree), Some(&new_tree), None)
65        .map_err(|e| {
66            Error::InvalidPushRequest(format!(
67                "failed creating diff: {old_ref} -> {new_ref}: {e}"
68            ))
69        })
70}