From 247347a76e897c9cec4f35d8cb6059aa4e281b2f Mon Sep 17 00:00:00 2001 From: mrbeanc Date: Tue, 19 Dec 2023 13:49:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E7=BC=96=E5=86=99add?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=9B=E6=96=B0=E5=A2=9E=E7=BB=88=E7=AB=AF?= =?UTF-8?q?=E9=A2=9C=E8=89=B2=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 3 +++ src/cli.rs | 7 ++---- src/commands/add.rs | 45 ++++++++++++++++++++++++++++++++++ src/commands/init.rs | 3 ++- src/commands/mod.rs | 3 ++- src/models/index.rs | 57 ++++++++++++++++++++++++++++++++++++++++---- src/utils/util.rs | 55 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 161 insertions(+), 12 deletions(-) create mode 100644 src/commands/add.rs diff --git a/Cargo.toml b/Cargo.toml index e2a80c3..abd5a3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,6 @@ sha1 = "0.10.6" hex = "0.4.3" clap = { version = "4.4.11", features = ["derive"] } chrono = "0.4.31" +serde = { version = "1.0.193", features = ["derive"] } +serde_json = "1.0.108" +colored = "2.1.0" \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index 80b8738..db95778 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,5 @@ use clap::{Parser, Subcommand}; +use mit::commands::add::add; use mit::commands::init::init; /// Rust实现的简易版本的Git,用于学习Rust语言 @@ -52,11 +53,7 @@ pub fn handle_command() { let _ = init(); } Command::Add { files , all, update} => { - if files.contains(&".".to_string()) || all { - println!("add all files"); - } else { - println!("add: {:?}, all:{:?}", files, all); - } + add(files, all, update); } Command::Rm { files, cached } => { println!("rm: {:?}, cached= {}", files, cached); diff --git a/src/commands/add.rs b/src/commands/add.rs new file mode 100644 index 0000000..67c16b9 --- /dev/null +++ b/src/commands/add.rs @@ -0,0 +1,45 @@ +use std::env; +use std::path::{Path, PathBuf}; +use colored::Colorize; +use crate::models::index::Index; +use crate::utils::util::{check_repo_exist, get_working_dir, list_files}; + +pub fn add(files: Vec, all: bool, mut update: bool) { + check_repo_exist(); + let index = Index::new(); + let mut dot = files.contains(&".".to_string()); + + let mut files: Vec = files.into_iter().map(PathBuf::from).collect(); + + if dot || all || update{ + if all { // all 优先级最高 + dot = false; + update = false; + } else if update { + dot = false; + } + + let path = if all || update { + println!("{}", "--all || --update 运行于工作区目录".bright_green()); + get_working_dir().unwrap() + } else { + println!("{}", "'.'代表了当前目录".bright_green()); + env::current_dir().unwrap() + }; + println!("Working on [{}]\n", path.to_str().unwrap().bright_blue()); + files = list_files(&*path).unwrap(); + if update { + files.retain(|file|{ + index.contains(file) + }); + } + } + + for file in files { + add_a_file(file.as_path(), &index); + } +} + +fn add_a_file(file: &Path, index: &Index) { + println!("add a file: {:?}", file); +} \ No newline at end of file diff --git a/src/commands/init.rs b/src/commands/init.rs index 6556afd..8b14652 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -1,4 +1,5 @@ use std::{env, fs, io}; +use crate::utils::util::ROOT_DIR; /** 初始化mit仓库 创建.mit/objects .mit/refs/heads .mit/HEAD @@ -7,7 +8,7 @@ use std::{env, fs, io}; */ pub fn init() -> io::Result<()> { let dir = env::current_dir()?; - let mit_dir = dir.join(".mit"); + let mit_dir = dir.join(ROOT_DIR); if mit_dir.exists() { println!("!Already a mit repo - [{}]", dir.display()); return Ok(()); diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 639a6c5..78fdf68 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1 +1,2 @@ -pub mod init; \ No newline at end of file +pub mod init; +pub mod add; \ No newline at end of file diff --git a/src/models/index.rs b/src/models/index.rs index 65e00ea..257a664 100644 --- a/src/models/index.rs +++ b/src/models/index.rs @@ -1,11 +1,13 @@ use std::collections::HashMap; -use std::path::PathBuf; +use std::fs; +use std::path::{Path, PathBuf}; use std::time::SystemTime; +use serde::{Deserialize, Serialize}; use crate::models::object::Hash; // 文件元数据结构 -#[derive(Debug, Clone)] -struct FileMetaData { +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct FileMetaData { hash: Hash, // SHA-1 哈希值 size: u64, // 文件大小 created_time: SystemTime, // 创建时间 @@ -14,11 +16,56 @@ struct FileMetaData { } // 索引数据结构 -#[derive(Debug, Default)] -struct Index { +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Index { entries: HashMap, } +impl Index { + // 创建索引 + pub(crate) fn new() -> Index { + let mut index = Index { + entries: HashMap::new(), + }; + index.load(); + return index; + } + + // 添加文件 + fn add(&mut self, path: PathBuf, data: FileMetaData) { + self.entries.insert(path, data); + } + + // 删除文件 + fn remove(&mut self, path: PathBuf) { + self.entries.remove(&path); + } + + // 获取文件元数据 + fn get(&self, path: PathBuf) -> Option<&FileMetaData> { + self.entries.get(&path) + } + + // 获取所有文件元数据 + fn get_all(&self) -> &HashMap { + &self.entries + } + + pub fn contains(&self, path: &Path) -> bool { + self.entries.contains_key(path) + } + + fn load(&mut self) { + + } + + /// 二进制序列化 + fn save(&self) { + let ser = serde_json::to_string(&self).unwrap(); + println!("{}", ser); + } +} + #[cfg(test)] mod tests { use std::fs; diff --git a/src/utils/util.rs b/src/utils/util.rs index b1edad5..f87f1d6 100644 --- a/src/utils/util.rs +++ b/src/utils/util.rs @@ -1,5 +1,9 @@ +use std::{fs, io}; +use std::path::{Path, PathBuf}; use sha1::{Digest, Sha1}; +pub const ROOT_DIR: &str = ".mit"; + pub fn calc_hash(data: &String) -> String { let mut hasher = Sha1::new(); hasher.update(data); @@ -16,6 +20,12 @@ pub fn storage_exist() -> bool { } } +pub fn check_repo_exist() { + if !storage_exist() { + panic!("不是合法的mit仓库"); + } +} + pub fn get_storage_path() -> Result { /*递归获取储存库 */ let mut current_dir = std::env::current_dir()?; @@ -34,11 +44,43 @@ pub fn get_storage_path() -> Result { } } +/// 获取项目工作区目录, 也就是.mit的父目录 +pub fn get_working_dir() -> Option { + if let Some(path) = PathBuf::from(get_storage_path().unwrap()).parent() { + Some(path.to_path_buf()) + } else { + None + } +} + pub fn format_time(time: &std::time::SystemTime) -> String { let datetime: chrono::DateTime = time.clone().into(); datetime.format("%Y-%m-%d %H:%M:%S.%3f").to_string() } +/// 递归遍历给定目录及其子目录,列出所有文件,除了.mit +pub fn list_files(path: &Path) -> io::Result> { + let mut files = Vec::new(); + + if path.is_dir() { + if path.file_name().unwrap_or_default() == ROOT_DIR { // 跳过 .mit 目录 + return Ok(files); + } + for entry in fs::read_dir(path)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + // 递归遍历子目录 + files.extend(list_files(&path)?); + } else { + // 将文件的路径添加到列表中 + files.push(path); + } + } + } + Ok(files) +} + #[cfg(test)] mod tests { use super::*; @@ -61,4 +103,17 @@ mod tests { let formatted_time = format_time(&time); println!("{}", formatted_time); } + + #[test] + fn test_list_files() { + let files = list_files(Path::new("F:\\Git-Test\\list-test")); + match files { + Ok(files) => { + for file in files { + println!("{}", file.display()); + } + } + Err(err) => println!("{}", err), + } + } } \ No newline at end of file