From c455f3e316ac1d93fb3c52a330828933749f8981 Mon Sep 17 00:00:00 2001 From: Stephan Dilly Date: Fri, 27 Mar 2020 22:10:56 +0100 Subject: [PATCH] fix working dir checkout (revert) --- Cargo.lock | 8 +- README.md | 1 - asyncgit/Cargo.toml | 2 +- asyncgit/src/sync/mod.rs | 4 +- asyncgit/src/sync/reset.rs | 191 ++++++++++++++++++++++++++++++++++++ asyncgit/src/sync/status.rs | 10 +- asyncgit/src/sync/utils.rs | 58 +++-------- 7 files changed, 222 insertions(+), 52 deletions(-) create mode 100644 asyncgit/src/sync/reset.rs diff --git a/Cargo.lock b/Cargo.lock index 42ac3fdd..12c33416 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -270,9 +270,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.10.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c1af51ea8a906616af45a4ce78eacf25860f7a13ae7bf8a814693f0f4037a26" +checksum = "b7da16ceafe24cedd9ba02c4463a2b506b6493baf4317c79c5acb553134a3c15" dependencies = [ "bitflags", "libc", @@ -379,9 +379,9 @@ checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" [[package]] name = "libgit2-sys" -version = "0.9.2" +version = "0.12.0+0.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4870c781f6063efb83150cd22c1ddf6ecf58531419e7570cdcced46970f64a16" +checksum = "05dff41ac39e7b653f5f1550886cf00ba52f8e7f57210b633cdeedb3de5b236c" dependencies = [ "cc", "libc", diff --git a/README.md b/README.md index b61bb792..6f4325b4 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,6 @@ GITUI_LOGGING=true gitui # todo for 0.1 (first release) -* [ ] fix: reset unstaged file also resets staged hunks * [ ] better help command * [ ] -> fix: dont show scroll option when any popup open * [ ] confirm destructive commands (revert/reset) diff --git a/asyncgit/Cargo.toml b/asyncgit/Cargo.toml index 25b7f420..80da059a 100644 --- a/asyncgit/Cargo.toml +++ b/asyncgit/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT" categories = ["concurrency","asynchronous"] [dependencies] -git2 = "0.10" +git2 = "0.13" rayon-core = "1.7" crossbeam-channel = "0.4" log = "0.4" diff --git a/asyncgit/src/sync/mod.rs b/asyncgit/src/sync/mod.rs index f3b9179d..a1ee689f 100644 --- a/asyncgit/src/sync/mod.rs +++ b/asyncgit/src/sync/mod.rs @@ -1,7 +1,9 @@ //! sync git api pub mod diff; +mod reset; pub mod status; pub mod utils; -pub use utils::{commit, index_reset, stage_add, stage_reset}; +pub use reset::{index_reset, stage_reset}; +pub use utils::{commit, stage_add}; diff --git a/asyncgit/src/sync/reset.rs b/asyncgit/src/sync/reset.rs new file mode 100644 index 00000000..dd353e87 --- /dev/null +++ b/asyncgit/src/sync/reset.rs @@ -0,0 +1,191 @@ +use super::utils::repo_at; +use git2::ObjectType; +use scopetime::scope_time; +use std::{path::Path, process::Command}; + +/// +pub fn stage_reset(path: &Path) -> bool { + stage_reset_at("./", path) +} + +/// +pub fn stage_reset_at(repo_path: &str, path: &Path) -> bool { + scope_time!("stage_reset_at"); + + let repo = repo_at(repo_path); + + let reference = repo.head().unwrap(); + let obj = repo + .find_object( + reference.target().unwrap(), + Some(ObjectType::Commit), + ) + .unwrap(); + + if repo.reset_default(Some(&obj), &[path]).is_ok() { + return true; + } + + false +} + +/// +pub fn index_reset(path: &Path) -> bool { + index_reset_at("./", path) +} + +/// +pub fn index_reset_at(repo_path: &str, path: &Path) -> bool { + let cmd = format!("git checkout {:?}", path); + + let output = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(&["/C", cmd.as_str()]) + .current_dir(repo_path) + .output() + } else { + Command::new("sh") + .arg("-c") + .arg(cmd.as_str()) + .current_dir(repo_path) + .output() + }; + + output.is_ok() + + //------------------------------------ + //TODO: why is this broken with libgit2 ??? + //------------------------------------ + + // scope_time!("index_reset"); + + // let repo = repo_at(repo_path); + + // let mut checkout_opts = CheckoutBuilder::new(); + // checkout_opts + // .remove_untracked(true) + // .force() + // .update_index(false) + // .allow_conflicts(true) + // .path(&path); + + // if repo.checkout_head(Some(&mut checkout_opts)).is_ok() { + // return true; + // } + + // false +} + +#[cfg(test)] +mod tests { + use super::index_reset_at; + use crate::sync::{ + status::{get_index_at, StatusType}, + utils::stage_add_at, + }; + use git2::Repository; + use std::{fs::File, io::Write, path::Path}; + use tempfile::TempDir; + + pub fn repo_init() -> (TempDir, Repository) { + let td = TempDir::new().unwrap(); + let repo = Repository::init(td.path()).unwrap(); + { + let mut config = repo.config().unwrap(); + config.set_str("user.name", "name").unwrap(); + config.set_str("user.email", "email").unwrap(); + + let mut index = repo.index().unwrap(); + let id = index.write_tree().unwrap(); + + let tree = repo.find_tree(id).unwrap(); + let sig = repo.signature().unwrap(); + repo.commit( + Some("HEAD"), + &sig, + &sig, + "initial", + &tree, + &[], + ) + .unwrap(); + } + (td, repo) + } + + static HUNK_A: &str = r" +1 start +2 +3 +4 +5 +6 middle +7 +8 +9 +0 +1 end"; + + static HUNK_B: &str = r" +1 start +2 newa +3 +4 +5 +6 middle +7 +8 +9 +0 newb +1 end"; + + #[test] + fn test_reset_only_unstaged() { + let (_td, repo) = repo_init(); + let root = repo.path().parent().unwrap(); + let repo_path = root.as_os_str().to_str().unwrap(); + + let res = get_index_at(repo_path, StatusType::WorkingDir); + assert_eq!(res.len(), 0); + + let file_path = root.join("bar.txt"); + + { + File::create(&file_path) + .unwrap() + .write_all(HUNK_A.as_bytes()) + .unwrap(); + } + + stage_add_at(repo_path, Path::new("bar.txt")); + + // overwrite with next content + { + File::create(&file_path) + .unwrap() + .write_all(HUNK_B.as_bytes()) + .unwrap(); + } + + assert_eq!( + get_index_at(repo_path, StatusType::Stage).len(), + 1 + ); + assert_eq!( + get_index_at(repo_path, StatusType::WorkingDir).len(), + 1 + ); + + let res = index_reset_at(repo_path, Path::new("bar.txt")); + assert_eq!(res, true); + + assert_eq!( + get_index_at(repo_path, StatusType::Stage).len(), + 1 + ); + assert_eq!( + get_index_at(repo_path, StatusType::WorkingDir).len(), + 0 + ); + } +} diff --git a/asyncgit/src/sync/status.rs b/asyncgit/src/sync/status.rs index 41b5caa7..5239ec97 100644 --- a/asyncgit/src/sync/status.rs +++ b/asyncgit/src/sync/status.rs @@ -64,9 +64,17 @@ impl Into for StatusType { /// pub fn get_index(status_type: StatusType) -> Vec { + get_index_at("./", status_type) +} + +/// +pub fn get_index_at( + repo_path: &str, + status_type: StatusType, +) -> Vec { scope_time!("get_index"); - let repo = utils::repo(); + let repo = utils::repo_at(repo_path); let statuses = repo .statuses(Some( diff --git a/asyncgit/src/sync/utils.rs b/asyncgit/src/sync/utils.rs index 4fec8957..16d7e699 100644 --- a/asyncgit/src/sync/utils.rs +++ b/asyncgit/src/sync/utils.rs @@ -1,16 +1,19 @@ //! sync git api (various methods) -use git2::{ - build::CheckoutBuilder, IndexAddOption, ObjectType, Repository, - RepositoryOpenFlags, -}; +use git2::{IndexAddOption, Repository, RepositoryOpenFlags}; use scopetime::scope_time; use std::path::Path; +//TODO: get rid of this /// pub fn repo() -> Repository { + repo_at("./") +} + +/// +pub fn repo_at(repo_path: &str) -> Repository { let repo = Repository::open_ext( - "./", + repo_path, RepositoryOpenFlags::empty(), Vec::<&Path>::new(), ) @@ -50,9 +53,14 @@ pub fn commit(msg: &str) { /// pub fn stage_add(path: &Path) -> bool { + stage_add_at("./", path) +} + +/// +pub fn stage_add_at(repo_path: &str, path: &Path) -> bool { scope_time!("stage_add"); - let repo = repo(); + let repo = repo_at(repo_path); let mut index = repo.index().unwrap(); @@ -75,41 +83,3 @@ pub fn stage_add(path: &Path) -> bool { false } - -/// -pub fn stage_reset(path: &Path) -> bool { - scope_time!("stage_reset"); - - let repo = repo(); - - let reference = repo.head().unwrap(); - let obj = repo - .find_object( - reference.target().unwrap(), - Some(ObjectType::Commit), - ) - .unwrap(); - - if repo.reset_default(Some(&obj), &[path]).is_ok() { - return true; - } - - false -} - -/// -pub fn index_reset(path: &Path) -> bool { - scope_time!("index_reset"); - - let repo = repo(); - - let mut checkout_opts = CheckoutBuilder::new(); - checkout_opts.remove_untracked(true); - checkout_opts.path(&path).force(); - - if repo.checkout_head(Some(&mut checkout_opts)).is_ok() { - return true; - } - - false -}