diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 8d5a1b2..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 197acc2..ba94ac3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,27 +2,12 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "cc" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" -dependencies = [ - "jobserver", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -60,7 +45,6 @@ version = "0.1.0" dependencies = [ "dialoguer", "fs_extra", - "git2", "indicatif", "serde", "strum", @@ -82,52 +66,18 @@ dependencies = [ "instant", ] -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - [[package]] name = "fs_extra" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" -[[package]] -name = "git2" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be36bc9e0546df253c0cc41fd0af34f5e92845ad8509462ec76672fac6997f5b" -dependencies = [ - "bitflags", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", -] - [[package]] name = "heck" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "indicatif" version = "0.17.3" @@ -149,15 +99,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "jobserver" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" -dependencies = [ - "libc", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -170,92 +111,12 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" -[[package]] -name = "libgit2-sys" -version = "0.14.1+1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a07fb2692bc3593bda59de45a502bb3071659f2c515e28c71e728306b038e17" -dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", -] - -[[package]] -name = "libssh2-sys" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - [[package]] name = "number_prefix" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - [[package]] name = "portable-atomic" version = "0.3.19" @@ -377,21 +238,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - [[package]] name = "toml" version = "0.5.11" @@ -401,50 +247,18 @@ dependencies = [ "serde", ] -[[package]] -name = "unicode-bidi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046be40136ef78dc325e0edefccf84ccddacd0afcc1ca54103fa3c61bbdab1d" - [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index b827b33..ed54619 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [dependencies] dialoguer = "0.10.3" indicatif = "0.17.3" -git2 = "0.16.0" strum = { version = "0.24.1", features = ["derive"] } serde = { version = "1.0.152", features = ["derive"] } toml = "0.5.10" diff --git a/src/main.rs b/src/main.rs index 8939a4e..40afaa6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use strum::{EnumIter, IntoEnumIterator}; enum MainMenu { Install, Collect, + Pull, Diff, Upload, Quit @@ -18,8 +19,9 @@ enum MainMenu { impl ToString for MainMenu { fn to_string(&self) -> String { match self { - MainMenu::Install => "Pull and install files", + MainMenu::Install => "Install files", MainMenu::Collect => "Collect files", + MainMenu::Pull => "Pull from git", MainMenu::Diff => "View git diff", MainMenu::Upload => "(Commit) and push to git", MainMenu::Quit => "Exit" @@ -28,14 +30,10 @@ impl ToString for MainMenu { } fn main() { - let repo = match repository::open_repo() { - Ok(v) => v, - Err(err) => { - println!("Failed to open/clone repo!"); - println!("{}", err); - return; - } - }; + if !repository::open_repo() { + println!("Failed to open/clone repo!"); + return; + } if !std::path::Path::new("repo/mod.toml").exists() { println!("'mod.toml' doesn't exist!"); @@ -54,17 +52,11 @@ fn main() { 'main_loop: loop { let res = prompt::select(Some("What do you want to do?"), MainMenu::iter().collect()); match res { - MainMenu::Install => { - if let Err(err) = repository::pull_repo(&repo) { - println!("Failed to pull repo!"); - println!("{}", err); - return; - } - operations::install(&mods); - }, + MainMenu::Install => operations::install(&mods), MainMenu::Collect => operations::collect(&mods), - MainMenu::Diff => { std::process::Command::new("git").arg("diff").current_dir("repo").spawn().unwrap().wait().unwrap(); }, - MainMenu::Upload => operations::upload(&repo), + MainMenu::Pull => repository::pull_repo(), + MainMenu::Diff => repository::diff(), + MainMenu::Upload => operations::upload(), MainMenu::Quit => break 'main_loop } } diff --git a/src/operations.rs b/src/operations.rs index 6a46ac9..78795a7 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -104,10 +104,15 @@ pub fn collect(config: &ModToml) { } } -pub fn upload(repo: &git2::Repository) { - if !crate::repository::is_clean(repo) { +pub fn upload() { + if !crate::repository::is_clean() { let msg = crate::prompt::input("Commit message"); - crate::repository::commit_changes(repo, msg); + if !crate::repository::commit_changes(&msg) { + println!("Failed to commit"); + return; + } + } + if !crate::repository::push_repo() { + println!("Failed to push to origin"); } - crate::repository::push_repo(repo).expect("Failed to push to origin"); } diff --git a/src/repository.rs b/src/repository.rs index 444407a..4ccfaaf 100644 --- a/src/repository.rs +++ b/src/repository.rs @@ -1,131 +1,90 @@ use std::path::Path; -use git2::Repository; +use std::process::{Command, ExitStatus}; +use std::io::Result; -fn generate_callbacks(use_ssh_agent: bool, pb: &mut indicatif::ProgressBar) -> git2::RemoteCallbacks { - let mut callbacks = git2::RemoteCallbacks::new(); +trait HadSuccess { + fn success(self) -> bool; +} - if use_ssh_agent { - callbacks.credentials(|_, username, _| git2::Cred::ssh_key_from_agent(username.unwrap())); - } else { - callbacks.credentials(|_, username, _| git2::Cred::ssh_key( - username.unwrap(), - None, - Path::new(&format!("{}/.ssh/id_rsa", std::env::var("HOME").unwrap_or_default())), - None - )); +impl HadSuccess for Result { + fn success(self) -> bool { + self.map_or(false, |status| status.success()) } +} - callbacks.transfer_progress(|progress| { - if progress.total_deltas() == 0 { - pb.set_length(progress.total_objects().max(1) as u64); - pb.set_position(progress.received_objects() as u64); - pb.set_message("Receiving objects"); +impl HadSuccess for &mut Command { + fn success(self) -> bool { + self.status().success() + } +} + +macro_rules! git { + () => { Command::new("git").current_dir(Path::new("repo")) }; + ([], $arg:expr) => { git!().arg($arg) }; + ([], $arg:expr, $($args:expr),+) => { git!([], $($args),+).arg($arg) }; + ([$first:expr $(, $rest:expr)*], $($reversed:expr),*) => { git!([$($rest),*], $first $(, $reversed)*) }; + ($($args:expr),+) => { git!([$($args),+],) }; +} + +fn check_or_set_config(key: &str, value: &str) -> bool { + if let Ok(status) = git!("config", "--local", "--get", key).status() { + if status.success() { + true + } else if status.code().unwrap_or(2) == 1 { + git!("config", "--local", key, value).success() } else { - pb.set_length(progress.total_deltas().max(1) as u64); - pb.set_position(progress.total_objects() as u64); - pb.set_message("Resolving deltas"); + false } - true - }); - callbacks.push_transfer_progress(|cur, tot, _| { - pb.set_length(tot.max(1) as u64); - pb.set_position(cur as u64); - }); - - callbacks -} - -fn _clone_repo(callbacks: git2::RemoteCallbacks) -> Result { - let mut fo = git2::FetchOptions::new(); - fo.remote_callbacks(callbacks); - - let mut builder = git2::build::RepoBuilder::new(); - builder.fetch_options(fo); - - builder.clone( - "git@ssh.gitlab.mattv.de:root/dotfiles.git", - Path::new("repo") - ) -} - -fn clone_repo() -> Result { - println!("Cloning repo..."); - let mut pb = indicatif::ProgressBar::new(1); - pb.set_style(indicatif::ProgressStyle::with_template("{spinner} [{wide_bar}] {pos:>7}/{len:7} {msg}").unwrap().progress_chars("#>-")); - _clone_repo(generate_callbacks(true, &mut pb)) - .or_else(|_| _clone_repo(generate_callbacks(false, &mut pb))) -} - -fn _pull_repo(repo: &Repository, callbacks: git2::RemoteCallbacks) -> Result<(), git2::Error> { - let mut fo = git2::FetchOptions::new(); - fo.remote_callbacks(callbacks); - - let mut co = git2::build::CheckoutBuilder::new(); - co.force(); - - repo.find_remote("origin").unwrap().fetch(&["main"], Some(&mut fo), None)?; - - let mut commits = vec![]; - repo.fetchhead_foreach(|name, url, oid, was_merge| if was_merge { commits.push(repo.annotated_commit_from_fetchhead(name, &String::from_utf8_lossy(url), oid)); true} else { true })?; - let commits = commits.into_iter().collect::, git2::Error>>()?; - if commits.is_empty() { - panic!("Failed to get heads from origin!"); + } else { + false } - - repo.merge(commits.iter().collect::>().as_slice(), None, Some(&mut co)) } -pub fn pull_repo(repo: &Repository) -> Result<(), git2::Error> { +fn check_config() -> bool { + check_or_set_config("user.name", "Mutzi") + && check_or_set_config("user.email", "root@mattv.de") +} + +fn clone_repo() -> bool { + println!("Cloning repo..."); + if !Path::new("repo").exists() { std::fs::create_dir("repo").unwrap(); } + git!("clone", "git@ssh.gitlab.mattv.de:root/dotfiles.git", ".").success() + && check_config() +} + +pub fn pull_repo() { println!("Pulling repo..."); - let mut pb = indicatif::ProgressBar::new(1); - pb.set_style(indicatif::ProgressStyle::with_template("{spinner} [{wide_bar}] {pos:>7}/{len:7} {msg}").unwrap().progress_chars("#>-")); - _pull_repo(repo, generate_callbacks(true, &mut pb)) - .or_else(|_| _pull_repo(repo, generate_callbacks(false, &mut pb))) + git!("pull").success(); } -fn _push_repo(repo: &Repository, callbacks: git2::RemoteCallbacks) -> Result<(), git2::Error> { - let mut pu = git2::PushOptions::new(); - pu.remote_callbacks(callbacks); - - let mut remote = repo.find_remote("origin").unwrap(); - - remote.push::<&str>(&["refs/heads/main:refs/heads/main"], Some(&mut pu)) -} - -pub fn push_repo(repo: &Repository) -> Result<(), git2::Error> { +pub fn push_repo() -> bool { println!("Pushing repo..."); - let mut pb = indicatif::ProgressBar::new(1); - pb.set_style(indicatif::ProgressStyle::with_template("{spinner} [{wide_bar}] {bytes}/{total_bytes} {msg}").unwrap().progress_chars("#>-")); - _push_repo(repo, generate_callbacks(true, &mut pb)) - .or_else(|_| _push_repo(repo, generate_callbacks(false, &mut pb))) + git!("push").success() } -pub fn open_repo() -> Result { - Repository::open("repo") - .or_else(|_| clone_repo()) - .map_err(|e| e.message().to_string()) +pub fn open_repo() -> bool { + Path::new("repo/.git").exists() || clone_repo() } -pub fn is_clean(repo: &Repository) -> bool { - let mut so = git2::StatusOptions::new(); - so.show(git2::StatusShow::IndexAndWorkdir); - so.include_untracked(true); - so.include_ignored(false); - so.include_unmodified(false); - - repo.statuses(Some(&mut so)).map_or(false, |s| s.is_empty()) +pub fn is_clean() -> bool { + if let Ok(output) = git!("status", "--porcelain").output() { + if output.status.success() { + output.stdout.is_empty() + } else { + println!("Failed to get status:\n{}", String::from_utf8(output.stderr).unwrap()); + false + } + } else { + println!("Failed to get status"); + false + } } -pub fn commit_changes(repo: &Repository, msg: String) { - let sig = git2::Signature::now("Mutzi", "root@mattv.de").unwrap(); - - let mut index = repo.index().unwrap(); - index.add_all(["*"], git2::IndexAddOption::DEFAULT, None).unwrap(); - - let tree_id = index.write_tree().unwrap(); - let tree = repo.find_tree(tree_id).unwrap(); - let parent_id = repo.refname_to_id("HEAD").unwrap(); - let parent = repo.find_commit(parent_id).unwrap(); - - repo.commit(Some("HEAD"), &sig, &sig, msg.as_str(), &tree, &[&parent]).unwrap(); +pub fn commit_changes(msg: &str) -> bool { + git!("add", ".").success() + && git!("commit", "-m", msg).success() +} + +pub fn diff() { + git!("diff").success(); }