优化import导入和mod导出,简化使用

This commit is contained in:
mrbeanc
2023-12-26 14:59:06 +08:00
parent 9773a83ae2
commit dc0a3138ad
20 changed files with 106 additions and 139 deletions

View File

@@ -1,8 +1,5 @@
use clap::{ArgGroup, Parser, Subcommand};
use mit::commands::{
add::add, branch::branch, commit::commit, init::init, log::log, merge::merge, remove::remove, restore::restore,
status::status, switch::switch,
};
use mit::commands as cmd;
/// Rust实现的简易版本的Git用于学习Rust语言
#[derive(Parser)]
@@ -127,28 +124,28 @@ pub fn handle_command() {
let cli = Cli::parse();
match cli.command {
Command::Init => {
init().expect("初始化失败");
cmd::init().expect("初始化失败");
}
Command::Add { files, all, update } => {
add(files, all, update);
cmd::add(files, all, update);
}
Command::Rm { files, cached, recursive } => {
remove(files, cached, recursive).expect("删除失败");
cmd::rm(files, cached, recursive).expect("删除失败");
}
Command::Commit { message, allow_empty } => {
commit(message, allow_empty);
cmd::commit(message, allow_empty);
}
Command::Status => {
status();
cmd::status();
}
Command::Log { all, number } => {
log(all, number);
cmd::log(all, number);
}
Command::Branch { list, delete, new_branch, commit_hash, show_current } => {
branch(new_branch, commit_hash, list, delete, show_current);
cmd::branch(new_branch, commit_hash, list, delete, show_current);
}
Command::Switch { branch, create, detach } => {
switch(branch, create, detach);
cmd::switch(branch, create, detach);
}
Command::Restore { path, source, mut worktree, staged } => {
// 未指定stage和worktree时默认操作worktree
@@ -161,10 +158,10 @@ pub fn handle_command() {
If `--source` not specified, the contents are restored from `HEAD` if `--staged` is given,
otherwise from the [index].
*/
restore(path, source, worktree, staged);
cmd::restore(path, source, worktree, staged);
}
Command::Merge { branch } => {
merge(branch);
cmd::merge(branch);
}
}
}

View File

@@ -3,10 +3,8 @@ use std::path::{Path, PathBuf};
use colored::Colorize;
use crate::commands::status;
use crate::models::{
blob::Blob,
index::{FileMetaData, Index},
};
use crate::models::index::FileMetaData;
use crate::models::*;
use crate::utils::util;
/// add是对index的操作不会对工作区产生影响

View File

@@ -1,11 +1,6 @@
use colored::Colorize;
use crate::{
head,
models::{commit::Commit, object::Hash},
store,
utils::util,
};
use crate::{head, models::*, store, utils::util};
// branch error
enum BranchErr {
@@ -38,8 +33,8 @@ fn create_branch(branch_name: String, _base_commit: Hash) -> Result<(), BranchEr
let base_commit = Commit::load(&base_commit.unwrap());
let exist_branchs = head::list_local_branches();
if exist_branchs.contains(&branch_name) {
let exist_branches = head::list_local_branches();
if exist_branches.contains(&branch_name) {
println!("fatal: 分支 '{}' 已存在", branch_name);
return Err(BranchErr::BranchExist);
}

View File

@@ -1,12 +1,9 @@
use crate::{
head,
models::{commit, index},
};
use crate::{head, models::*};
use super::status;
pub fn commit(message: String, allow_empty: bool) {
let index = index::Index::new();
let index = Index::new();
if !allow_empty && status::changes_to_be_committed().is_empty() {
panic!("工作区没有任何改动,不需要提交");
}
@@ -16,9 +13,9 @@ pub fn commit(message: String, allow_empty: bool) {
let mut commit = {
if current_commit_hash.is_empty() {
commit::Commit::new(&index, vec![], message.clone())
Commit::new(&index, vec![], message.clone())
} else {
commit::Commit::new(&index, vec![current_commit_hash.clone()], message.clone())
Commit::new(&index, vec![current_commit_hash.clone()], message.clone())
}
};
let commit_hash = commit.save();
@@ -40,7 +37,7 @@ pub fn commit(message: String, allow_empty: bool) {
mod test {
use std::path::Path;
use crate::{commands, head, models, utils::util};
use crate::{commands as cmd, head, models, utils::util};
#[test]
#[should_panic]
@@ -58,8 +55,8 @@ mod test {
assert!(head_one.is_empty());
util::ensure_test_file(&Path::new(test_file), "test content".into());
commands::add::add(vec![], true, false);
commands::commit::commit("test commit 1".to_string(), true);
cmd::add(vec![], true, false);
cmd::commit("test commit 1".to_string(), true);
let head_two = head::current_head_commit();
assert!(head_two.len() > 0);

View File

@@ -1,4 +1,4 @@
use crate::{head, models::commit::Commit};
use crate::{head, models::Commit};
use colored::Colorize;
const DEFAULT_LOG_NUMBER: usize = 10;
@@ -8,7 +8,7 @@ pub fn log(all: bool, number: Option<usize>) {
let _ = __log(all, number);
}
pub fn __log(all: bool, number: Option<usize>) -> usize {
fn __log(all: bool, number: Option<usize>) -> usize {
let mut log_count = 0usize;
let head = head::current_head();

View File

@@ -1,10 +1,7 @@
use crate::{
commands::{
self,
status::{changes_to_be_committed, changes_to_be_staged},
},
commands::{self, status::*},
head,
models::{commit::Commit, object::Hash},
models::{Commit, Hash},
store::Store,
utils::util,
};

View File

@@ -1,10 +1,20 @@
pub mod add;
pub use add::add;
pub mod branch;
pub use branch::branch;
pub mod commit;
pub use commit::commit;
pub mod init;
pub use init::init;
pub mod log;
pub use log::log;
pub mod merge;
pub use merge::merge;
pub mod remove;
pub use remove::remove as rm;
pub mod restore;
pub use restore::restore;
pub mod status;
pub use status::status;
pub mod switch;
pub use switch::switch;

View File

@@ -1,13 +1,10 @@
use crate::{
models::index::Index,
utils::{util, util::check_repo_exist},
};
use crate::{models::Index, utils::util};
use colored::Colorize;
use std::{fs, io, path::PathBuf};
/// 从暂存区&|工作区删除文件
pub fn remove(files: Vec<String>, cached: bool, recursive: bool) -> io::Result<()> {
check_repo_exist();
util::check_repo_exist();
let mut index = Index::new();
for file in files.iter() {
let path = PathBuf::from(file);

View File

@@ -4,16 +4,7 @@ use std::{
path::PathBuf,
};
use crate::{
head,
models::{
commit::Commit,
index::{FileMetaData, Index},
object::Hash,
},
store::Store,
utils::{util, util::get_working_dir},
};
use crate::{head, models::*, store::Store, utils::util};
/// 统计[工作区]中相对于target_blobs已删除的文件根据filters进行过滤
fn get_worktree_deleted_files_in_filters(
@@ -51,7 +42,7 @@ 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
vec![util::get_working_dir().unwrap()] //None == all(workdir), '.' == cur_dir
}
}
@@ -222,20 +213,17 @@ pub fn restore(paths: Vec<String>, source: Option<String>, worktree: bool, stage
mod test {
use std::fs;
//TODO 写测试!
use std::path::PathBuf;
use crate::commands::add::add;
use crate::commands::restore::restore;
use crate::commands::status;
use crate::{models::index::Index, utils::util};
use crate::{commands as cmd, models::Index, utils::util};
use std::path::PathBuf;
#[test]
fn test_restore_stage() {
util::setup_test_with_empty_workdir();
let path = PathBuf::from("a.txt");
util::ensure_no_file(&path);
add(vec![], true, false); //add -A
restore(vec![".".to_string()], Some("HEAD".to_string()), false, true);
cmd::add(vec![], true, false); //add -A
cmd::restore(vec![".".to_string()], Some("HEAD".to_string()), false, true);
let index = Index::new();
assert!(index.get_tracked_files().is_empty());
}
@@ -246,10 +234,10 @@ mod test {
let files = vec!["a.txt", "b.txt", "c.txt", "test/in.txt"];
util::ensure_test_files(&files);
add(vec![], true, false);
cmd::add(vec![], true, false);
assert_eq!(status::changes_to_be_committed().new.iter().count(), 4);
restore(vec!["c.txt".to_string()], None, false, true); //restore c.txt --staged
cmd::restore(vec!["c.txt".to_string()], None, false, true); //restore c.txt --staged
assert_eq!(status::changes_to_be_committed().new.iter().count(), 3);
assert_eq!(status::changes_to_be_staged().new.iter().count(), 1);
@@ -257,7 +245,7 @@ mod test {
fs::remove_dir_all("test").unwrap(); //删除test文件夹
assert_eq!(status::changes_to_be_staged().deleted.iter().count(), 2);
restore(vec![".".to_string()], None, true, false); //restore . //from index
cmd::restore(vec![".".to_string()], None, true, false); //restore . //from index
assert_eq!(status::changes_to_be_committed().new.iter().count(), 3);
assert_eq!(status::changes_to_be_staged().new.iter().count(), 1);
assert_eq!(status::changes_to_be_staged().deleted.iter().count(), 0);

View File

@@ -1,8 +1,8 @@
use crate::{
head,
head::Head,
models::{commit::Commit, index::Index},
utils::{util, util::check_repo_exist},
models::{Commit, Index},
utils::util,
};
use colored::Colorize;
use std::path::PathBuf;
@@ -137,7 +137,7 @@ pub fn changes_to_be_staged() -> Changes {
2. staged to be committed: 暂存区与HEAD(最后一次Commit::Tree)比较,即上次的暂存区
*/
pub fn status() {
check_repo_exist();
util::check_repo_exist();
match head::current_head() {
Head::Detached(commit) => {
println!("HEAD detached at {}", commit[0..7].to_string());
@@ -197,10 +197,7 @@ pub fn status() {
#[cfg(test)]
mod tests {
use super::*;
use crate::{
commands::{self, commit},
utils::util,
};
use crate::{commands as cmd, utils::util};
use std::path::Path;
#[test]
@@ -209,8 +206,8 @@ mod tests {
let test_file = "a.txt";
util::ensure_test_file(Path::new(test_file), None);
commit::commit("test commit".to_string(), true);
commands::add::add(vec![test_file.to_string()], false, false);
cmd::commit("test commit".to_string(), true);
cmd::add(vec![test_file.to_string()], false, false);
let change = changes_to_be_committed();
assert_eq!(change.new.len(), 1);
assert_eq!(change.modified.len(), 0);
@@ -218,9 +215,9 @@ mod tests {
println!("{:?}", change.to_absolute());
commit::commit("test commit".to_string(), true);
cmd::commit("test commit".to_string(), true);
util::ensure_test_file(Path::new(test_file), Some("new content"));
commands::add::add(vec![test_file.to_string()], false, false);
cmd::add(vec![test_file.to_string()], false, false);
let change = changes_to_be_committed();
assert_eq!(change.new.len(), 0);
assert_eq!(change.modified.len(), 1);
@@ -228,8 +225,8 @@ mod tests {
println!("{:?}", change);
commit::commit("test commit".to_string(), true);
let _ = commands::remove::remove(vec![test_file.to_string()], false, false);
cmd::commit("test commit".to_string(), true);
let _ = cmd::rm(vec![test_file.to_string()], false, false);
let change = changes_to_be_committed();
assert_eq!(change.new.len(), 0);
assert_eq!(change.modified.len(), 0);

View File

@@ -2,7 +2,7 @@ use colored::Colorize;
use crate::{
head::{self},
models::{commit::Commit, object::Hash},
models::{Commit, Hash},
store::Store,
utils::util,
};
@@ -10,7 +10,7 @@ use crate::{
use super::{
branch,
restore::{restore_index, restore_worktree},
status::{changes_to_be_committed, changes_to_be_staged},
status,
};
enum SwitchErr {
@@ -33,10 +33,10 @@ fn switch_to_commit(commit_hash: Hash) {
fn switch_to(branch: String, detach: bool) -> Result<(), SwitchErr> {
// 检查更改
if !changes_to_be_staged().is_empty() {
if !status::changes_to_be_staged().is_empty() {
println!("fatal: 你有未暂存的更改,切换分支会导致更改丢失");
return Err(SwitchErr::NoClean);
} else if !changes_to_be_committed().is_empty() {
} else if !status::changes_to_be_committed().is_empty() {
println!("fatal: 你有未提交的更改,无法切换分支");
return Err(SwitchErr::NoClean);
}
@@ -85,21 +85,16 @@ pub fn switch(target_branch: Option<String>, create: Option<String>, detach: boo
#[cfg(test)]
mod test {
use std::{fs, path::PathBuf};
use crate::commands::{self, status};
use super::*;
use crate::commands::{self as cmd};
use std::path::PathBuf;
#[test]
fn test_switch() {
util::setup_test_with_clean_mit();
util::list_workdir_files().iter().for_each(|f| {
fs::remove_file(f).unwrap();
});
util::setup_test_with_empty_workdir();
commands::commit::commit("init".to_string(), true);
cmd::commit("init".to_string(), true);
let test_branch_1 = "test_branch_1".to_string();
commands::branch::branch(Some(test_branch_1.clone()), None, false, None, false);
cmd::branch(Some(test_branch_1.clone()), None, false, None, false);
/* test 1: NoClean */
let test_file_1 = PathBuf::from("test_file_1");
@@ -108,10 +103,10 @@ mod test {
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), SwitchErr::NoClean));
commands::add::add(vec![], true, false); // add all
commands::commit::commit("add file 1".to_string(), true);
cmd::add(vec![], true, false); // add all
cmd::commit("add file 1".to_string(), true);
let test_branch_2 = "test_branch_2".to_string();
commands::branch::branch(Some(test_branch_2.clone()), None, false, None, false); // branch2: test_file_1 exists
cmd::branch(Some(test_branch_2.clone()), None, false, None, false); // branch2: test_file_1 exists
/* test 2: InvalidBranch */
let result = switch_to("invalid_branch".to_string(), false);
@@ -125,14 +120,14 @@ mod test {
let tees_file_2 = PathBuf::from("test_file_2");
util::ensure_test_file(&tees_file_2, None);
commands::add::add(vec![], true, false); // add all
commands::commit::commit("add file 2".to_string(), false);
cmd::add(vec![], true, false); // add all
cmd::commit("add file 2".to_string(), false);
let history_commit = head::current_head_commit(); // commit: test_file_1 exists, test_file_2 exists
util::ensure_no_file(&test_file_1);
commands::add::add(vec![], true, false); // add all
cmd::add(vec![], true, false); // add all
assert!(!test_file_1.exists());
commands::commit::commit("delete file 1".to_string(), false);
cmd::commit("delete file 1".to_string(), false);
let branch_master = match head::current_head()/* master: test_file_1 not exists, test_file_2 exists */{
head::Head::Branch(branch) => branch,
_ => panic!("current head is not branch"),

View File

@@ -1,4 +1,4 @@
use crate::{models::object::Hash, utils::util};
use crate::{models::Hash, utils::util};
pub enum Head {
Detached(String),

View File

@@ -1,6 +1,6 @@
// 不使用lib.rs的话就无法在tests里引用到src中的模块
pub mod commands;
mod head;
pub mod head;
pub mod models;
mod store;
pub mod store;
pub mod utils;

View File

@@ -1,6 +1,5 @@
mod cli;
fn main() {
color_backtrace::install(); // colorize backtrace
cli::handle_command();
}

View File

@@ -1,4 +1,4 @@
use crate::{models::object::Hash, store::Store, utils::util::calc_hash};
use crate::{models::Hash, store::Store, utils::util};
use std::{fs, path::Path};
/**Blob<br>
@@ -14,7 +14,7 @@ impl Blob {
/// 从源文件新建blob对象并直接保存到/objects/中
pub fn new(file: &Path) -> Blob {
let data = fs::read_to_string(file).expect(format!("无法读取文件:{:?}", file).as_str());
let hash = calc_hash(&data);
let hash = util::calc_hash(&data);
let blob = Blob { hash, data };
blob.save();
blob

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{store, utils::util};
use super::{index::Index, object::Hash, tree::Tree};
use super::*;
/*Commit
* git中版本控制的单位。
* 一份Commit中对应一份版Tree记录了该版本所包含的文件parent记录本次commit的来源形成了版本树

View File

@@ -1,7 +1,4 @@
use crate::{
models::{blob::Blob, object::Hash},
utils::{util, util::get_relative_path},
};
use crate::{models::*, utils::util};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
@@ -171,7 +168,7 @@ impl Index {
.entries
.iter()
.map(|(path, value)| {
let relative_path = get_relative_path(path, &self.working_dir);
let relative_path = util::get_relative_path(path, &self.working_dir);
(relative_path, value.clone())
})
.collect();

View File

@@ -1,5 +1,11 @@
pub mod blob;
pub use blob::Blob;
pub mod commit;
pub use commit::Commit;
pub mod index;
pub use index::FileMetaData;
pub use index::Index;
pub mod object;
pub use object::Hash;
pub mod tree;
pub use tree::Tree;

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{store, utils::util};
use super::{index::Index, object::Hash};
use super::{Hash, Index};
/*Tree
* Tree是一个版本中所有文件的集合。从根目录还是每个目录是一个Tree每个文件是一个Blob。Tree之间互相嵌套表示文件的层级关系。
* 每一个Tree对象也是对应到git储存仓库的一个文件其内容是一个或多个TreeEntry。
@@ -157,18 +157,12 @@ impl Tree {
mod test {
use std::path::PathBuf;
use crate::{
models::{blob::Blob, index::FileMetaData},
utils::{
util,
util::{get_absolute_path, to_workdir_absolute_path},
},
};
use crate::{models::*, utils::util};
#[test]
fn test_new() {
util::setup_test_with_clean_mit();
let mut index = super::Index::new();
let mut index = Index::new();
for test_file in vec!["b.txt", "mit_src/a.txt", "test/test.txt"] {
let test_file = PathBuf::from(test_file);
util::ensure_test_file(&test_file, None);
@@ -176,7 +170,7 @@ mod test {
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
}
let tree = super::Tree::new(&index);
let tree = Tree::new(&index);
assert!(tree.entries.len() == 3);
assert!(tree.hash.len() != 0);
}
@@ -184,7 +178,7 @@ mod test {
#[test]
fn test_load() {
util::setup_test_with_clean_mit();
let mut index = super::Index::new();
let mut index = Index::new();
let test_files = vec!["b.txt", "mit_src/a.txt"];
for test_file in test_files.clone() {
let test_file = PathBuf::from(test_file);
@@ -192,10 +186,10 @@ mod test {
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
}
let tree = super::Tree::new(&index);
let tree = Tree::new(&index);
let tree_hash = tree.get_hash();
let loaded_tree = super::Tree::load(&tree_hash);
let loaded_tree = Tree::load(&tree_hash);
assert!(loaded_tree.entries.len() == tree.entries.len());
assert!(tree.entries[0].name == loaded_tree.entries[0].name);
assert!(tree.entries[1].name == loaded_tree.entries[1].name);
@@ -204,35 +198,35 @@ mod test {
#[test]
fn test_get_recursive_file_entries() {
util::setup_test_with_clean_mit();
let mut index = super::Index::new();
let mut index = Index::new();
let mut test_files = vec![PathBuf::from("b.txt"), PathBuf::from("mit_src/a.txt")];
for test_file in test_files.clone() {
util::ensure_test_file(&test_file, None);
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
}
let tree = super::Tree::new(&index);
let tree = Tree::new(&index);
let tree_hash = tree.get_hash();
let loaded_tree = super::Tree::load(&tree_hash);
let loaded_tree = Tree::load(&tree_hash);
let mut files = loaded_tree.get_recursive_file_entries();
files.sort();
test_files.sort();
assert_eq!(files.len(), test_files.len());
assert_eq!(
to_workdir_absolute_path(&files[0]).to_str().unwrap(), //TODO 罪大恶极的路径问题
get_absolute_path(&test_files[0]).to_str().unwrap()
util::to_workdir_absolute_path(&files[0]).to_str().unwrap(), //TODO 罪大恶极的路径问题
util::get_absolute_path(&test_files[0]).to_str().unwrap()
);
assert_eq!(
to_workdir_absolute_path(&files[1]).to_str().unwrap(),
get_absolute_path(&test_files[1]).to_str().unwrap()
util::to_workdir_absolute_path(&files[1]).to_str().unwrap(),
util::get_absolute_path(&test_files[1]).to_str().unwrap()
);
}
#[test]
fn test_get_recursive_blobs() {
util::setup_test_with_clean_mit();
let mut index = super::Index::new();
let mut index = Index::new();
let test_files = vec!["b.txt", "mit_src/a.txt"];
let mut test_blobs = vec![];
for test_file in test_files.clone() {
@@ -243,10 +237,10 @@ mod test {
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
}
let tree = super::Tree::new(&index);
let tree = Tree::new(&index);
let tree_hash = tree.get_hash();
let loaded_tree = super::Tree::load(&tree_hash);
let loaded_tree = Tree::load(&tree_hash);
let blobs = loaded_tree.get_recursive_blobs();
assert!(blobs.len() == test_files.len());
assert!(blobs.contains(&(PathBuf::from(test_files[0]), test_blobs[0].get_hash())));

View File

@@ -1,6 +1,6 @@
use std::path::PathBuf;
use crate::models::object::Hash;
use crate::models::Hash;
use super::utils::util;