// git restore-autosave // - --user and --all-users cannot both be present // - --branch and --all-branches cannot both be present // - if --user or -u is not present, the selected user is the repository signature // - if --branch or -b is not present, the selected branch is the checked out branch // - if --device or -d is present (UUID or hostname), filter to autosaves from that device // - filter to autosaves from the current user (name or email) if --all-users is not present // - filter to autosaves on the current branch // - filter to autosaves after the head commit // - if there is more than one option, enter pick mode // - if there are no options, tell the user to use --all-users or --all-branches or --anytime use std::fmt::Display; use auth_git2::GitAuthenticator; use chrono::Local; use git_autosave::{Autosave, Config, authenticate::Inquirer}; use git2::{RemoteCallbacks, Repository, build::CheckoutBuilder}; struct AutosaveOption { text: String, value: Autosave, } impl Display for AutosaveOption { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.text.fmt(f) } } 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 force = std::env::args().any(|arg| arg == "--force"); let repository = Repository::discover(".")?; let gitconfig = repository.config()?; let repo_id = gitconfig.get_entry("autosave.id")?; let signature = repository.signature()?; let branch = git_autosave::current_branch(&repository)?; let earliest_time = repository.head()?.peel_to_commit()?.time(); 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 mut autosaves = git_autosave::autosaves(&repository)? .into_iter() .filter(|autosave| { all_users || signature .name() .zip(autosave.author.clone()) .is_some_and(|(a, b)| a == b) || signature .email() .zip(autosave.email.clone()) .is_some_and(|(a, b)| a == b) }) .filter(|autosave| all_branches || autosave.branch_name == branch) .filter(|autosave| anytime || autosave.time > earliest_time) .filter(|autosave| all_devices || autosave.repo_id.as_bytes() != repo_id.value_bytes()) .collect::>(); 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, or --anytime to include more options." ); } std::process::exit(1); } let autosave = if autosaves.len() > 1 { let autosaves = autosaves .into_iter() .map(|autosave| { let device = autosave.host_name.as_ref().unwrap_or(&autosave.repo_id); let time = chrono::DateTime::from_timestamp(autosave.time.seconds(), 0) .map(|time| time.with_timezone(&Local)) .map(|time| time.to_rfc2822()) .unwrap_or(autosave.time.seconds().to_string()); let branch = if all_branches { format!(" on {}", &autosave.branch_name) } else { String::new() }; let author = if let Some(author) = autosave.author.as_ref().or(autosave.email.as_ref()) && all_users { format!(" by {author}") } else { String::new() }; AutosaveOption { text: format!("{device} ({time}{branch}{author})"), value: autosave, } }) .collect(); inquire::Select::new("Select an autosave:", autosaves) .prompt()? .value } else { autosaves .pop() .expect("There should be an autosave to select but there isn't. This is a bug!") }; let autosaved_commit = repository.find_commit(autosave.commit_id)?; let workdir = repository.find_tree(git_autosave::workdir_to_tree(&repository)?)?; let new_tree = git_autosave::merge_commit_with_tree(&repository, &autosaved_commit, &workdir)?; git_autosave::save_undo_tree(&repository, &workdir)?; git_autosave::saved_restored_autosave(&repository, &autosave)?; let mut options = CheckoutBuilder::new(); if force { options.force(); } repository.checkout_tree( &repository.find_object(new_tree, Some(git2::ObjectType::Tree))?, Some(&mut options), )?; Ok(()) }