summaryrefslogtreecommitdiff
path: root/src/bin/git-restore-autosave.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/git-restore-autosave.rs')
-rw-r--r--src/bin/git-restore-autosave.rs128
1 files changed, 128 insertions, 0 deletions
diff --git a/src/bin/git-restore-autosave.rs b/src/bin/git-restore-autosave.rs
new file mode 100644
index 0000000..51cc14c
--- /dev/null
+++ b/src/bin/git-restore-autosave.rs
@@ -0,0 +1,128 @@
+// 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, 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 anytime = std::env::args().any(|arg| arg == "--anytime");
+ let force = std::env::args().any(|arg| arg == "--force");
+
+ let repository = Repository::discover(".")?;
+ let signature = repository.signature()?;
+ let branch = git_autosave::current_branch(&repository)?;
+ let earliest_time = repository.head()?.peel_to_commit()?.time();
+
+ let gitconfig = repository.config()?;
+ let config: &'static _ = Box::leak(Box::new(git_autosave::load_config()?));
+ 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)
+ .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, 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(())
+}