mirror of
https://github.com/MrBeanCpp/MIT.git
synced 2026-02-04 10:54:47 +08:00
清理警告,将restore功能转入Blob
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod store;
|
||||
pub use store::Store;
|
||||
pub mod test;
|
||||
pub mod util;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user