diff --git a/ceres/src/api_service/mod.rs b/ceres/src/api_service/mod.rs index eff9048b7..631cfda6e 100644 --- a/ceres/src/api_service/mod.rs +++ b/ceres/src/api_service/mod.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashMap, HashSet}, + collections::{HashMap, HashSet, VecDeque}, path::{Component, Path, PathBuf}, }; @@ -14,6 +14,7 @@ use mercury::{ tree::{Tree, TreeItem, TreeItemMode}, }, }; +use venus::monorepo::converter; use crate::model::{ create_file::CreateFileInfo, @@ -64,25 +65,32 @@ pub trait ApiHandler: Send + Sync { let filename = file_path.file_name().unwrap().to_str().unwrap(); let path = file_path.parent().unwrap(); let mut plain_text = String::new(); - let (_, tree) = self.search_tree_by_path(path).await?; - if let Some(item) = tree.tree_items.into_iter().find(|x| x.name == filename) { - plain_text = match self.get_raw_blob_by_hash(&item.id.to_plain_str()).await { - Ok(Some(model)) => String::from_utf8(model.data.unwrap()).unwrap(), - _ => String::new(), - }; + if let Some(tree) = self.search_tree_by_path(path).await? { + if let Some(item) = tree.tree_items.into_iter().find(|x| x.name == filename) { + plain_text = match self.get_raw_blob_by_hash(&item.id.to_plain_str()).await { + Ok(Some(model)) => String::from_utf8(model.data.unwrap()).unwrap(), + _ => String::new(), + }; + } } - Ok(plain_text) + return Ok(plain_text); } async fn get_latest_commit(&self, path: PathBuf) -> Result { - let (_, tree) = self.search_tree_by_path(&path).await.unwrap(); + let tree = if let Some(tree) = self.search_tree_by_path(&path).await? { + tree + } else { + return Err(GitError::CustomError( + "can't find target parent tree under latest commit".to_string(), + )); + }; let commit = self.get_tree_relate_commit(&tree.id.to_plain_str()).await; self.convert_commit_to_info(commit) } async fn get_tree_info(&self, path: PathBuf) -> Result, GitError> { - match self.search_tree_by_path(&path).await { - Ok((_, tree)) => { + match self.search_tree_by_path(&path).await? { + Some(tree) => { let mut items = Vec::new(); for item in tree.tree_items { let mut info: TreeBriefItem = item.clone().into(); @@ -94,13 +102,13 @@ pub trait ApiHandler: Send + Sync { } Ok(items) } - Err(_) => Ok(Vec::new()), + None => Ok(Vec::new()), } } async fn get_tree_commit_info(&self, path: PathBuf) -> Result, GitError> { - match self.search_tree_by_path(&path).await { - Ok((_, tree)) => { + match self.search_tree_by_path(&path).await? { + Some(tree) => { let mut item_to_commit = HashMap::new(); self.add_trees_to_map( @@ -153,7 +161,7 @@ pub trait ApiHandler: Send + Sync { } Ok(items) } - Err(_) => Ok(Vec::new()), + None => Ok(Vec::new()), } } @@ -191,7 +199,7 @@ pub trait ApiHandler: Send + Sync { /// /// Returns a tuple containing a vector of parent trees to be updated and /// the target tree if found, or an error of type `GitError`. - async fn search_tree_by_path(&self, path: &Path) -> Result<(Vec, Tree), GitError> { + async fn search_tree_for_update(&self, path: &Path) -> Result<(Vec, Tree), GitError> { let relative_path = self.strip_relative(path)?; let root_tree = self.get_root_tree().await; let mut search_tree = root_tree.clone(); @@ -224,6 +232,110 @@ pub trait ApiHandler: Send + Sync { Ok((update_tree, search_tree)) } + async fn search_tree_by_path(&self, path: &Path) -> Result, GitError> { + let relative_path = self.strip_relative(path)?; + let root_tree = self.get_root_tree().await; + let mut search_tree = root_tree.clone(); + for component in relative_path.components() { + // root tree already found + if component != Component::RootDir { + let target_name = component.as_os_str().to_str().unwrap(); + let search_res = search_tree + .tree_items + .iter() + .find(|x| x.name == target_name); + if let Some(search_res) = search_res { + let res = self.get_tree_by_hash(&search_res.id.to_plain_str()).await; + search_tree = res.clone(); + } else { + return Ok(None); + } + } + } + Ok(Some(search_tree)) + } + + // move to monorepo. + async fn search_and_create_tree(&self, path: &Path) -> Result, GitError> { + let relative_path = self.strip_relative(path)?; + let root_tree = self.get_root_tree().await; + let mut search_tree = root_tree.clone(); + let mut update_item_tree = VecDeque::new(); + update_item_tree.push_back((root_tree, Component::RootDir)); + let mut result = vec![]; + let mut stack: VecDeque<_> = VecDeque::new(); + + for component in relative_path.components() { + // skip rootdir + if component != Component::RootDir { + let target_name = component.as_os_str().to_str().unwrap(); + let search_res = search_tree + .tree_items + .iter() + .find(|x| x.name == target_name); + if let Some(search_res) = search_res { + let res = self.get_tree_by_hash(&search_res.id.to_plain_str()).await; + search_tree = res.clone(); + update_item_tree.push_back((res, component)); + } else { + stack.push_back(component) + } + } + } + + let blob = converter::generate_git_keep_with_timestamp(); + let new_item = TreeItem { + mode: TreeItemMode::Blob, + id: blob.id, + name: String::from(".gitkeep"), + }; + let mut last_tree = Tree::from_tree_items(vec![new_item]).unwrap(); + let mut last_tree_name = ""; + let mut first_element = true; + + while let Some(component) = stack.pop_back() { + if first_element { + first_element = false; + } else { + let new_item = TreeItem { + mode: TreeItemMode::Tree, + id: last_tree.id, + name: last_tree_name.to_owned(), + }; + last_tree = Tree::from_tree_items(vec![new_item]).unwrap(); + } + result.push(last_tree.clone()); + last_tree_name = component.as_os_str().to_str().unwrap(); + } + + let (new_item_tree, search_name) = update_item_tree.pop_back().unwrap(); + let new_item = TreeItem { + mode: TreeItemMode::Tree, + id: last_tree.id, + name: last_tree_name.to_owned(), + }; + let mut items = new_item_tree.tree_items.clone(); + items.push(new_item); + last_tree = Tree::from_tree_items(items).unwrap(); + result.push(last_tree.clone()); + + let mut replace_hash = last_tree.id; + let mut search_name = search_name.as_os_str().to_str().unwrap(); + while let Some((mut tree, component)) = update_item_tree.pop_back() { + let index = tree + .tree_items + .iter() + .position(|x| x.name == search_name) + .unwrap(); + tree.tree_items[index].id = replace_hash; + let new_tree = Tree::from_tree_items(tree.tree_items).unwrap(); + replace_hash = new_tree.id; + search_name = component.as_os_str().to_str().unwrap(); + result.push(new_tree) + } + Ok(result) + } + async fn reachable_in_tree( &self, root_tree: &Tree, diff --git a/ceres/src/api_service/mono_api_service.rs b/ceres/src/api_service/mono_api_service.rs index 2b1af7ffe..4e7270bc3 100644 --- a/ceres/src/api_service/mono_api_service.rs +++ b/ceres/src/api_service/mono_api_service.rs @@ -32,7 +32,7 @@ impl ApiHandler for MonoApiService { let path = PathBuf::from(file_info.path); let mut save_trees = vec![]; - let (update_trees, search_tree) = self.search_tree_by_path(&path).await.unwrap(); + let (update_trees, search_tree) = self.search_tree_for_update(&path).await.unwrap(); let mut t_items = search_tree.tree_items; let new_item = if file_info.is_directory { @@ -89,14 +89,18 @@ impl ApiHandler for MonoApiService { .unwrap(); save_trees.push(p_tree); - for save_t in save_trees { - let mut tree_model: mega_tree::Model = save_t.into(); - tree_model.commit_id.clone_from(&commit_id); - let tree_model: mega_tree::ActiveModel = tree_model.into(); - batch_save_model(storage.get_connection(), vec![tree_model]) - .await - .unwrap(); - } + let save_trees = save_trees + .into_iter() + .map(|save_t| { + let mut tree_model: mega_tree::Model = save_t.into(); + tree_model.commit_id.clone_from(&commit_id); + let tree_model: mega_tree::ActiveModel = tree_model.into(); + tree_model + }) + .collect(); + batch_save_model(storage.get_connection(), save_trees) + .await + .unwrap(); Ok(()) } @@ -297,7 +301,7 @@ impl MonoApiService { let path = PathBuf::from(mr.path.clone()); // beacuse only parent tree is needed so we skip current directory let (tree_vec, _) = self - .search_tree_by_path(path.parent().unwrap()) + .search_tree_for_update(path.parent().unwrap()) .await .unwrap(); self.update_parent_tree(path, tree_vec, commit) diff --git a/ceres/src/pack/import_repo.rs b/ceres/src/pack/import_repo.rs index 8c8bd90f3..e7a6cae55 100644 --- a/ceres/src/pack/import_repo.rs +++ b/ceres/src/pack/import_repo.rs @@ -13,9 +13,12 @@ use futures::{future::join_all, StreamExt}; use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; -use callisto::raw_blob; +use callisto::{mega_tree, raw_blob}; use common::errors::MegaError; -use jupiter::{context::Context, storage::GitStorageProvider}; +use jupiter::{ + context::Context, + storage::{batch_save_model, GitStorageProvider}, +}; use mercury::{ errors::GitError, internal::{ @@ -31,7 +34,6 @@ use venus::import_repo::{ use crate::{ api_service::{mono_api_service::MonoApiService, ApiHandler}, - model::create_file::CreateFileInfo, pack::handler::PackHandler, }; @@ -55,7 +57,7 @@ impl PackHandler for ImportRepo { } async fn handle_receiver(&self, receiver: Receiver) -> Result<(), GitError> { - self.create_tree_not_exist().await; + self.create_monorepo_parent().await.unwrap(); let storage = self.context.services.git_db_storage.clone(); let mut entry_list = vec![]; let mut join_tasks = vec![]; @@ -319,19 +321,55 @@ impl PackHandler for ImportRepo { } impl ImportRepo { - async fn create_tree_not_exist(&self) { + // create monorepo parent for preserve import repo + async fn create_monorepo_parent(&self) -> Result<(), GitError> { let path = PathBuf::from(self.repo.repo_path.clone()); - let f_name = path.file_name().unwrap().to_str().unwrap(); - let api_service = MonoApiService { + let mono_api_service = MonoApiService { context: self.context.clone(), }; - let req = CreateFileInfo { - is_directory: true, - name: f_name.to_owned(), - path: path.parent().unwrap().to_str().unwrap().to_owned(), - content: None, - }; + let storage = self.context.services.mega_storage.clone(); + let save_trees = mono_api_service.search_and_create_tree(&path).await?; + + let mut root_ref = storage.get_ref("/").await.unwrap().unwrap(); + let new_commit = Commit::from_tree_id( + save_trees[save_trees.len() - 1].id, + vec![SHA1::from_str(&root_ref.ref_commit_hash).unwrap()], + &format!( + "push thrid-part crates {:?} commit", + path.file_name().unwrap() + ), + ); + + let save_trees: Vec = save_trees + .into_iter() + .map(|tree| { + let mut model: mega_tree::Model = tree.into(); + model.commit_id = new_commit.id.to_plain_str(); + model.into() + }) + .collect(); + + batch_save_model(storage.get_connection(), save_trees) + .await + .unwrap(); + + root_ref.ref_commit_hash = new_commit.id.to_plain_str(); + root_ref.ref_tree_hash = new_commit.tree_id.to_plain_str(); + storage.update_ref(root_ref).await.unwrap(); + storage.save_mega_commits(vec![new_commit]).await.unwrap(); + Ok(()) + } +} - if (api_service.create_monorepo_file(req).await).is_ok() {} +#[cfg(test)] +mod test { + use std::path::PathBuf; + #[test] + pub fn test_recurse_tree() { + let path = PathBuf::from("/third-part/crates/tokio/tokio-console"); + let ancestors: Vec<_> = path.ancestors().collect(); + for path in ancestors.into_iter() { + println!("{:?}", path); + } } }