完善restore逻辑,默认从index恢复(若没有--source且没有--staged)

This commit is contained in:
mrbeanc
2023-12-24 18:01:15 +08:00
parent 465047b3ea
commit ee4ab06da3
5 changed files with 66 additions and 37 deletions

View File

@@ -100,7 +100,6 @@ enum Command {
},
/// restore
Restore {
// TODO 行为不确定
/// 要恢复的文件
#[clap(required = true)]
path: Vec<String>,
@@ -148,25 +147,21 @@ pub fn handle_command() {
Command::Branch { list, delete, new_branch, commit_hash, show_current } => {
branch(new_branch, commit_hash, list, delete, show_current);
}
Command::Switch { branch, create, detach } => {
switch(branch, create, detach);
}
Command::Restore { path, mut source, mut worktree, staged } => {
Command::Restore { path, source, mut worktree, staged } => {
// 未指定stage和worktree时默认操作worktree
// 指定 --staged 将仅还原index
if !staged {
worktree = true;
}
// 未指定source默认操作HEAD
/*TODO
// 未指定source 且 --staged默认操作HEAD否则从index中恢复就近原则
/*
If `--source` not specified, the contents are restored from `HEAD` if `--staged` is given,
otherwise from the [index].
*/
if source.is_none() {
source = Some("HEAD".to_string());
}
restore(path, source.unwrap(), worktree, staged);
restore(path, source, worktree, staged);
}
Command::Merge { branch } => {
merge(branch);

View File

@@ -52,7 +52,7 @@ fn merge_ff(commit_hash: String) -> Result<(), MergeErr> {
match head {
head::Head::Branch(branch) => {
head::update_branch(&branch, &commit_hash.clone());
commands::restore::restore(vec![], commit_hash.clone(), true, true)
commands::restore::restore(vec![], Some(commit_hash.clone()), true, true)
}
head::Head::Detached(_) => {
// 相当于切换到了commit_hash什么都没有发生

View File

@@ -155,38 +155,65 @@ pub fn restore_index(filter: Option<&Vec<PathBuf>>, target_blobs: &Vec<(PathBuf,
对于暂存区中被删除的文件,同样会恢复<br>
注意:不会删除空文件夹
*/
pub fn restore(paths: Vec<String>, source: String, worktree: bool, staged: bool) {
// TODO 尝试合并restore_index和restore_worktree逻辑上是一致的
pub fn restore(paths: Vec<String>, source: Option<String>, worktree: bool, staged: bool) {
let paths = paths.iter().map(PathBuf::from).collect::<Vec<PathBuf>>();
let target_commit: Hash = {
if source == "HEAD" {
//Default
head::current_head_commit()
} else if head::list_local_branches().contains(&source) {
// Branch Name, e.g. master
head::get_branch_head(&source)
} else {
// Commit Hash, e.g. a1b2c3d4
let store = Store::new();
let commit = store.search(&source);
if commit.is_none() || !util::is_typeof_commit(commit.clone().unwrap()) {
println!("fatal: 非法的 commit hash: '{}'", source);
return;
match source {
None => {
/*If `--source` not specified, the contents are restored from `HEAD` if `--staged` is given,
otherwise from the [index].*/
if staged {
head::current_head_commit() // `HEAD`
} else {
Hash::default() //index
}
}
Some(ref src) => {
if src == "HEAD" {
//Default Source
head::current_head_commit() // "" if not exist
} else if head::list_local_branches().contains(&src) {
// Branch Name, e.g. master
head::get_branch_head(&src) // "" if not exist
} else {
// [Commit Hash, e.g. a1b2c3d4] || [Wrong Branch Name]
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);
return;
}
commit.unwrap()
}
}
commit.unwrap()
}
};
// 分别处理worktree和staged
let target_blobs = {
if target_commit.is_empty() {
// 没有commit的情况
Vec::new()
/*If `--source` not specified, the contents are restored from `HEAD` if `--staged` is given,
otherwise from the [index].*/
if source.is_none() && !staged {
// 没有指定source且没有指定--staged从[index]中恢复到worktree //只有这种情况是从[index]恢复
let entries = Index::new().get_tracked_entries();
entries.into_iter().map(|(p, meta)| (p, meta.hash)).collect()
} else {
let tree = Commit::load(&target_commit).get_tree();
tree.get_recursive_blobs() // 相对路径
//从[target_commit]中恢复
if target_commit.is_empty() {
//target_commit不存在 无法从目标恢复
if source.is_some() {
// 如果指定了source说明source解析失败报错
println!("fatal: could not resolve {}", source.unwrap());
return;
}
Vec::new() //否则使用[空]来恢复 代表default status
} else {
//target_commit存在最正常的情况谢天谢地
let tree = Commit::load(&target_commit).get_tree();
tree.get_recursive_blobs() // 相对路径
}
}
};
// 分别处理worktree和staged
if worktree {
restore_worktree(Some(&paths), &target_blobs);
}
@@ -207,7 +234,7 @@ mod test {
let path = PathBuf::from("a.txt");
util::ensure_no_file(&path);
commands::add::add(vec![], true, false);
super::restore(vec![".".to_string()], "HEAD".to_string(), false, true);
super::restore(vec![".".to_string()], Some("HEAD".to_string()), false, true);
let index = Index::new();
assert!(index.get_tracked_files().is_empty());
}

View File

@@ -84,9 +84,9 @@ impl Index {
}
// 获取文件元数据
pub fn get(&self, path: &Path) -> Option<&FileMetaData> {
pub fn get(&self, path: &Path) -> Option<FileMetaData> {
let path = Index::preprocess_path(path);
self.entries.get(&path)
self.entries.get(&path).cloned()
}
pub fn get_hash(&self, file: &Path) -> Option<Hash> {
@@ -184,11 +184,16 @@ impl Index {
pub fn get_tracked_files(&self) -> Vec<PathBuf> {
self.entries.keys().map(|f| f.clone()).collect()
}
pub fn get_tracked_entries(&self) -> HashMap<PathBuf, FileMetaData> {
self.entries.clone()
}
}
/// 析构自动保存
impl Drop for Index {
fn drop(&mut self) {
//TODO! 优化为只有在修改后才保存
self.save();
// println!("{}", "Index auto saved".bright_green());
}

View File

@@ -444,10 +444,12 @@ pub fn get_absolute_path_to_dir(path: &Path, dir: &Path) -> PathBuf {
if path.is_absolute() {
path.to_path_buf()
} else {
//相对路径
/*let abs_path = path.canonicalize().unwrap(); //这一步会统一路径分隔符 //canonicalize()不能处理不存在的文件
clean_win_abs_path_pre(abs_path)*/
// 所以决定手动解析相对路径中的../ ./
let mut abs_path = dir.to_path_buf();
// 这里会拆分所有组件,所以会自动统一路径分隔符
for component in path.components() {
match component {
std::path::Component::ParentDir => {
@@ -541,7 +543,7 @@ mod tests {
#[test]
fn test_get_absolute_path() {
let path = Path::new("./mit_test_storage/.././src/main.rs");
let path = Path::new("./mit_test_storage/.././src\\main.rs");
let abs_path = get_absolute_path(path);
println!("{:?}", abs_path);
@@ -549,7 +551,7 @@ mod tests {
cur_dir.push("mit_test_storage");
cur_dir.pop();
cur_dir.push("src/main.rs");
assert_eq!(abs_path, cur_dir);
assert_eq!(abs_path, cur_dir); // 只比较组件,不比较分隔符
}
#[test]