mirror of
https://github.com/MrBeanCpp/MIT.git
synced 2026-03-24 05:51:16 +08:00
实现:status命令
TODO:add 不存在的文件报错
This commit is contained in:
@@ -4,6 +4,7 @@ use mit::commands::commit::commit;
|
||||
use mit::commands::init::init;
|
||||
use mit::commands::log::log;
|
||||
use mit::commands::remove::remove;
|
||||
use mit::commands::status::status;
|
||||
|
||||
/// Rust实现的简易版本的Git,用于学习Rust语言
|
||||
#[derive(Parser)]
|
||||
@@ -51,6 +52,8 @@ enum Command {
|
||||
#[clap(long, action)]
|
||||
allow_empty: bool,
|
||||
},
|
||||
/// 查看当前状态
|
||||
Status,
|
||||
/// log 现实提交历史
|
||||
Log {
|
||||
#[clap(short = 'A', long)]
|
||||
@@ -75,6 +78,9 @@ pub fn handle_command() {
|
||||
Command::Commit { message, allow_empty } => {
|
||||
commit(message, allow_empty);
|
||||
}
|
||||
Command::Status => {
|
||||
status();
|
||||
}
|
||||
Command::Log { all, number } => {
|
||||
log(all, number);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ use crate::utils::util::{
|
||||
ROOT_DIR,
|
||||
};
|
||||
|
||||
// TODO: fatal: ../moj/app.py: '../moj/app.py' is outside repository at 'Git-Rust'
|
||||
pub fn add(files: Vec<String>, all: bool, mut update: bool) {
|
||||
check_repo_exist();
|
||||
let mut index = Index::new();
|
||||
@@ -53,7 +52,8 @@ pub fn add(files: Vec<String>, all: bool, mut update: bool) {
|
||||
}
|
||||
|
||||
fn add_a_file(file: &Path, index: &mut Index) {
|
||||
if !is_inside_workdir(file) {
|
||||
//TODO 文件不存在会报错
|
||||
if !is_inside_workdir(file) && file.exists() {
|
||||
//文件不在工作区内
|
||||
println!("fatal: '{}' is outside repository at '{}'", file.display(), get_working_dir().unwrap().display());
|
||||
return;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use crate::head::Head;
|
||||
use crate::models::commit::Commit;
|
||||
use crate::models::index::Index;
|
||||
use crate::utils::util::check_repo_exist;
|
||||
use crate::{head, utils::util};
|
||||
use colored::Colorize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::utils::util::to_workdir_absolute_path;
|
||||
use crate::{
|
||||
head,
|
||||
models::{commit, index},
|
||||
utils::util,
|
||||
};
|
||||
|
||||
/** 获取需要commit的更改(staged) */
|
||||
/** 获取需要commit的更改(staged)
|
||||
注:相对路径
|
||||
*/
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Changes {
|
||||
pub new: Vec<PathBuf>,
|
||||
@@ -15,9 +16,15 @@ pub struct Changes {
|
||||
pub deleted: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl Changes {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.new.is_empty() && self.modified.is_empty() && self.deleted.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn changes_to_be_committed() -> Changes {
|
||||
let mut change = Changes::default();
|
||||
let index = index::Index::new();
|
||||
let index = Index::new();
|
||||
let head_hash = head::current_head_commit();
|
||||
let tracked_files = index
|
||||
.get_tracked_files()
|
||||
@@ -30,7 +37,7 @@ pub fn changes_to_be_committed() -> Changes {
|
||||
return change;
|
||||
}
|
||||
|
||||
let commit = commit::Commit::load(&head_hash);
|
||||
let commit = Commit::load(&head_hash);
|
||||
let tree = commit.get_tree();
|
||||
let tree_files = tree.get_recursive_blobs(); //相对路径
|
||||
let index_files: Vec<PathBuf> = tracked_files;
|
||||
@@ -38,7 +45,7 @@ pub fn changes_to_be_committed() -> Changes {
|
||||
for (tree_file, blob_hash) in tree_files.iter() {
|
||||
let index_file = index_files.iter().find(|&f| f == tree_file);
|
||||
if let Some(index_file) = index_file {
|
||||
let index_path = to_workdir_absolute_path(index_file);
|
||||
let index_path = util::to_workdir_absolute_path(index_file);
|
||||
if !index.verify_hash(&index_path, blob_hash) {
|
||||
change.modified.push(tree_file.clone());
|
||||
}
|
||||
@@ -55,8 +62,29 @@ pub fn changes_to_be_committed() -> Changes {
|
||||
change
|
||||
}
|
||||
|
||||
pub fn changes_to_be_staged() {
|
||||
unimplemented!()
|
||||
pub fn changes_to_be_staged() -> Changes {
|
||||
let mut change = Changes::default();
|
||||
let index = Index::new();
|
||||
for file in index.get_tracked_files() {
|
||||
//TODO 考虑当前目录
|
||||
if !file.exists() {
|
||||
change.deleted.push(util::to_workdir_relative_path(&file));
|
||||
} else if index.is_modified(&file) {
|
||||
// 若文件元数据被修改,才需要比较暂存区与文件的hash来判别内容修改
|
||||
if !index.verify_hash(&file, &util::calc_file_hash(&file)) {
|
||||
change.modified.push(util::to_workdir_relative_path(&file));
|
||||
}
|
||||
}
|
||||
}
|
||||
let files = util::list_workdir_files(); //TODO 考虑当前目录
|
||||
for file in files {
|
||||
if !index.tracked(&file) {
|
||||
//文件未被跟踪
|
||||
change.new.push(util::to_workdir_relative_path(&file));
|
||||
}
|
||||
}
|
||||
|
||||
change
|
||||
}
|
||||
|
||||
/** 分为两个部分
|
||||
@@ -64,7 +92,60 @@ pub fn changes_to_be_staged() {
|
||||
2. staged to be committed: 暂存区与HEAD(最后一次Commit::Tree)比较,即上次的暂存区
|
||||
*/
|
||||
pub fn status() {
|
||||
unimplemented!()
|
||||
check_repo_exist();
|
||||
//TODO: 输出文件与当前所在目录有关 输出时过滤
|
||||
match head::current_head() {
|
||||
Head::Detached(commit) => {
|
||||
println!("HEAD detached at {}", commit[0..7].to_string());
|
||||
}
|
||||
Head::Branch(branch) => {
|
||||
println!("On branch {}", branch);
|
||||
}
|
||||
}
|
||||
|
||||
let staged = changes_to_be_committed();
|
||||
let unstaged = changes_to_be_staged();
|
||||
if staged.is_empty() && unstaged.is_empty() {
|
||||
println!("nothing to commit, working tree clean");
|
||||
return;
|
||||
}
|
||||
|
||||
if !staged.is_empty() {
|
||||
println!("Changes to be committed:");
|
||||
staged.deleted.iter().for_each(|f| {
|
||||
let str = format!("\tdeleted: {}", f.display());
|
||||
println!("{}", str.bright_green());
|
||||
});
|
||||
staged.modified.iter().for_each(|f| {
|
||||
let str = format!("\tmodified: {}", f.display());
|
||||
println!("{}", str.bright_green());
|
||||
});
|
||||
staged.new.iter().for_each(|f| {
|
||||
let str = format!("\tnew file: {}", f.display());
|
||||
println!("{}", str.bright_green());
|
||||
});
|
||||
}
|
||||
|
||||
if !unstaged.deleted.is_empty() || !unstaged.modified.is_empty() {
|
||||
println!("Changes not staged for commit:");
|
||||
println!(" use \"mit add <file>...\" to update what will be committed");
|
||||
unstaged.deleted.iter().for_each(|f| {
|
||||
let str = format!("\tdeleted: {}", f.display());
|
||||
println!("{}", str.bright_red());
|
||||
});
|
||||
unstaged.modified.iter().for_each(|f| {
|
||||
let str = format!("\tmodified: {}", f.display());
|
||||
println!("{}", str.bright_red());
|
||||
});
|
||||
}
|
||||
if !unstaged.new.is_empty() {
|
||||
println!("Untracked files:");
|
||||
println!(" use \"mit add <file>...\" to include in what will be committed");
|
||||
unstaged.new.iter().for_each(|f| {
|
||||
let str = format!("\t{}", f.display());
|
||||
println!("{}", str.bright_red());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -90,6 +90,11 @@ impl Index {
|
||||
self.entries.contains_key(&path)
|
||||
}
|
||||
|
||||
/// 检查文件是否被跟踪, same as [Index::contains]
|
||||
pub fn tracked(&self, path: &Path) -> bool {
|
||||
self.contains(path)
|
||||
}
|
||||
|
||||
/// 与暂存区比较,获取工作区中被删除的文件
|
||||
pub fn get_deleted_files(&self) -> Vec<PathBuf> {
|
||||
let mut files = Vec::new();
|
||||
@@ -172,7 +177,7 @@ impl Index {
|
||||
impl Drop for Index {
|
||||
fn drop(&mut self) {
|
||||
self.save();
|
||||
println!("{}", "Index auto saved".bright_green());
|
||||
// println!("{}", "Index auto saved".bright_green());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,7 @@ pub fn ensure_test_file(path: &Path, content: option::Option<&str>) {
|
||||
file.write(content.as_bytes()).unwrap();
|
||||
} else {
|
||||
// 写入文件名
|
||||
file.write(path.file_name().unwrap().to_str().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
file.write(path.file_name().unwrap().to_str().unwrap().as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +79,12 @@ pub fn calc_hash(data: &String) -> String {
|
||||
hex::encode(hash)
|
||||
}
|
||||
|
||||
/// 计算文件的hash
|
||||
pub fn calc_file_hash(path: &Path) -> String {
|
||||
let data = fs::read_to_string(path).expect(&format!("无法读取文件:{}", path.display()));
|
||||
calc_hash(&data)
|
||||
}
|
||||
|
||||
pub fn storage_exist() -> bool {
|
||||
/*检查是否存在储存库 */
|
||||
let rt = get_storage_path();
|
||||
@@ -91,6 +96,7 @@ pub fn storage_exist() -> bool {
|
||||
|
||||
pub fn check_repo_exist() {
|
||||
if !storage_exist() {
|
||||
println!("fatal: not a mit repository (or any of the parent directories): .mit");
|
||||
panic!("不是合法的mit仓库");
|
||||
}
|
||||
}
|
||||
@@ -172,6 +178,15 @@ pub fn list_files(path: &Path) -> io::Result<Vec<PathBuf>> {
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
/// 列出工作区所有文件(包括子文件夹)
|
||||
pub fn list_workdir_files() -> Vec<PathBuf> {
|
||||
if let Ok(files) = list_files(&get_working_dir().unwrap()) {
|
||||
files
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取相对于dir的相对路径
|
||||
pub fn get_relative_path(path: &Path, dir: &Path) -> PathBuf {
|
||||
let path = if path.is_relative() {
|
||||
@@ -252,6 +267,7 @@ pub fn clean_win_abs_path_pre(path: PathBuf) -> PathBuf {
|
||||
|
||||
/// 获取绝对路径(相对于当前current_dir)
|
||||
pub fn get_absolute_path(path: &Path) -> PathBuf {
|
||||
//TODO 不能处理不存在的文件
|
||||
if path.is_absolute() {
|
||||
path.to_path_buf()
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user