mirror of
https://github.com/MrBeanCpp/MIT.git
synced 2026-02-11 06:05:11 +08:00
将Index改为单例模式,防止状态不一致; 注意:测试为单进程,需要reset防止共享单例
This commit is contained in:
@@ -15,3 +15,5 @@ serde_json = "1.0.108"
|
||||
colored = "2.1.0"
|
||||
rand = "0.8.5"
|
||||
color-backtrace = "0.6.1"
|
||||
once_cell = "1.19.0"
|
||||
backtrace = "0.3.69"
|
||||
@@ -31,13 +31,12 @@ pub fn add(raw_paths: Vec<String>, all: bool, mut update: bool) {
|
||||
println!("{}", "--update 只对已跟踪文件进行操作 不包含new".bright_green());
|
||||
}
|
||||
|
||||
let mut index = Index::new();
|
||||
for file in &files {
|
||||
add_a_file(file, &mut index);
|
||||
add_a_file(file);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_a_file(file: &Path, index: &mut Index) {
|
||||
fn add_a_file(file: &Path) {
|
||||
let workdir = util::get_working_dir().unwrap();
|
||||
if !util::is_sub_path(file, &workdir) {
|
||||
//文件不在工作区内
|
||||
@@ -50,6 +49,7 @@ fn add_a_file(file: &Path, index: &mut Index) {
|
||||
return;
|
||||
}
|
||||
|
||||
let index = Index::get_instance();
|
||||
let rel_path = util::to_cur_relative_path(file);
|
||||
if !file.exists() {
|
||||
//文件被删除
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{head, models::*};
|
||||
use super::status;
|
||||
|
||||
pub fn commit(message: String, allow_empty: bool) {
|
||||
let index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
if !allow_empty && status::changes_to_be_committed().is_empty() {
|
||||
panic!("工作区没有任何改动,不需要提交");
|
||||
}
|
||||
@@ -13,9 +13,9 @@ pub fn commit(message: String, allow_empty: bool) {
|
||||
|
||||
let mut commit = {
|
||||
if current_commit_hash.is_empty() {
|
||||
Commit::new(&index, vec![], message.clone())
|
||||
Commit::new(index, vec![], message.clone())
|
||||
} else {
|
||||
Commit::new(&index, vec![current_commit_hash.clone()], message.clone())
|
||||
Commit::new(index, vec![current_commit_hash.clone()], message.clone())
|
||||
}
|
||||
};
|
||||
let commit_hash = commit.save();
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{fs, io, path::PathBuf};
|
||||
/// 从暂存区&|工作区删除文件
|
||||
pub fn remove(files: Vec<String>, cached: bool, recursive: bool) -> io::Result<()> {
|
||||
util::check_repo_exist();
|
||||
let mut index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
for file in files.iter() {
|
||||
let path = PathBuf::from(file);
|
||||
if !path.exists() {
|
||||
|
||||
@@ -64,7 +64,7 @@ pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathB
|
||||
let mut file_paths = util::integrate_paths(&input_paths); //根据用户输入整合存在的文件(绝对路径)
|
||||
file_paths.extend(deleted_files); //已删除的文件
|
||||
|
||||
let index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
let store = Store::new();
|
||||
|
||||
for path in &file_paths {
|
||||
@@ -103,7 +103,7 @@ pub fn restore_index(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathBuf,
|
||||
let input_paths = preprocess_filters(filter); //预处理filter 将None转化为workdir
|
||||
let target_blobs = preprocess_blobs(target_blobs); //预处理target_blobs 转化为绝对路径HashMap
|
||||
|
||||
let mut index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
let deleted_files_index = get_index_deleted_files_in_filters(&index, &input_paths, &target_blobs); //统计已删除的文件
|
||||
|
||||
//1.获取index中包含于input_path的文件(使用paths进行过滤)
|
||||
@@ -181,7 +181,7 @@ pub fn restore(paths: Vec<String>, source: Option<String>, worktree: bool, stage
|
||||
otherwise from the [index].*/
|
||||
if source.is_none() && !staged {
|
||||
// 没有指定source,且没有指定--staged,从[index]中恢复到worktree //只有这种情况是从[index]恢复
|
||||
let entries = Index::new().get_tracked_entries();
|
||||
let entries = Index::get_instance().get_tracked_entries();
|
||||
entries.into_iter().map(|(p, meta)| (p, meta.hash)).collect()
|
||||
} else {
|
||||
//从[target_commit]中恢复
|
||||
@@ -224,7 +224,7 @@ mod test {
|
||||
util::ensure_no_file(&path);
|
||||
cmd::add(vec![], true, false); //add -A
|
||||
cmd::restore(vec![".".to_string()], Some("HEAD".to_string()), false, true);
|
||||
let index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
assert!(index.get_tracked_files().is_empty());
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ impl Changes {
|
||||
*/
|
||||
pub fn changes_to_be_committed() -> Changes {
|
||||
let mut change = Changes::default();
|
||||
let index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
let head_hash = head::current_head_commit();
|
||||
let tracked_files = index
|
||||
.get_tracked_files()
|
||||
@@ -111,7 +111,7 @@ pub fn changes_to_be_committed() -> Changes {
|
||||
/// 比较工作区与暂存区的差异,返回相对路径(to workdir),不筛选
|
||||
pub fn changes_to_be_staged() -> Changes {
|
||||
let mut change = Changes::default();
|
||||
let index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
for file in index.get_tracked_files() {
|
||||
if !file.exists() {
|
||||
change.deleted.push(util::to_workdir_relative_path(&file));
|
||||
|
||||
@@ -88,7 +88,7 @@ mod test {
|
||||
fn test_commit() {
|
||||
util::setup_test_with_clean_mit();
|
||||
|
||||
let index = super::Index::new();
|
||||
let index = super::Index::get_instance();
|
||||
let mut commit = super::Commit::new(&index, vec!["123".to_string(), "456".to_string()], "test".to_string());
|
||||
assert_eq!(commit.hash.len(), 0);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{models::*, utils::util};
|
||||
use once_cell::unsync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@@ -43,7 +44,8 @@ impl FileMetaData {
|
||||
}
|
||||
|
||||
/** Index
|
||||
注意:逻辑处理均为绝对路径,但是存储时为相对路径
|
||||
注意:逻辑处理均为绝对路径,但是存储时为相对路径(to workdir)<br>
|
||||
<a href="https://wolfsonliu.github.io/archive/2018/li-jie-git-index-wen-jian.html">理解 Git index 文件</a>
|
||||
*/
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
pub struct Index {
|
||||
@@ -53,8 +55,7 @@ pub struct Index {
|
||||
|
||||
impl Index {
|
||||
/// 从index文件加载
|
||||
pub(crate) fn new() -> Index {
|
||||
//TODO! 设计为单例模式
|
||||
fn new() -> Index {
|
||||
let mut index = Index {
|
||||
entries: HashMap::new(),
|
||||
working_dir: util::get_working_dir().unwrap(),
|
||||
@@ -63,26 +64,44 @@ impl Index {
|
||||
return index;
|
||||
}
|
||||
|
||||
/// 单例模式,线程不安全,但是本程序默认单线程
|
||||
pub fn get_instance() -> &'static mut Index {
|
||||
static mut INSTANCE: Lazy<Index> = Lazy::new(Index::new); //延迟初始化,线程不安全
|
||||
unsafe { &mut INSTANCE }
|
||||
}
|
||||
|
||||
/// 重置index,主要用于测试,防止单例模式的影响
|
||||
pub fn reset() {
|
||||
let index = Index::get_instance();
|
||||
index.clear();
|
||||
*index = Index::new(); //drop happened 导致旧数据写入文件
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.entries.clear();
|
||||
self.working_dir.clear();
|
||||
}
|
||||
|
||||
/// 预处理路径,统一形式为绝对路径
|
||||
fn preprocess_path(path: &Path) -> PathBuf {
|
||||
fn preprocess(path: &Path) -> PathBuf {
|
||||
util::get_absolute_path(&path)
|
||||
}
|
||||
|
||||
// 添加文件
|
||||
pub fn add(&mut self, mut path: PathBuf, data: FileMetaData) {
|
||||
path = Index::preprocess_path(&path);
|
||||
path = Index::preprocess(&path);
|
||||
self.entries.insert(path, data);
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
pub fn remove(&mut self, path: &Path) {
|
||||
let path = Index::preprocess_path(&path);
|
||||
let path = Index::preprocess(&path);
|
||||
self.entries.remove(&path);
|
||||
}
|
||||
|
||||
// 获取文件元数据
|
||||
pub fn get(&self, path: &Path) -> Option<FileMetaData> {
|
||||
let path = Index::preprocess_path(path);
|
||||
let path = Index::preprocess(path);
|
||||
self.entries.get(&path).cloned()
|
||||
}
|
||||
|
||||
@@ -96,7 +115,7 @@ impl Index {
|
||||
}
|
||||
|
||||
pub fn contains(&self, path: &Path) -> bool {
|
||||
let path = Index::preprocess_path(path);
|
||||
let path = Index::preprocess(path);
|
||||
self.entries.contains_key(&path)
|
||||
}
|
||||
|
||||
@@ -134,7 +153,7 @@ impl Index {
|
||||
}
|
||||
|
||||
pub fn update(&mut self, mut path: PathBuf, data: FileMetaData) {
|
||||
path = Index::preprocess_path(&path);
|
||||
path = Index::preprocess(&path);
|
||||
self.entries.insert(path, data);
|
||||
}
|
||||
|
||||
@@ -151,6 +170,8 @@ impl Index {
|
||||
(abs_path, value)
|
||||
})
|
||||
.collect();
|
||||
} else {
|
||||
// println!("index文件不存在,创建空index");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +182,7 @@ impl Index {
|
||||
path
|
||||
}
|
||||
|
||||
/// 二进制序列化
|
||||
/// 保存到文件
|
||||
pub fn save(&mut self) {
|
||||
//要先转化为相对路径
|
||||
let relative_index: HashMap<PathBuf, FileMetaData> = self
|
||||
@@ -214,14 +235,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_load() {
|
||||
util::setup_test_with_clean_mit();
|
||||
let index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
println!("{:?}", index);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save() {
|
||||
util::setup_test_with_clean_mit();
|
||||
let mut index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
let path = PathBuf::from("../mit_test_storage/.mit/HEAD"); //测试../相对路径的处理
|
||||
index.add(path.clone(), FileMetaData::new(&Blob::new(&path), &path));
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_new() {
|
||||
util::setup_test_with_clean_mit();
|
||||
let mut index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
for test_file in vec!["b.txt", "mit_src/a.txt", "test/test.txt"] {
|
||||
let test_file = PathBuf::from(test_file);
|
||||
util::ensure_test_file(&test_file, None);
|
||||
@@ -178,7 +178,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_load() {
|
||||
util::setup_test_with_clean_mit();
|
||||
let mut index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
let test_files = vec!["b.txt", "mit_src/a.txt"];
|
||||
for test_file in test_files.clone() {
|
||||
let test_file = PathBuf::from(test_file);
|
||||
@@ -198,7 +198,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_get_recursive_file_entries() {
|
||||
util::setup_test_with_clean_mit();
|
||||
let mut index = Index::new();
|
||||
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() {
|
||||
util::ensure_test_file(&test_file, None);
|
||||
@@ -226,7 +226,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_get_recursive_blobs() {
|
||||
util::setup_test_with_clean_mit();
|
||||
let mut index = Index::new();
|
||||
let index = Index::get_instance();
|
||||
let test_files = vec!["b.txt", "mit_src/a.txt"];
|
||||
let mut test_blobs = vec![];
|
||||
for test_file in test_files.clone() {
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::models::{commit::Commit, object::Hash, tree::Tree};
|
||||
use crate::models::{commit::Commit, object::Hash, tree::Tree, Index};
|
||||
|
||||
pub const ROOT_DIR: &str = ".mit";
|
||||
pub const TEST_DIR: &str = "mit_test_storage"; // 执行测试的储存库
|
||||
@@ -33,26 +33,33 @@ fn find_cargo_dir() -> PathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_test_dir() {
|
||||
/// 准备测试环境,切换到测试目录
|
||||
fn setup_test_env() {
|
||||
color_backtrace::install(); // colorize backtrace
|
||||
|
||||
let mut path = find_cargo_dir();
|
||||
path.push(TEST_DIR);
|
||||
if !path.exists() {
|
||||
fs::create_dir(&path).unwrap();
|
||||
}
|
||||
std::env::set_current_dir(&path).unwrap();
|
||||
std::env::set_current_dir(&path).unwrap(); // 将执行目录切换到测试目录
|
||||
}
|
||||
|
||||
pub fn init_mit() {
|
||||
let _ = crate::commands::init();
|
||||
Index::reset(); // 重置index, 以防止其他测试修改了index单例
|
||||
}
|
||||
|
||||
/// with 初始化的干净的mit
|
||||
pub fn setup_test_with_clean_mit() {
|
||||
setup_test_without_mit();
|
||||
let _ = crate::commands::init::init();
|
||||
init_mit();
|
||||
}
|
||||
|
||||
pub fn setup_test_without_mit() {
|
||||
// 将执行目录切换到测试目录,并清除测试目录下的.mit目录
|
||||
setup_test_dir();
|
||||
let mut path = std::env::current_dir().unwrap();
|
||||
setup_test_env();
|
||||
let mut path = cur_dir();
|
||||
path.push(ROOT_DIR);
|
||||
if path.exists() {
|
||||
fs::remove_dir_all(&path).unwrap();
|
||||
@@ -632,7 +639,7 @@ mod tests {
|
||||
ensure_test_file(Path::new("test.txt"), Some("test"));
|
||||
let hash = Blob::new(get_working_dir().unwrap().join("test.txt").as_path()).get_hash();
|
||||
assert_eq!(check_object_type(hash), ObjectType::Blob);
|
||||
let mut commit = Commit::new(&Index::new(), vec![], "test".to_string());
|
||||
let mut commit = Commit::new(&Index::get_instance(), vec![], "test".to_string());
|
||||
assert_eq!(check_object_type(commit.get_tree_hash()), ObjectType::Tree);
|
||||
commit.save();
|
||||
assert_eq!(check_object_type(commit.get_hash()), ObjectType::Commit);
|
||||
|
||||
Reference in New Issue
Block a user