use std::path::PathBuf; use crate::{ head, models::{blob, index}, utils::util, }; /** 获取需要commit的更改(staged) */ #[derive(Debug)] pub struct Changes { pub new: Vec, pub modified: Vec, pub deleted: Vec, } fn __file_string(path: &PathBuf) -> String { util::to_root_relative_path(&path) //todo: to_string_lossy() .as_os_str() .to_str() .unwrap() .to_string() } pub fn changes_to_be_committed() -> Changes { let mut change = Changes { //todo: Changes::default() new: vec![], modified: vec![], deleted: vec![], }; let index = index::Index::new(); let head_hash = head::current_head_commit(); if head_hash == "".to_string() { //todo: head_hash.is_empty() or head_hash == "" // 初始提交 change.new = index .get_tracked_files() .iter() .map(|f| __file_string(f)) .collect(); return change; } let commit = crate::models::commit::Commit::load(&head_hash); let tree = commit.get_tree(); let tree_files = tree.get_recursive_blobs(); let index_files: Vec = index .get_tracked_files() .iter() .map(|f| util::to_root_relative_path(f)) .collect(); for tree_item in tree_files.iter() { let index_file = index_files.iter().find(|f| **f == tree_item.0); if index_file.is_none() { change.deleted.push(__file_string(&tree_item.0)); //todo: abs_path? } else { let index_blob = blob::Blob::new( //todo: index有函数可以获取blob_hash 不需要new util::get_working_dir() //todo: 优化:提取为变量 .unwrap() .join(index_file.unwrap()) .as_path(), ); // XXX @mrbeanc 我看到Blob的new被改成调用save了。这里的实现希望比较Blob内容,不然就得读取文件内容。 if index_blob.get_hash() != tree_item.1.get_hash() { change.modified.push(__file_string(&tree_item.0)); } } } for index_file in index_files.iter() { let tree_item = tree_files.iter().find(|f| f.0 == **index_file); if tree_item.is_none() { change.new.push(__file_string(&index_file)); } } change } /** 分为两个部分 1. unstaged: 暂存区与工作区比较 2. staged to be committed: 暂存区与HEAD(最后一次Commit::Tree)比较,即上次的暂存区 */ pub fn status() { unimplemented!() } #[cfg(test)] mod tests { use super::*; use crate::{commands::commit, utils::util}; use std::{fs, path::Path}; #[test] fn test_changes_to_be_committed() { util::setup_test_with_clean_mit(); let test_file = "a.txt"; util::ensure_test_file(Path::new(test_file), None); commit::commit("test commit".to_string(), true); let mut index = index::Index::new(); index.add( //todo 可以直接调用add函数 PathBuf::from(test_file), index::FileMetaData::new(&blob::Blob::new(Path::new(test_file)), Path::new(test_file)), ); index.save(); let change = changes_to_be_committed(); assert_eq!(change.new.len(), 1); assert_eq!(change.modified.len(), 0); assert_eq!(change.deleted.len(), 0); commit::commit("test commit".to_string(), true); util::ensure_test_file(Path::new(test_file), Some("new content")); index.add( PathBuf::from(test_file), index::FileMetaData::new(&blob::Blob::new(Path::new(test_file)), Path::new(test_file)), ); index.save(); let change = changes_to_be_committed(); assert_eq!(change.new.len(), 0); assert_eq!(change.modified.len(), 1); assert_eq!(change.deleted.len(), 0); commit::commit("test commit".to_string(), true); index.remove( util::get_working_dir() .unwrap() .join(Path::new(test_file)) .as_path(), ); index.save(); let change = changes_to_be_committed(); assert_eq!(change.new.len(), 0); assert_eq!(change.modified.len(), 0); assert_eq!(change.deleted.len(), 1); } }