restore: 优化报错 & 编写测试

This commit is contained in:
mrbeanc
2023-12-25 16:31:32 +08:00
parent 9a3d3d72dd
commit 9773a83ae2
3 changed files with 87 additions and 39 deletions

View File

@@ -21,7 +21,7 @@ Git in Rust. 用 `Rust` 实现的mini `Git`. Called `mit`.
- 支持 `mit init`, `mit add`, `mit rm`, `mit commit`
- [x] `init`: 初始化(若仓库已存在,则不执行)
- [x] `init`: 初始化(若仓库已存在,则不执行)- `idempotent`
- [x] `add`: 将变更添加至暂存区(包括新建、修改、删除),可指定文件或目录
- `-A(all)` : 暂存工作区中的所有文件(从根目录开始)变更(新建√ 修改√ 删除√)
- `-u(update)`: 仅对暂存区[`index`]中已跟踪的文件进行操作(新建× 修改√ 删除√)
@@ -55,7 +55,6 @@ Git in Rust. 用 `Rust` 实现的mini `Git`. Called `mit`.
### 名词释义
- 暂存区:`index` or `stage`,保存下一次`commit`需要的的文件快照
- 工作区:`worktree`,用户直接操作的文件夹
- 工作目录:`working directory`,代码仓库的根目录,即`.mit`所在的目录
- 仓库:`repository`,包含`.mit`目录的目录
- 工作目录:`working directory` or `repository`,代码仓库的根目录,即`.mit`所在的目录
- `HEAD`:指向当前`commit`的指针
- 已跟踪:`tracked`,指已经在暂存区[`index`]中的文件(即曾经`add`过的文件)

View File

@@ -68,9 +68,7 @@ pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathB
let input_paths = preprocess_filters(filter); //预处理filter 将None转化为workdir
let target_blobs = preprocess_blobs(target_blobs); //预处理target_blobs 转化为绝对路径HashMap
//TODO 输出不存在于targetworktree文件
let deleted_files = get_worktree_deleted_files_in_filters(&input_paths, &target_blobs); //统计所有目录中已删除的文件
let deleted_files = get_worktree_deleted_files_in_filters(&input_paths, &target_blobs); //统计已删除的文件
let mut file_paths = util::integrate_paths(&input_paths); //根据用户输入整合存在的文件(绝对路径)
file_paths.extend(deleted_files); //已删除的文件
@@ -86,9 +84,8 @@ pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathB
//文件存在于target_commit (deleted),需要恢复
store.restore_to_file(&target_blobs[path], &path);
} else {
//在target_commit和workdir中都不存在(非法路径)
// println!("fatal: pathspec '{}' did not match any files", path.display());
// TODO 如果是用户输入的路径才应该报错integrate_paths产生的不应该报错
//在target_commit和workdir中都不存在(非法路径) 用户输入
println!("fatal: pathspec '{}' did not match any files", path.display());
}
} else {
//文件存在有两种情况1.修改 2.新文件
@@ -116,7 +113,7 @@ pub fn restore_index(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathBuf,
let target_blobs = preprocess_blobs(target_blobs); //预处理target_blobs 转化为绝对路径HashMap
let mut index = Index::new();
let deleted_files_index = get_index_deleted_files_in_filters(&index, &input_paths, &target_blobs); //统计所有目录中已删除的文件
let deleted_files_index = get_index_deleted_files_in_filters(&index, &input_paths, &target_blobs); //统计已删除的文件
//1.获取index中包含于input_path的文件使用paths进行过滤
let mut file_paths: HashSet<PathBuf> = util::filter_to_fit_paths(&index.get_tracked_files(), &input_paths);
@@ -133,8 +130,7 @@ pub fn restore_index(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathBuf,
index.add(path.clone(), FileMetaData { hash: target_blobs[path].clone(), ..Default::default() });
} else {
//在target_commit和index中都不存在(非法路径)
// println!("fatal: pathspec '{}' did not match any files", path.display());
// TODO 如果是用户输入的路径才应该报错integrate_paths产生的不应该报错
println!("fatal: pathspec '{}' did not match any files", path.display());
}
} else {
//文件存在于index有两种情况1.修改 2.新文件
@@ -224,18 +220,46 @@ pub fn restore(paths: Vec<String>, source: Option<String>, worktree: bool, stage
#[cfg(test)]
mod test {
use std::fs;
//TODO 写测试!
use std::path::PathBuf;
use crate::{commands, models::index::Index, utils::util};
use crate::commands::add::add;
use crate::commands::restore::restore;
use crate::commands::status;
use crate::{models::index::Index, utils::util};
#[test]
fn test_restore_stage() {
util::setup_test_with_clean_mit();
util::setup_test_with_empty_workdir();
let path = PathBuf::from("a.txt");
util::ensure_no_file(&path);
commands::add::add(vec![], true, false);
super::restore(vec![".".to_string()], Some("HEAD".to_string()), false, true);
add(vec![], true, false); //add -A
restore(vec![".".to_string()], Some("HEAD".to_string()), false, true);
let index = Index::new();
assert!(index.get_tracked_files().is_empty());
}
#[test]
fn test_restore_worktree() {
util::setup_test_with_empty_workdir();
let files = vec!["a.txt", "b.txt", "c.txt", "test/in.txt"];
util::ensure_test_files(&files);
add(vec![], true, false);
assert_eq!(status::changes_to_be_committed().new.iter().count(), 4);
restore(vec!["c.txt".to_string()], None, false, true); //restore c.txt --staged
assert_eq!(status::changes_to_be_committed().new.iter().count(), 3);
assert_eq!(status::changes_to_be_staged().new.iter().count(), 1);
fs::remove_file("a.txt").unwrap(); //删除a.txt
fs::remove_dir_all("test").unwrap(); //删除test文件夹
assert_eq!(status::changes_to_be_staged().deleted.iter().count(), 2);
restore(vec![".".to_string()], None, true, false); //restore . //from index
assert_eq!(status::changes_to_be_committed().new.iter().count(), 3);
assert_eq!(status::changes_to_be_staged().new.iter().count(), 1);
assert_eq!(status::changes_to_be_staged().deleted.iter().count(), 0);
}
}

View File

@@ -3,7 +3,6 @@ use std::{
collections::HashSet,
fs, io,
io::Write,
option,
path::{Path, PathBuf},
};
@@ -13,29 +12,30 @@ pub const ROOT_DIR: &str = ".mit";
pub const TEST_DIR: &str = "mit_test_storage"; // 执行测试的储存库
/* tools for test */
fn find_cargo_dir() -> PathBuf {
let cargo_path = std::env::var("CARGO_MANIFEST_DIR");
if cargo_path.is_err() {
// vscode DEBUG test没有CARGO_MANIFEST_DIR宏手动尝试查找cargo.toml
let mut path = cur_dir();
loop {
path.push("Cargo.toml");
if path.exists() {
break;
}
if !path.pop() {
panic!("找不到CARGO_MANIFEST_DIR");
}
}
path.pop();
path
} else {
PathBuf::from(cargo_path.unwrap())
}
}
fn setup_test_dir() {
color_backtrace::install(); // colorize backtrace
let cargo_path = std::env::var("CARGO_MANIFEST_DIR");
let path: PathBuf = {
if cargo_path.is_err() {
// vscode DEBUG test没有CARGO_MANIFEST_DIR宏手动尝试查找cargo.toml
let mut path = std::env::current_dir().unwrap();
loop {
path.push("Cargo.toml");
if path.exists() {
break;
}
if !path.pop() {
panic!("找不到CARGO_MANIFEST_DIR");
}
}
path.pop();
path
} else {
PathBuf::from(cargo_path.unwrap())
}
};
let mut path = PathBuf::from(path);
let mut path = find_cargo_dir();
path.push(TEST_DIR);
if !path.exists() {
fs::create_dir(&path).unwrap();
@@ -59,7 +59,32 @@ pub fn setup_test_without_mit() {
}
}
pub fn ensure_test_file(path: &Path, content: option::Option<&str>) {
pub fn ensure_test_files<T: AsRef<str>>(paths: &Vec<T>) {
for path in paths {
ensure_test_file(path.as_ref().as_ref(), None);
}
}
pub fn ensure_empty_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
let entries = fs::read_dir(path.as_ref())?;
for entry in entries {
let path = entry?.path();
if path.is_dir() {
fs::remove_dir_all(&path)?; // 如果是目录,则递归删除
} else {
fs::remove_file(&path)?; // 如果是文件,则直接删除
}
}
Ok(())
}
pub fn setup_test_with_empty_workdir() {
let test_dir = find_cargo_dir().join(TEST_DIR);
ensure_empty_dir(&test_dir).unwrap();
setup_test_with_clean_mit();
}
pub fn ensure_test_file(path: &Path, content: Option<&str>) {
// 以测试目录为根目录,创建文件
fs::create_dir_all(path.parent().unwrap()).unwrap(); // ensure父目录
let mut file =