基本实现restore命令

This commit is contained in:
mrbeanc
2023-12-23 16:10:28 +08:00
parent 282b3556d1
commit d4009cd2cf
4 changed files with 126 additions and 32 deletions

View File

@@ -24,6 +24,7 @@ pub fn add(files: Vec<String>, all: bool, mut update: bool) {
let mut files: Vec<PathBuf> = files.into_iter().map(PathBuf::from).collect();
if dot || all || update {
//TODO files中可能包含文件夹需要统计文件夹中被删除的文件
if all {
// all 优先级最高
dot = false;

View File

@@ -1,7 +1,7 @@
use std::collections::{HashMap, HashSet};
use std::{fs, path::PathBuf};
use crate::utils::util::{get_absolute_path, list_files};
use crate::models::index::FileMetaData;
use crate::{
head,
models::{commit::Commit, index::Index, object::Hash},
@@ -9,25 +9,14 @@ use crate::{
utils::{util, util::get_working_dir},
};
/** 根据filter restore workdir */
pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathBuf, Hash)>) {
let paths: Vec<PathBuf> = if let Some(filter) = filter {
filter.clone()
} else {
vec![get_working_dir().unwrap()] //None == all(workdir), '.' == cur_dir
};
let target_blobs = target_blobs // 转为绝对路径 //TODO tree改变路径表示方式后这里需要修改
.iter()
.map(|(path, hash)| (util::to_workdir_absolute_path(path), hash.clone()))
.collect::<HashMap<PathBuf, Hash>>();
let dirs: Vec<PathBuf> = paths.iter().filter(|path| path.is_dir()).cloned().collect();
let del_files = target_blobs //统计所有目录中(包括None & '.'),删除的文件
/// 统计[工作区]中的dirs文件夹中相对于target_blobs已删除的文件
fn get_worktree_deleted_files_in_dirs(dirs: &Vec<PathBuf>, target_blobs: &HashMap<PathBuf, Hash>) -> HashSet<PathBuf> {
target_blobs //统计所有目录中(包括None & '.'),删除的文件
.iter()
.filter(|(path, _)| {
assert!(path.is_absolute()); //
if !path.exists() {
for dir in &dirs {
for dir in dirs {
if util::is_parent_dir(path, dir) {
//需要包含在指定dir内
return true;
@@ -37,23 +26,76 @@ pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathB
false
})
.map(|(path, _)| path.clone())
.collect::<HashSet<PathBuf>>(); //HashSet自动去重
let mut paths = util::integrate_paths(&paths); //存在的文件路径
paths.extend(del_files); //不存在的文件路径
.collect() //HashSet自动去重
}
/// 统计[暂存区index]中相对于target_blobs已删除的文件且包含在指定dirs内
fn get_index_deleted_files_in_dirs(
index: &Index,
dirs: &Vec<PathBuf>,
target_blobs: &HashMap<PathBuf, Hash>,
) -> HashSet<PathBuf> {
target_blobs //统计index中相对target已删除的文件且包含在指定dir内
.iter()
.filter(|(path, _)| {
assert!(path.is_absolute()); //
if !index.contains(path) {
//index中不存在
for dir in dirs {
if util::is_parent_dir(path, dir) {
//需要包含在指定dir内
return true;
}
}
}
false
})
.map(|(path, _)| path.clone())
.collect() //HashSet自动去重
}
/// 将None转化为workdir
fn preprocess_filters(filters: Option<&Vec<PathBuf>>) -> Vec<PathBuf> {
if let Some(filter) = filters {
filter.clone()
} else {
vec![get_working_dir().unwrap()] //None == all(workdir), '.' == cur_dir
}
}
/// 转化为绝对路径to workdir的HashMap
fn preprocess_blobs(blobs: &Vec<(PathBuf, Hash)>) -> HashMap<PathBuf, Hash> {
blobs // 转为绝对路径 //TODO tree改变路径表示方式后这里需要修改
.iter()
.map(|(path, hash)| (util::to_workdir_absolute_path(path), hash.clone()))
.collect() //to HashMap
}
/** 根据filter restore workdir */
pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathBuf, Hash)>) {
let paths = preprocess_filters(filter); //预处理filter 将None转化为workdir
let target_blobs = preprocess_blobs(target_blobs); //预处理target_blobs 转化为绝对路径HashMap
let dirs = util::filter_dirs(&paths); //统计所有目录
let deleted_files = get_worktree_deleted_files_in_dirs(&dirs, &target_blobs); //统计所有目录中已删除的文件
let mut file_paths = util::integrate_paths(&paths); //整合存在的文件(绝对路径)
file_paths.extend(deleted_files); //已删除的文件
let index = Index::new();
let store = Store::new();
for path in &paths {
for path in &file_paths {
assert!(path.is_absolute() && !path.is_dir()); // 绝对路径且不是目录
if !path.exists() {
//文件不存在于workdir
if target_blobs.contains_key(path) {
//文件存在于target_commit
//文件存在于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());
// println!("fatal: pathspec '{}' did not match any files", path.display());
// TODO 如果是用户输入的路径才应该报错integrate_paths产生的不应该报错
}
} else {
//文件存在有两种情况1.修改 2.新文件
@@ -73,10 +115,44 @@ pub fn restore_worktree(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathB
}
}
}
/** 根据filte restore staged */
/** 根据filter restore staged */
pub fn restore_index(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathBuf, Hash)>) {
// TODO 让@mrbeanc来写吧
unimplemented!("TODO");
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 dirs = util::filter_dirs(&input_paths); //统计所有目录
let deleted_files_index = get_index_deleted_files_in_dirs(&index, &dirs, &target_blobs); //统计所有目录中已删除的文件
let mut file_paths = util::integrate_paths(&input_paths); //整合存在的文件(绝对路径)
file_paths.extend(deleted_files_index); //已删除的文件
for path in &file_paths {
assert!(path.is_absolute() && !path.is_dir()); // 绝对路径且不是目录
if !index.contains(path) {
//文件不存在于index
if target_blobs.contains_key(path) {
//文件存在于target_commit (deleted),需要恢复
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产生的不应该报错
}
} else {
//文件存在于index有两种情况1.修改 2.新文件
if target_blobs.contains_key(path) {
if !index.verify_hash(path, &target_blobs[path]) {
//文件已修改(modified)
index.update(path.clone(), FileMetaData { hash: target_blobs[path].clone(), ..Default::default() });
}
} else {
//新文件 需要从index中删除
index.remove(path);
}
}
}
}
/**
对于工作区中的新文件,若已跟踪,则删除;若未跟踪,则保留<br>

View File

@@ -20,6 +20,18 @@ pub struct FileMetaData {
pub mode: String, // 文件模式
}
impl Default for FileMetaData {
fn default() -> Self {
FileMetaData {
hash: Default::default(),
size: Default::default(),
created_time: SystemTime::now(), // 或者使用 UNIX_EPOCH
modified_time: SystemTime::now(), // 或者使用 UNIX_EPOCH
mode: Default::default(),
}
}
}
impl FileMetaData {
pub fn new(blob: &Blob, file: &Path) -> FileMetaData {
let meta = file.metadata().unwrap();

View File

@@ -1,4 +1,5 @@
use sha1::{Digest, Sha1};
use std::collections::HashSet;
use std::{
fs, io,
io::Write,
@@ -148,6 +149,7 @@ pub fn is_inside_dir(file: &Path, dir: &Path) -> bool {
/// 检测dir是否是file的父目录 (不论文件是否存在)
pub fn is_parent_dir(file: &Path, dir: &Path) -> bool {
assert!(dir.is_dir());
let file = get_absolute_path(file);
let dir = get_absolute_path(dir);
file.starts_with(dir)
@@ -168,6 +170,11 @@ pub fn format_time(time: &std::time::SystemTime) -> String {
datetime.format("%Y-%m-%d %H:%M:%S.%3f").to_string()
}
/// 过滤出路径数组中的目录
pub fn filter_dirs(paths: &Vec<PathBuf>) -> Vec<PathBuf> {
paths.iter().filter(|path| path.is_dir()).cloned().collect()
}
/// 将路径中的分隔符统一为当前系统的分隔符
fn unify_path_separator(path: &Path) -> PathBuf {
#[cfg(windows)]
@@ -315,9 +322,9 @@ pub fn get_absolute_path(path: &Path) -> PathBuf {
abs_path
}
}
/// 整理输入的路径数组(相对、绝对、文件、目录),返回一个绝对路径的文件数组
pub fn integrate_paths(paths: &Vec<PathBuf>) -> Vec<PathBuf> {
let mut abs_paths = Vec::new();
/// 整理输入的路径数组(相对、绝对、文件、目录),返回一个绝对路径的文件数组只包含exist
pub fn integrate_paths(paths: &Vec<PathBuf>) -> HashSet<PathBuf> {
let mut abs_paths = HashSet::new();
for path in paths {
let path = get_absolute_path(&path); // 统一转换为绝对路径
if path.is_dir() {
@@ -325,11 +332,9 @@ pub fn integrate_paths(paths: &Vec<PathBuf>) -> Vec<PathBuf> {
let files = list_files(&path).unwrap();
abs_paths.extend(files);
} else {
abs_paths.push(path);
abs_paths.insert(path);
}
}
abs_paths.sort();
abs_paths.dedup(); // 去重
abs_paths
}