diff options
| author | Mica White <botahamec@outlook.com> | 2026-04-04 09:15:15 -0400 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2026-04-04 09:15:15 -0400 |
| commit | 830a7ad5a468cbc9a8daa519d461962c0cf44b06 (patch) | |
| tree | d53c7853c0f8b9bfa340fef66c497f980f1d36d7 /src | |
| parent | 86c4f7743a0a3835d595cb32af7eafdc41f2be34 (diff) | |
Basic diff command
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/git-diff-autosave.rs | 81 | ||||
| -rw-r--r-- | src/bin/git-restore-autosave.rs | 85 | ||||
| -rw-r--r-- | src/inquire.rs | 103 | ||||
| -rw-r--r-- | src/lib.rs | 1 |
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!")) + } +} @@ -28,6 +28,7 @@ use uuid::Uuid; pub mod authenticate; pub mod config; +pub mod inquire; pub mod utils; pub use config::Config; |
