清理警告,将restore功能转入Blob

This commit is contained in:
mrbeanc
2023-12-29 14:10:42 +08:00
parent 4d69273293
commit ebbc134117
12 changed files with 62 additions and 138 deletions

View File

@@ -2,7 +2,7 @@ use colored::Colorize;
use crate::{
models::*,
utils::{store, util},
utils::{util, Store},
};
// branch error
@@ -21,7 +21,7 @@ fn search_hash(commit_hash: Hash) -> Option<Hash> {
return Some(commit_hash);
}
// commit hash
let store = store::Store::new();
let store = Store::new();
let commit = store.search(&commit_hash);
commit
}

View File

@@ -1,7 +1,7 @@
use crate::{
commands::{self, status::*},
models::{head, Commit, Hash},
utils::{store, util},
utils::{util, Store},
};
enum MergeErr {
@@ -65,7 +65,7 @@ pub fn merge(branch: String) {
head::get_branch_head(&branch)
} else {
// Commit Hash, e.g. a1b2c3d4
let store = store::Store::new();
let store = Store::new();
let commit = store.search(&branch);
if commit.is_none() || !util::is_typeof_commit(commit.clone().unwrap()) {
println!("fatal: 非法的 commit hash: '{}'", branch);

View File

@@ -6,7 +6,7 @@ use std::{
use crate::{
models::*,
utils::{store, util},
utils::{util, Store},
};
/// 统计[工作区]中相对于target_blobs已删除的文件根据filters进行过滤
@@ -68,7 +68,6 @@ pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathB
file_paths.extend(deleted_files); //已删除的文件
let index = Index::get_instance();
let store = store::Store::new();
for path in &file_paths {
assert!(path.is_absolute()); // 绝对路径
@@ -76,7 +75,7 @@ pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathB
//文件不存在于workdir
if target_blobs.contains_key(path) {
//文件存在于target_commit (deleted),需要恢复
store.restore_to_file(&target_blobs[path], &path);
Blob::load(&target_blobs[path]).restore(&path);
} else {
//在target_commit和workdir中都不存在(非法路径) 用户输入
println!("fatal: pathspec '{}' did not match any files", path.display());
@@ -87,7 +86,7 @@ pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathB
//文件已修改(modified)
let file_hash = util::calc_file_hash(&path); //TODO tree没有存修改时间所以这里只能用hash判断
if file_hash != target_blobs[path] {
store.restore_to_file(&target_blobs[path], &path);
Blob::load(&target_blobs[path]).restore(&path);
}
} else {
//新文件也分两种情况1.已跟踪,需要删除 2.未跟踪,保留
@@ -167,7 +166,7 @@ pub fn restore(paths: Vec<String>, source: Option<String>, worktree: bool, stage
head::get_branch_head(&src) // "" if not exist
} else {
// [Commit Hash, e.g. a1b2c3d4] || [Wrong Branch Name]
let store = store::Store::new();
let store = Store::new();
let commit = store.search(&src);
if commit.is_none() || !util::is_typeof_commit(commit.clone().unwrap()) {
println!("fatal: 非法的 commit hash: '{}'", src);
@@ -251,5 +250,7 @@ mod test {
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);
assert!(test::is_file_exist("a.txt"));
assert!(test::is_file_exist("test/in.txt"));
}
}

View File

@@ -2,7 +2,7 @@ use colored::Colorize;
use crate::{
models::{head, Commit, Hash},
utils::{store, util},
utils::{util, Store},
};
use super::{
@@ -41,7 +41,7 @@ fn switch_to(branch: String, detach: bool) -> Result<(), SwitchErr> {
return Err(SwitchErr::NoClean);
}
let store = store::Store::new();
let store = Store::new();
if head::list_local_branches().contains(&branch) {
// 切到分支
let branch_commit = head::get_branch_head(&branch);

View File

@@ -1,6 +1,6 @@
use crate::{
models::Hash,
utils::{store, util},
utils::{util, Store},
};
use std::{fs, path::Path};
@@ -16,22 +16,29 @@ pub struct Blob {
impl Blob {
/// 从源文件新建blob对象并直接保存到/objects/中
pub fn new(file: &Path) -> Blob {
let data = fs::read_to_string(file).expect(format!("无法读取文件:{:?}", file).as_str());
let data = fs::read_to_string(file).expect("无法读取文件");
let hash = util::calc_hash(&data);
let blob = Blob { hash, data };
blob.save();
blob
}
/// 通过hash值加载blob从/objects/
#[allow(dead_code)]
pub fn load(hash: &String) -> Blob {
let s = store::Store::new();
let s = Store::new();
let data = s.load(hash);
Blob { hash: hash.clone(), data }
}
///将hash对应的blob还原到file
pub fn restore(&self, file: &Path) {
util::write(file, &self.data).unwrap();
}
/// 写入文件;优化:文件已存在时不做操作
pub fn save(&self) {
let s = store::Store::new();
let s = Store::new();
if !s.contains(&self.hash) {
let hash = s.save(&self.data);
assert_eq!(hash, self.hash);

View File

@@ -2,7 +2,7 @@ use std::time::SystemTime;
use serde::{Deserialize, Serialize};
use crate::utils::{store, util};
use crate::utils::{util, Store};
use super::*;
/*Commit
@@ -29,7 +29,8 @@ impl Commit {
pub fn get_date(&self) -> String {
util::format_time(&self.date)
}
pub fn get_tree_hash(&self) -> String {
#[allow(dead_code)]
pub fn get_tree_hash(&self) -> Hash {
self.tree.clone()
}
pub fn get_tree(&self) -> Tree {
@@ -44,6 +45,7 @@ impl Commit {
pub fn get_author(&self) -> String {
self.author.clone()
}
#[allow(dead_code)]
pub fn get_committer(&self) -> String {
self.committer.clone()
}
@@ -63,7 +65,7 @@ impl Commit {
}
pub fn load(hash: &String) -> Commit {
let s = store::Store::new();
let s = Store::new();
let commit_data = s.load(hash);
let mut commit: Commit = serde_json::from_str(&commit_data).unwrap();
commit.hash = hash.clone();
@@ -72,7 +74,7 @@ impl Commit {
pub fn save(&mut self) -> String {
// unimplemented!()
let s = store::Store::new();
let s = Store::new();
let commit_data = serde_json::to_string_pretty(&self).unwrap();
let hash = s.save(&commit_data);
self.hash = hash.clone();

View File

@@ -68,6 +68,7 @@ impl Index {
}
/// 重置index主要用于测试防止单例模式的影响
#[cfg(test)]
pub fn reload() {
let index = Index::get_instance();
index.load();
@@ -115,17 +116,6 @@ impl Index {
self.contains(path)
}
/// 与暂存区比较,获取工作区中被删除的文件
pub fn get_deleted_files(&self, dir: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
self.entries.keys().for_each(|file| {
if !file.exists() && util::is_sub_path(file, dir) {
files.push(file.clone());
}
});
files
}
/// 与暂存区比较确定文件自上次add以来是否被编辑内容不一定修改还需要算hash
pub fn is_modified(&self, file: &Path) -> bool {
if let Some(self_data) = self.get(file) {
@@ -201,6 +191,7 @@ impl Index {
self.entries.clone()
}
#[cfg(test)]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}

View File

@@ -2,7 +2,7 @@ use std::{collections::HashSet, path::PathBuf};
use serde::{Deserialize, Serialize};
use crate::utils::{store, util};
use crate::utils::{util, Store};
use super::{Hash, Index};
/*Tree
@@ -92,7 +92,7 @@ impl Tree {
}
pub fn load(hash: &String) -> Tree {
let s = store::Store::new();
let s = Store::new();
let tree_data = s.load(hash);
let mut tree: Tree = serde_json::from_str(&tree_data).unwrap();
tree.hash = hash.clone();
@@ -100,35 +100,13 @@ impl Tree {
}
pub fn save(&mut self) -> String {
let s = store::Store::new();
let s = Store::new();
let tree_data = serde_json::to_string_pretty(&self).unwrap();
let hash = s.save(&tree_data);
self.hash = hash.clone();
hash
}
/**递归获取Tree对应的所有文件 */
pub fn get_recursive_file_entries(&self) -> Vec<PathBuf> {
let mut files = Vec::new();
for entry in self.entries.iter() {
if entry.filemode.0 == "blob" {
files.push(PathBuf::from(entry.name.clone()));
} else {
let sub_tree = Tree::load(&entry.object_hash);
let sub_files = sub_tree.get_recursive_file_entries();
files.append(
sub_files
.iter()
.map(|file| PathBuf::from(entry.name.clone()).join(file))
.collect::<Vec<PathBuf>>()
.as_mut(),
);
}
}
files
}
///注:相对路径(to workdir)
pub fn get_recursive_blobs(&self) -> Vec<(PathBuf, Hash)> {
//TODO 返回HashMap
@@ -157,10 +135,7 @@ impl Tree {
mod test {
use std::path::PathBuf;
use crate::{
models::*,
utils::{test, util},
};
use crate::{models::*, utils::test};
#[test]
fn test_new() {
@@ -174,8 +149,8 @@ mod test {
}
let tree = Tree::new(&index);
assert!(tree.entries.len() == 3);
assert!(tree.hash.len() != 0);
assert_eq!(tree.entries.len(), 3);
assert_ne!(tree.hash.len(), 0);
}
#[test]
@@ -193,37 +168,9 @@ mod test {
let tree_hash = tree.get_hash();
let loaded_tree = Tree::load(&tree_hash);
assert!(loaded_tree.entries.len() == tree.entries.len());
assert!(tree.entries[0].name == loaded_tree.entries[0].name);
assert!(tree.entries[1].name == loaded_tree.entries[1].name);
}
#[test]
fn test_get_recursive_file_entries() {
test::setup_with_clean_mit();
let index = Index::get_instance();
let mut test_files = vec![PathBuf::from("b.txt"), PathBuf::from("mit_src/a.txt")];
for test_file in test_files.clone() {
test::ensure_file(&test_file, None);
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
}
let tree = Tree::new(&index);
let tree_hash = tree.get_hash();
let loaded_tree = Tree::load(&tree_hash);
let mut files = loaded_tree.get_recursive_file_entries();
files.sort();
test_files.sort();
assert_eq!(files.len(), test_files.len());
assert_eq!(
util::to_workdir_absolute_path(&files[0]).to_str().unwrap(), //TODO 罪大恶极的路径问题
util::get_absolute_path(&test_files[0]).to_str().unwrap()
);
assert_eq!(
util::to_workdir_absolute_path(&files[1]).to_str().unwrap(),
util::get_absolute_path(&test_files[1]).to_str().unwrap()
);
assert_eq!(loaded_tree.entries.len(), tree.entries.len());
assert_eq!(tree.entries[0].name, loaded_tree.entries[0].name);
assert_eq!(tree.entries[1].name, loaded_tree.entries[1].name);
}
#[test]

View File

@@ -1,3 +1,4 @@
pub mod store;
pub use store::Store;
pub mod test;
pub mod util;

View File

@@ -33,16 +33,6 @@ impl Store {
path.exists()
}
/// 将hash对应的文件内容(主要是blob)还原到file
pub fn restore_to_file(&self, hash: &Hash, file: &PathBuf) {
let content = self.load(hash);
// 保证文件层次存在
let mut parent = file.clone();
parent.pop();
std::fs::create_dir_all(parent).unwrap();
std::fs::write(file, content).unwrap();
}
/** 根据前缀搜索,有歧义时返回 None */
pub fn search(&self, hash: &String) -> Option<Hash> {
if hash.is_empty() {

View File

@@ -108,3 +108,7 @@ pub fn ensure_no_file(path: &Path) {
fs::remove_file(util::get_working_dir().unwrap().join(path)).unwrap();
}
}
pub fn is_file_exist<P: AsRef<Path>>(path: P) -> bool {
path.as_ref().exists()
}

View File

@@ -67,6 +67,12 @@ pub fn get_working_dir() -> Option<PathBuf> {
}
}
/// 将contents写入path若文件不存在则创建且确保父目录存在
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
fs::create_dir_all(path.as_ref().parent().unwrap())?;
fs::write(path, contents)
}
/// 检查文件是否在dir内(包括子文件夹) 若不存在则false
pub fn is_inside_dir(file: &Path, dir: &Path) -> bool {
if file.exists() {
@@ -138,11 +144,6 @@ where
.collect::<O>()
}
/// 检查文件是否在工作区内, 若不存在则false
pub fn is_inside_workdir(file: &Path) -> bool {
is_inside_dir(file, &get_working_dir().unwrap())
}
/// 检查文件是否在.mit内 若不存在则false
pub fn is_inside_repo(file: &Path) -> bool {
is_inside_dir(file, &get_storage_path().unwrap())
@@ -177,7 +178,8 @@ pub fn list_files(path: &Path) -> io::Result<Vec<PathBuf>> {
}
/** 列出子文件夹 */
pub fn list_subpath(path: &Path) -> io::Result<Vec<PathBuf>> {
#[allow(dead_code)]
pub fn list_subdir(path: &Path) -> io::Result<Vec<PathBuf>> {
let mut files = Vec::new();
let path = get_absolute_path(path);
if path.is_dir() {
@@ -332,25 +334,6 @@ pub fn get_file_mode(path: &Path) -> String {
}
}
/// 清除Windows下的绝对路径前缀"\\\\?\\" (由[PathBuf::canonicalize]函数产生)
/// <br><a href="https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation">Windows 系统中的文件路径格式</a>
pub fn clean_win_abs_path_pre(path: PathBuf) -> PathBuf {
#[cfg(windows)]
{
const DOS_PREFIX: &str = "\\\\?\\";
let path_str = path.to_string_lossy();
if path_str.starts_with(DOS_PREFIX) {
PathBuf::from(&path_str[DOS_PREFIX.len()..])
} else {
path
}
}
#[cfg(not(target_os = "windows"))]
{
path
}
}
/// 获取绝对路径相对于目录current_dir 不论是否存在
pub fn get_absolute_path(path: &Path) -> PathBuf {
get_absolute_path_to_dir(path, &std::env::current_dir().unwrap())
@@ -434,6 +417,14 @@ mod tests {
utils::{test, util::*},
};
#[test]
fn test_write() {
test::setup_with_empty_workdir();
let path = Path::new("src/test/a.txt");
write(path, "test").unwrap();
assert!(path.exists());
}
#[test]
fn test_get_storage_path() {
let path = get_storage_path();
@@ -494,16 +485,6 @@ mod tests {
assert_eq!(abs_path, cur_dir);
}
#[test]
fn test_is_inside_repo() {
test::setup_with_clean_mit();
let path = Path::new("../Cargo.toml");
assert_eq!(is_inside_workdir(path), false);
let path = Path::new(".mit/HEAD");
assert_eq!(is_inside_workdir(path), true);
}
#[test]
fn test_format_time() {
let time = std::time::SystemTime::now();
@@ -550,7 +531,7 @@ mod tests {
list_workdir_files().iter().for_each(|f| {
fs::remove_file(f).unwrap();
});
list_subpath(Path::new("./")).unwrap().iter().for_each(|f| {
list_subdir(Path::new("./")).unwrap().iter().for_each(|f| {
if include_root_dir(f) {
fs::remove_dir_all(f).unwrap();
}