diff --git a/src/cli.rs b/src/cli.rs index ac13a48..66d85fc 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -100,7 +100,6 @@ enum Command { }, /// restore Restore { - // TODO 行为不确定 /// 要恢复的文件 #[clap(required = true)] path: Vec, @@ -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); diff --git a/src/commands/merge.rs b/src/commands/merge.rs index c451a47..135a9d2 100644 --- a/src/commands/merge.rs +++ b/src/commands/merge.rs @@ -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,什么都没有发生 diff --git a/src/commands/restore.rs b/src/commands/restore.rs index 688f88b..0e42f81 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -155,38 +155,65 @@ pub fn restore_index(filter: Option<&Vec>, target_blobs: &Vec<(PathBuf, 对于暂存区中被删除的文件,同样会恢复
注意:不会删除空文件夹 */ -pub fn restore(paths: Vec, source: String, worktree: bool, staged: bool) { - // TODO 尝试合并restore_index和restore_worktree(逻辑上是一致的) +pub fn restore(paths: Vec, source: Option, worktree: bool, staged: bool) { let paths = paths.iter().map(PathBuf::from).collect::>(); 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()); } diff --git a/src/models/index.rs b/src/models/index.rs index 13b7d3f..f7a2274 100644 --- a/src/models/index.rs +++ b/src/models/index.rs @@ -84,9 +84,9 @@ impl Index { } // 获取文件元数据 - pub fn get(&self, path: &Path) -> Option<&FileMetaData> { + pub fn get(&self, path: &Path) -> Option { let path = Index::preprocess_path(path); - self.entries.get(&path) + self.entries.get(&path).cloned() } pub fn get_hash(&self, file: &Path) -> Option { @@ -184,11 +184,16 @@ impl Index { pub fn get_tracked_files(&self) -> Vec { self.entries.keys().map(|f| f.clone()).collect() } + + pub fn get_tracked_entries(&self) -> HashMap { + self.entries.clone() + } } /// 析构自动保存 impl Drop for Index { fn drop(&mut self) { + //TODO! 优化为只有在修改后才保存 self.save(); // println!("{}", "Index auto saved".bright_green()); } diff --git a/src/utils/util.rs b/src/utils/util.rs index 4222f62..bc7c086 100644 --- a/src/utils/util.rs +++ b/src/utils/util.rs @@ -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]