summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bin/git-diff-autosave.rs81
-rw-r--r--src/bin/git-restore-autosave.rs85
-rw-r--r--src/inquire.rs103
-rw-r--r--src/lib.rs1
4 files changed, 202 insertions, 68 deletions
diff --git a/src/bin/git-diff-autosave.rs b/src/bin/git-diff-autosave.rs
new file mode 100644
index 0000000..63aa130
--- /dev/null
+++ b/src/bin/git-diff-autosave.rs
@@ -0,0 +1,81 @@
+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(())
+}
diff --git a/src/bin/git-restore-autosave.rs b/src/bin/git-restore-autosave.rs
index a8fdb9a..482e2b5 100644
--- a/src/bin/git-restore-autosave.rs
+++ b/src/bin/git-restore-autosave.rs
@@ -10,24 +10,10 @@
// - 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 git_autosave::{Config, authenticate::Inquirer, inquire::AutosaveFilters};
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");
@@ -48,23 +34,21 @@ fn main() -> Result<(), anyhow::Error> {
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.as_bytes())
- .collect::<Vec<_>>();
+ 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.");
@@ -76,42 +60,7 @@ fn main() -> Result<(), anyhow::Error> {
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 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 =
diff --git a/src/inquire.rs b/src/inquire.rs
new file mode 100644
index 0000000..6dd2e3c
--- /dev/null
+++ b/src/inquire.rs
@@ -0,0 +1,103 @@
+use std::fmt::Display;
+
+use chrono::Local;
+use git2::{Signature, Time};
+
+use crate::Autosave;
+
+pub struct AutosaveFilters<'a> {
+ pub signature: &'a Signature<'a>,
+ pub branch: &'a str,
+ pub earliest_time: Time,
+ pub repo_id: &'a str,
+ pub all_users: bool,
+ pub all_branches: bool,
+ pub anytime: bool,
+ pub all_devices: bool,
+}
+
+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)
+ }
+}
+
+pub fn filter_autosaves(
+ autosaves: impl IntoIterator<Item = Autosave>,
+ filters: AutosaveFilters<'_>,
+) -> impl Iterator<Item = Autosave> {
+ let AutosaveFilters {
+ all_users,
+ signature,
+ all_branches,
+ branch,
+ anytime,
+ earliest_time,
+ all_devices,
+ repo_id,
+ } = filters;
+ autosaves
+ .into_iter()
+ .filter(move |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(move |autosave| all_branches || autosave.branch_name == branch)
+ .filter(move |autosave| anytime || autosave.time > earliest_time)
+ .filter(move |autosave| all_devices || autosave.repo_id.as_bytes() != repo_id.as_bytes())
+}
+
+pub fn select_autosave(
+ mut autosaves: Vec<Autosave>,
+ all_branches: bool,
+ all_users: bool,
+) -> Result<Autosave, inquire::InquireError> {
+ 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();
+ Ok(inquire::Select::new("Select an autosave:", autosaves)
+ .prompt()?
+ .value)
+ } else {
+ Ok(autosaves
+ .pop()
+ .expect("There should be an autosave to select but there isn't. This is a bug!"))
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index c876775..91d82fc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -28,6 +28,7 @@ use uuid::Uuid;
pub mod authenticate;
pub mod config;
+pub mod inquire;
pub mod utils;
pub use config::Config;