diff --git a/archived/README.md b/archived/README.md index f13537f92..91153a2de 100644 --- a/archived/README.md +++ b/archived/README.md @@ -3,7 +3,7 @@ This projects in archive folder has been officially archived. Please note the following: 1. **No Further Development** The projects is no longer actively developed. There will be no new features, enhancements, or updates. -2. **Maintenance Status** We will not be maintaining the project. This includes not addressing any new issues, bugs, or security vulnerabilities that may be discovered. +2. **Maintenance Status** We will not be maintaining this projects. This includes not addressing any new issues, bugs, or security vulnerabilities that may be discovered. 3. **Read-Only** The repository is now in a read-only state. We will not accept any pull requests, changes, or contributions. 4. **Documentation & Issues** Existing documentation and issues will remain accessible for reference, but no new issues can be raised, and existing ones will not be addressed. @@ -11,14 +11,17 @@ This projects in archive folder has been officially archived. Please note the fo 1. *ui* - The ui project will be replaced by a new project named [moon](../moon/README.md). 2. *sync* - There are no replaced plan for the sync project. -3. *build-bazel-tool* - The project will be replaced, but still not have a new project plan. +3. *build-bazel-tool* - The project will be abandon. The mega will use the [Buck2](https://buck2.build) as the build tool. 4. *website* - The website project will be replaced by a new project named [Mars](../mars/README.md) in the feature. 5. *mda* - The mda project still not have a replacement project. For now, we are working on decentralized LLM development. 6. *fuse* - There are an alternate project plan to rewrite the **fuse** within [OSPP](https://summer-ospp.ac.cn) 2024 program, will be named [Scorpion](../scorpio/README.md). 7. *kvcache* - The kvcache project still does not have a replacement project. 8. *p2p* - The p2p project will be replaced by a new project named [Gemini](../gemini/README.md). +9. *git* - The git project will be replaced by a new project named [Mercury](../mercury/README.md). +10. *storage* - The storage project will be replaced by a new project named [Venus](../venus/README.md). [Optional: Suggest any alternative tools, libraries, or projects that users can consider as a replacement for this project.] -Contact -If you have any questions or need further information, please feel free to reach out to [contact information]. \ No newline at end of file +## Contact + +If you interested in Mega, you can make an appointment with us on [Google Calendar](https://calendar.app.google/QuBf2sdmf68wVYWL7) to discuss your ideas, questions or problems, and we will share our vision and roadmap with you. diff --git a/libra/README.md b/libra/README.md index 00e08fb10..4be03a71a 100644 --- a/libra/README.md +++ b/libra/README.md @@ -1,5 +1,6 @@ ## Libra -`libra` is a `Git` Client in `Rust`. + +`Libra` is a partial implementation of a **Git** client, developed using **Rust**. Our goal is not to create a 100% replica of Git (for those interested in such a project, please refer to the [gitoxide](https://github.com/Byron/gitoxide)). Instead, `libra` focus on implementing the basic functionalities of Git for learning **Git** and **Rust**. A key feature of `libra` is the replacement of the original **Git** internal storage architecture with **SQLite**. ## Example ``` diff --git a/libra/src/command/init.rs b/libra/src/command/init.rs index 5d5e54a5a..c5d370938 100644 --- a/libra/src/command/init.rs +++ b/libra/src/command/init.rs @@ -1,79 +1,100 @@ +//! This module implements the `init` command for the Libra CLI. +//! +//! +//! +// Import necessary standard libraries +use std::{env, fs, io}; + +// Import necessary libraries from sea_orm +use sea_orm::{ActiveModelTrait, DbConn, DbErr, Set, TransactionTrait}; + +// Import necessary modules from the internal crate use crate::internal::db; use crate::internal::model::{config, reference}; use crate::utils::util::{DATABASE, ROOT_DIR}; -use sea_orm::{ActiveModelTrait, DbConn, DbErr, Set, TransactionTrait}; -use std::{env, fs, io}; +/// Execute the init function pub async fn execute() { init().await.unwrap(); } /// Initialize a new Libra repository +/// This function creates the necessary directories and files for a new Libra repository. +/// It also sets up the database and the initial configuration. #[allow(dead_code)] pub async fn init() -> io::Result<()> { + // Get the current directory let cur_dir = env::current_dir()?; + // Join the current directory with the root directory let root_dir = cur_dir.join(ROOT_DIR); + // Check if the root directory already exists if root_dir.exists() { println!("Already initialized - [{}]", root_dir.display()); return Ok(()); } - // create .libra & sub-dirs + // Create .libra & sub-dirs let dirs = ["objects/pack", "objects/info", "info"]; for dir in dirs { fs::create_dir_all(root_dir.join(dir))?; } - // create info/exclude + // Create info/exclude // `include_str!` includes the file content while compiling fs::write( root_dir.join("info/exclude"), include_str!("../../template/exclude"), )?; - // create .libra/description + // Create .libra/description fs::write( root_dir.join("description"), include_str!("../../template/description"), )?; - // create database: .libra/libra.db + // Create database: .libra/libra.db let database = root_dir.join(DATABASE); let conn = db::create_database(database.to_str().unwrap()).await?; - // create config table + // Create config table init_config(&conn).await.unwrap(); - // create HEAD + // Create HEAD reference::ActiveModel { name: Set(Some("master".to_owned())), kind: Set(reference::ConfigKind::Head), ..Default::default() // all others are `NotSet` } - .insert(&conn) - .await - .unwrap(); + .insert(&conn) + .await + .unwrap(); - // set .libra as hidden + // Set .libra as hidden set_dir_hidden(root_dir.to_str().unwrap())?; println!( "Initializing empty Libra repository in {}", root_dir.display() ); + Ok(()) } +/// Initialize the configuration for the Libra repository +/// This function creates the necessary configuration entries in the database. async fn init_config(conn: &DbConn) -> Result<(), DbErr> { + // Begin a new transaction let txn = conn.begin().await?; + // Define the configuration entries for non-Windows systems #[cfg(not(target_os = "windows"))] - let entries = [ + let entries = [ ("repositoryformatversion", "0"), ("filemode", "true"), ("bare", "false"), ("logallrefupdates", "true"), ]; + // Define the configuration entries for Windows systems #[cfg(target_os = "windows")] - let entries = [ + let entries = [ ("repositoryformatversion", "0"), ("filemode", "false"), // no filemode on windows ("bare", "false"), @@ -82,6 +103,7 @@ async fn init_config(conn: &DbConn) -> Result<(), DbErr> { ("ignorecase", "true"), // ignorecase on windows ]; + // Insert each configuration entry into the database for (key, value) in entries { // tip: Set(None) == NotSet == default == NULL let entry = config::ActiveModel { @@ -92,10 +114,13 @@ async fn init_config(conn: &DbConn) -> Result<(), DbErr> { }; entry.insert(&txn).await?; } + // Commit the transaction txn.commit().await?; Ok(()) } +/// Set a directory as hidden on Windows systems +/// This function uses the `attrib` command to set the directory as hidden. #[cfg(target_os = "windows")] fn set_dir_hidden(dir: &str) -> io::Result<()> { use std::process::Command; @@ -103,21 +128,27 @@ fn set_dir_hidden(dir: &str) -> io::Result<()> { Ok(()) } +/// On Unix-like systems, directories starting with a dot are hidden by default +/// Therefore, this function does nothing. #[cfg(not(target_os = "windows"))] fn set_dir_hidden(_dir: &str) -> io::Result<()> { // on unix-like systems, dotfiles are hidden by default Ok(()) } +/// Unit tests for the init module #[cfg(test)] mod tests { - use super::*; + use super::init; use crate::utils::test; + /// Test the init function #[tokio::test] async fn test_init() { - test::setup_without_libra(); + // Set up the test environment without a Libra repository + test::setup_clean_testing_env(); + + // Run the init function init().await.unwrap(); - // TODO check the result } -} +} \ No newline at end of file diff --git a/libra/src/main.rs b/libra/src/main.rs index 5818430c9..c711cb4f2 100644 --- a/libra/src/main.rs +++ b/libra/src/main.rs @@ -1,40 +1,46 @@ +//! This is the main entry point for the Libra. +//! It includes the definition of the CLI and the main function. +//! +//! use clap::{Parser, Subcommand}; + mod command; mod internal; mod utils; +// The Cli struct represents the root of the command line interface. #[derive(Parser, Debug)] -#[command(about = "Simulates git commands", version = "1.0")] +#[command(about = "Libra: A partial Git implemented in Rust", version = "0.1.0-pre")] struct Cli { #[command(subcommand)] command: Commands, } -/// Libra sub commands, similar to git -/// subcommands's excute and args are defined in `command` module +/// THe Commands enum represents the subcommands that can be used with the CLI. +/// subcommand's execute and args are defined in `command` module #[derive(Subcommand, Debug)] enum Commands { - // start a working area + // Each variant of the enum represents a subcommand. + // The about attribute provides a brief description of the subcommand. + // The arguments of the subcommand are defined in the command module. + + // Init and Clone are the only commands that can be executed without a repository #[command(about = "Initialize a new repository")] Init, #[command(about = "Clone a repository into a new directory")] Clone(command::clone::CloneArgs), - // work on the current change + // The rest of the commands require a repository to be present #[command(about = "Add file contents to the index")] Add(command::add::AddArgs), #[command(about = "Remove files from the working tree and from the index")] Rm(command::remove::RemoveArgs), #[command(about = "Restore working tree files")] Restore(command::restore::RestoreArgs), - - // examine the history and state #[command(about = "Show the working tree status")] Status, #[command(about = "Show commit logs")] Log(command::log::LogArgs), - - // grow, mark and tweak your common history #[command(about = "List, create, or delete branches")] Branch(command::branch::BranchArgs), #[command(about = "Record changes to the repository")] @@ -43,9 +49,6 @@ enum Commands { Switch(command::switch::SwitchArgs), #[command(about = "Merge changes")] Merge(command::merge::MergeArgs), - - // collaborate - // todo: implement in the future #[command(about = "Update remote refs along with associated objects")] Push(command::push::PushArgs), #[command(about = "Download objects and refs from another repository")] @@ -64,6 +67,8 @@ enum Commands { IndexPack(command::index_pack::IndexPackArgs), } +// The main function is the entry point of the Libra application. +// It parses the command-line arguments and executes the corresponding function. #[tokio::main] async fn main() { let args = Cli::parse(); @@ -109,5 +114,6 @@ async fn main() { #[test] fn verify_cli() { use clap::CommandFactory; + Cli::command().debug_assert() } diff --git a/libra/src/utils/test.rs b/libra/src/utils/test.rs index 0805b04a0..9c509807e 100644 --- a/libra/src/utils/test.rs +++ b/libra/src/utils/test.rs @@ -1,69 +1,100 @@ +//! This module contains the tests util functions for the Libra. +//! +//! +//! #![cfg(test)] use std::io::Write; use std::path::Path; use std::{env, fs, path::PathBuf}; -use super::util; +use crate::utils::util; use crate::command; pub const TEST_DIR: &str = "libra_test_repo"; -/* tools for test */ + fn find_cargo_dir() -> PathBuf { let cargo_path = env::var("CARGO_MANIFEST_DIR"); + match cargo_path { Ok(path) => PathBuf::from(path), Err(_) => { - // vscode DEBUG test没有CARGO_MANIFEST_DIR宏,手动尝试查找cargo.toml + // vscode DEBUG test does not have the CARGO_MANIFEST_DIR macro, manually try to find cargo.toml println!("CARGO_MANIFEST_DIR not found, try to find Cargo.toml manually"); let mut path = util::cur_dir(); + loop { path.push("Cargo.toml"); if path.exists() { break; } if !path.pop() { - panic!("找不到CARGO_MANIFEST_DIR"); + panic!("Could not find CARGO_MANIFEST_DIR"); } } + path.pop(); + path } } } -/// switch cur_dir to test_dir + +/// Sets up the environment for testing. +/// +/// This function performs the following steps: +/// 1. Installs the color_backtrace crate to provide colored backtraces. +/// 2. Finds the directory where the Cargo.toml file is located. +/// 3. Appends the test directory to the Cargo directory. +/// 4. If the test directory does not exist, it creates it. +/// 5. Sets the current directory to the test directory. fn setup_env() { - color_backtrace::install(); // colorize backtrace + // Install the color_backtrace crate to provide colored backtraces + color_backtrace::install(); + // Find the directory where the Cargo.toml file is located let mut path = find_cargo_dir(); + + // Append the test directory to the Cargo directory path.push(TEST_DIR); + + // If the test directory does not exist, create it if !path.exists() { fs::create_dir(&path).unwrap(); } - env::set_current_dir(&path).unwrap(); // 将执行目录切换到测试目录 -} - -// pub async fn init_repo() { -// crate::command::init().await.unwrap(); -// } -/// switch to test dir and create a new .libra -pub async fn setup_with_new_libra() { - setup_without_libra(); - command::init::init().await.unwrap(); + // Set the current directory to the test directory + env::set_current_dir(&path).unwrap(); } -/// switch to test dir and clean .libra -pub fn setup_without_libra() { +/// Sets up a clean environment for testing. +/// +/// This function first calls `setup_env()` to switch the current directory to the test directory. +/// Then, it checks if the Libra root directory (`.libra`) exists in the current directory. +/// If it does, the function removes the entire `.libra` directory. +pub fn setup_clean_testing_env() { + // Switch the current directory to the test directory setup_env(); + + // Get the current directory let mut path = util::cur_dir(); + + // Append the Libra root directory to the current directory path.push(util::ROOT_DIR); + + // If the Libra root directory exists, remove it if path.exists() { fs::remove_dir_all(&path).unwrap(); } } +/// switch to test dir and create a new .libra +pub async fn setup_with_new_libra() { + setup_clean_testing_env(); + command::init::init().await.unwrap(); +} + pub fn init_debug_logger() { tracing::subscriber::set_global_default( tracing_subscriber::fmt() diff --git a/libra/src/utils/util.rs b/libra/src/utils/util.rs index 345a103ab..690a25e0a 100644 --- a/libra/src/utils/util.rs +++ b/libra/src/utils/util.rs @@ -1,17 +1,29 @@ -use crate::utils::client_storage::ClientStorage; -use crate::utils::path; -use crate::utils::path_ext::PathExt; use path_abs::{PathAbs, PathInfo}; use std::collections::HashSet; use std::io::{BufReader, Read, Write}; use std::path::{Path, PathBuf}; use std::{env, fs, io}; + use mercury::hash::SHA1; use mercury::internal::object::types::ObjectType; +use crate::utils::client_storage::ClientStorage; +use crate::utils::path; +use crate::utils::path_ext::PathExt; + pub const ROOT_DIR: &str = ".libra"; pub const DATABASE: &str = "libra.db"; +/// Returns the current working directory as a `PathBuf`. +/// +/// This function wraps the `std::env::current_dir()` function and unwraps the result. +/// If the current directory value is not available for any reason, this function will panic. +/// +/// TODO - Add additional check result from `std::env::current_dir()` to handle the panic +/// +/// # Returns +/// +/// A `PathBuf` representing the current working directory. pub fn cur_dir() -> PathBuf { env::current_dir().unwrap() } @@ -35,6 +47,7 @@ pub fn try_get_storage_path() -> Result { } } } + /// Get the storage path of the repository, aka `.libra` /// - panics if the current directory is not a repository pub fn storage_path() -> PathBuf { @@ -274,9 +287,10 @@ pub fn is_cur_dir(dir: &Path) -> bool { } /// transform path to string, use '/' as separator even on windows +/// TODO test on windows +/// TODO maybe 'into_os_string().into_string().unwrap()' is good pub fn path_to_string(path: &Path) -> String { - path.to_string_lossy().to_string() // TODO: test on windows - // TODO maybe 'into_os_string().into_string().unwrap()' is good + path.to_string_lossy().to_string() } /// extend hash, panic if not valid or ambiguous @@ -322,6 +336,15 @@ pub fn read_sha1(file: &mut impl Read) -> io::Result { mod test { use super::*; use crate::utils::test; + use std::env; + use std::path::PathBuf; + + #[test] + fn cur_dir_returns_current_directory() { + let expected = env::current_dir().unwrap(); + let actual = cur_dir(); + assert_eq!(actual, expected); + } #[test] fn test_is_sub_path() {