将Index改为单例模式,防止状态不一致; 注意:测试为单进程,需要reset防止共享单例

This commit is contained in:
mrbeanc
2023-12-27 16:19:02 +08:00
parent 228e1d681e
commit b568e87925
10 changed files with 67 additions and 37 deletions

View File

@@ -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"

View File

@@ -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() {
//文件被删除

View File

@@ -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();

View File

@@ -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() {

View File

@@ -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());
}

View File

@@ -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));

View 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);

View File

@@ -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));

View File

@@ -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() {

View File

@@ -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);