diff --git a/asyncgit/src/sync/reset.rs b/asyncgit/src/sync/reset.rs index 876de398..bc008ec7 100644 --- a/asyncgit/src/sync/reset.rs +++ b/asyncgit/src/sync/reset.rs @@ -1,7 +1,7 @@ use super::utils::repo; -use git2::{build::CheckoutBuilder, ObjectType}; +use git2::{build::CheckoutBuilder, ObjectType, Status}; use scopetime::scope_time; -use std::path::Path; +use std::{fs, path::Path}; /// pub fn reset_stage(repo_path: &str, path: &Path) -> bool { @@ -30,25 +30,48 @@ pub fn reset_workdir(repo_path: &str, path: &Path) -> bool { let repo = repo(repo_path); - let mut checkout_opts = CheckoutBuilder::new(); - checkout_opts - .remove_untracked(true) - .force() - .update_index(false) - .path(&path); + // Note: early out for removing untracked files, due to bug in checkout_head code: + // see https://github.com/libgit2/libgit2/issues/5089 + if let Ok(status) = repo.status_file(&path) { + dbg!(status); - //first reset working dir file - repo.checkout_head(Some(&mut checkout_opts)).unwrap(); + let removed_file_wd = if status == Status::WT_NEW + || (status == Status::WT_MODIFIED | Status::INDEX_NEW) + { + fs::remove_file(Path::new(repo_path).join(path)).is_ok() + } else { + false + }; - let mut checkout_opts = CheckoutBuilder::new(); - checkout_opts - .update_index(true) // windows: needs this to be true WTF?! - .path(&path); + if status == Status::WT_NEW { + return removed_file_wd; + } - // now reset staged changes back to working dir - repo.checkout_index(None, Some(&mut checkout_opts)).unwrap(); + if status != Status::WT_MODIFIED | Status::INDEX_NEW { + let mut checkout_opts = CheckoutBuilder::new(); + checkout_opts + .remove_untracked(true) + .force() + .update_index(false) + .path(&path); - true + //first reset working dir file + repo.checkout_head(Some(&mut checkout_opts)).unwrap(); + } + + let mut checkout_opts = CheckoutBuilder::new(); + checkout_opts + .update_index(true) // windows: needs this to be true WTF?! + .allow_conflicts(true) + .path(&path); + + // now reset staged changes back to working dir + repo.checkout_index(None, Some(&mut checkout_opts)).unwrap(); + + true + } else { + false + } } #[cfg(test)] @@ -59,7 +82,11 @@ mod tests { tests::{debug_cmd_print, repo_init}, utils::stage_add, }; - use std::{fs::File, io::Write, path::Path}; + use std::{ + fs::{self, File}, + io::Write, + path::Path, + }; static HUNK_A: &str = r" 1 start @@ -138,4 +165,36 @@ mod tests { 0 ); } + + #[test] + fn test_reset_untracked_in_subdir() { + let (_td, repo) = repo_init(); + let root = repo.path().parent().unwrap(); + let repo_path = root.as_os_str().to_str().unwrap(); + + { + fs::create_dir(&root.join("foo")).unwrap(); + File::create(&root.join("foo/bar.txt")) + .unwrap() + .write_all(b"test\nfoo") + .unwrap(); + } + + debug_cmd_print(repo_path, "git status"); + + assert_eq!( + get_status(repo_path, StatusType::WorkingDir).len(), + 1 + ); + + let res = reset_workdir(repo_path, Path::new("foo/bar.txt")); + assert_eq!(res, true); + + debug_cmd_print(repo_path, "git status"); + + assert_eq!( + get_status(repo_path, StatusType::WorkingDir).len(), + 0 + ); + } }