summaryrefslogtreecommitdiff
path: root/src/bin/git-diff-autosave.rs
blob: 63aa1304c638a4e0dcd047f0955b9c11424f1164 (plain)
use auth_git2::GitAuthenticator;
use git_autosave::{Config, authenticate::Inquirer, inquire::AutosaveFilters};
use git2::{DiffOptions, RemoteCallbacks, Repository};

fn main() -> Result<(), anyhow::Error> {
	let all_users = std::env::args().any(|arg| arg == "--all-users");
	let all_branches = std::env::args().any(|arg| arg == "--all-branches");
	let all_devices = std::env::args().any(|arg| arg == "--all-devices");
	let anytime = std::env::args().any(|arg| arg == "--anytime");

	let repository = Repository::discover(".")?;
	let repo_id = git_autosave::repository_id(&repository)?;
	let signature = repository.signature()?;
	let branch = git_autosave::utils::current_branch(&repository)?;
	let earliest_time = repository.head()?.peel_to_commit()?.time();

	let gitconfig = repository.config()?;
	let config: &'static _ = Box::leak(Box::new(Config::load()?));
	let auth = GitAuthenticator::new().set_prompter(Inquirer(config));
	let mut callbacks = RemoteCallbacks::new();
	callbacks.credentials(auth.credentials(&gitconfig));
	git_autosave::fetch_autosaves(&repository, callbacks)?;

	let autosaves = git_autosave::autosaves(&repository)?;
	let autosaves = git_autosave::inquire::filter_autosaves(
		autosaves,
		AutosaveFilters {
			signature: &signature,
			branch: &branch,
			earliest_time,
			repo_id: &repo_id,
			all_users,
			all_branches,
			anytime,
			all_devices,
		},
	)
	.collect::<Vec<_>>();

	if autosaves.is_empty() {
		eprintln!("ERROR: There are no available autosaves for the given filters.");
		if !all_users || !all_branches || !anytime {
			eprintln!(
				"hint: Use --all-users, --all-branches, --all-devices, or --anytime to include more options."
			);
		}
		std::process::exit(1);
	}

	let autosave = git_autosave::inquire::select_autosave(autosaves, all_branches, all_users)?;
	let autosaved_commit = repository.find_commit(autosave.commit_id)?;
	let workdir = repository.find_tree(git_autosave::utils::workdir_to_tree(&repository)?)?;
	let new_tree = repository.find_tree(git_autosave::utils::merge_commit_with_tree(
		&repository,
		&autosaved_commit,
		&workdir,
	)?)?;
	let diff = repository.diff_tree_to_tree(
		Some(&workdir),
		Some(&new_tree),
		Some(DiffOptions::new().include_unmodified(false).patience(true)),
	)?;
	diff.print(git2::DiffFormat::Patch, |_, _, line| {
		let origin = match line.origin_value() {
			git2::DiffLineType::Context => " ",
			git2::DiffLineType::Addition => "+",
			git2::DiffLineType::Deletion => "-",
			git2::DiffLineType::ContextEOFNL => "=",
			git2::DiffLineType::AddEOFNL => ">",
			git2::DiffLineType::DeleteEOFNL => "<",
			git2::DiffLineType::FileHeader => "",
			git2::DiffLineType::HunkHeader => "",
			git2::DiffLineType::Binary => "",
		};
		let content = String::from_utf8_lossy(line.content());
		println!("{origin}{content}");
		true
	})?;

	Ok(())
}