use std::path::Path;
use crate::git_helpers::git2_to_io_error;
pub(super) enum HeadTreeOid {
Tree(git2::Oid),
UnbornBranch,
}
pub(super) fn configured_diff_options() -> git2::DiffOptions {
let mut diff_opts = git2::DiffOptions::new();
diff_opts.include_untracked(true);
diff_opts.recurse_untracked_dirs(true);
diff_opts
}
pub(super) fn discover_repo(from: &Path) -> std::io::Result<git2::Repository> {
git2::Repository::discover(from).map_err(|e| git2_to_io_error(&e))
}
pub(super) fn resolve_head_tree_oid(repo: &git2::Repository) -> std::io::Result<HeadTreeOid> {
repo.head()
.map_err(|e| git2_to_io_error(&e))
.and_then(|head| {
head.peel_to_tree()
.map_err(|e| git2_to_io_error(&e))
.map(|tree| HeadTreeOid::Tree(tree.id()))
})
.or_else(|io_err| {
if io_err.kind() == std::io::ErrorKind::NotFound {
Ok(HeadTreeOid::UnbornBranch)
} else {
Err(io_err)
}
})
}
pub(super) fn diff_from_oid_impl(
repo: &git2::Repository,
oid: git2::Oid,
) -> std::io::Result<String> {
let start_commit = repo.find_commit(oid).map_err(|e| git2_to_io_error(&e))?;
let start_tree = start_commit.tree().map_err(|e| git2_to_io_error(&e))?;
diff_tree_to_workdir(repo, Some(&start_tree))
}
pub(super) fn diff_from_tree_oid_impl(
repo: &git2::Repository,
tree_oid: git2::Oid,
) -> std::io::Result<String> {
let tree = repo.find_tree(tree_oid).map_err(|e| git2_to_io_error(&e))?;
diff_tree_to_workdir(repo, Some(&tree))
}
pub(super) fn diff_from_empty_tree_impl(repo: &git2::Repository) -> std::io::Result<String> {
diff_tree_to_workdir(repo, None)
}
pub(super) fn diff_tree_to_workdir(
repo: &git2::Repository,
tree: Option<&git2::Tree<'_>>,
) -> std::io::Result<String> {
let mut diff_opts = configured_diff_options();
let diff = repo
.diff_tree_to_workdir_with_index(tree, Some(&mut diff_opts))
.map_err(|e| git2_to_io_error(&e))?;
let mut output = String::new();
diff.print(
git2::DiffFormat::Patch,
&mut |_delta: git2::DiffDelta<'_>,
_hunk: Option<git2::DiffHunk<'_>>,
line: git2::DiffLine<'_>| {
if let Ok(content) = std::str::from_utf8(line.content()) {
output.push_str(content);
}
true
},
)
.map_err(|e| git2_to_io_error(&e))?;
Ok(output)
}