1use std::io::{self, Write};
2use std::path::Path;
3
4use git2::{Diff, Oid};
5
6use crate::Error;
7
8pub(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 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
40pub fn diff<'a>(repo: &'a git2::Repository, old: &str, new: &str) -> crate::Result<Diff<'a>> {
42 let old_oid: Oid = old
44 .parse()
45 .map_err(|_| Error::InvalidPushRequest(format!("invalid old ref: {old}")))?;
46 let old_commit = repo
47 .find_commit(old_oid)
48 .map_err(|_| Error::InvalidPushRequest(format!("nonexistent old ref: {old}")))?;
49 let old_tree = old_commit.tree().unwrap();
50
51 let new_oid: Oid = new
53 .parse()
54 .map_err(|_| Error::InvalidPushRequest(format!("invalid new ref: {new}")))?;
55 let new_commit = repo
56 .find_commit(new_oid)
57 .map_err(|_| Error::InvalidPushRequest(format!("nonexistent new ref: {new}")))?;
58 let new_tree = new_commit.tree().unwrap();
59
60 repo.diff_tree_to_tree(Some(&old_tree), Some(&new_tree), None)
61 .map_err(|e| Error::InvalidPushRequest(format!("failed diff: {old} -> {new}: {e}")))
62}