Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libra] Adds the --bare option to the init command #809

Merged
merged 10 commits into from
Jan 25, 2025
9 changes: 8 additions & 1 deletion aria/contents/docs/libra/command/init/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ description:

### Usage

libra init
libra init [OPTIONS]

### Description

This command creates the necessary directories and files for a new Libra repository.
It also sets up the database and the initial configuration.
By default, the repository is initialized with a .libra directory.
You can initialize a bare repository with the --bare flag,
which sets up the repository without a working directory, containing only Libra data.

### Options

- `--bare`
Initialize a bare repository.<br>
**Note**:Unlike Git's `--bare`, the Libra init `--bare` initializes a bare repository
that contains Libra's own SQLite database.
- `-h`, `--help` Print help
6 changes: 3 additions & 3 deletions libra/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ enum Commands {

// Init and Clone are the only commands that can be executed without a repository
#[command(about = "Initialize a new repository")]
Init,
Init(command::init::InitArgs),
#[command(about = "Clone a repository into a new directory")]
Clone(command::clone::CloneArgs),

Expand Down Expand Up @@ -88,14 +88,14 @@ pub async fn parse_async(args: Option<&[&str]>) -> Result<(), GitError> {
None => Cli::parse(),
};
// TODO: try check repo before parsing
if let Commands::Init = args.command {
if let Commands::Init(_) = args.command {
} else if let Commands::Clone(_) = args.command {
} else if !utils::util::check_repo_exist() {
return Err(GitError::RepoNotFound);
}
// parse the command and execute the corresponding function with it's args
match args.command {
Commands::Init => command::init::execute().await,
Commands::Init(args) => command::init::execute(args).await,
Commands::Clone(args) => command::clone::execute(args).await,
Commands::Add(args) => command::add::execute(args).await,
Commands::Rm(args) => command::remove::execute(args).unwrap(),
Expand Down
5 changes: 3 additions & 2 deletions libra/src/command/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ pub async fn execute(args: CloneArgs) {

// CAUTION: change [current_dir] to the repo directory
env::set_current_dir(&local_path).unwrap();
command::init::execute().await;

let init_args = command::init::InitArgs { bare: false };
command::init::execute(init_args).await;

/* fetch remote */
let remote_config = RemoteConfig {
name: "origin".to_string(),
Expand Down
129 changes: 114 additions & 15 deletions libra/src/command/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,67 @@
//!
//!
//!
// 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 clap::Parser;

use crate::internal::db;
use crate::internal::model::{config, reference};
use crate::utils::util::{DATABASE, ROOT_DIR};
use std::path::Path;

#[derive(Parser, Debug)]
pub struct InitArgs {
/// Create a bare repository
#[clap(short, long, required = false)]
pub bare: bool, // Default is false
}

/// Execute the init function
pub async fn execute() {
init().await.unwrap();
pub async fn execute(args: InitArgs){
match init(args).await{
Ok(_) => {},
Err(e) => {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
}

/// Check if the repository has already been initialized based on the presence of the description file.
fn is_reinit(cur_dir: &Path) -> bool {
let bare_head_path = cur_dir.join("description");
let head_path = cur_dir.join(".libra/description");
// Check the presence of the description file
head_path.exists() || bare_head_path.exists()
}

/// 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<()> {
pub async fn init(args: InitArgs) -> 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);
let root_dir = if args.bare{
cur_dir.clone()
}else{
cur_dir.join(ROOT_DIR)
};

// Check if the root directory already exists
if root_dir.exists() {
if is_reinit(&cur_dir) {
println!("Already initialized - [{}]", root_dir.display());
return Ok(());

return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"Initialization failed: The repository is already initialized at the specified location.
If you wish to reinitialize, please remove the existing directory or file.",
));

}

// Create .libra & sub-dirs
Expand Down Expand Up @@ -66,17 +98,17 @@ pub async fn init() -> io::Result<()> {
.insert(&conn)
.await
.unwrap();



// 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> {
Expand Down Expand Up @@ -139,16 +171,83 @@ fn set_dir_hidden(_dir: &str) -> io::Result<()> {
/// Unit tests for the init module
#[cfg(test)]
mod tests {
use super::init;
use super::*;
use crate::utils::test;

/// Test the init function
pub fn verify_init(base_dir: &Path){

// List of subdirectories to verify
let dirs = ["objects/pack", "objects/info", "info"];

// Loop through the directories and verify they exist
for dir in dirs {
let dir_path = base_dir.join(dir);
assert!(dir_path.exists(), "Directory {} does not exist", dir);
}

// Additional file verification
let files = [
"description",
"libra.db",
"info/exclude",
];

for file in files {
let file_path = base_dir.join(file);
assert!(file_path.exists(), "File {} does not exist", file);
}
}
/// Test the init function with no parameters
#[tokio::test]
async fn test_init() {
// Set up the test environment without a Libra repository
test::setup_clean_testing_env();
let args = InitArgs {bare: false};
// Run the init function
init(args).await.unwrap();

// Verify that the `.libra` directory exists
let libra_dir = Path::new(".libra");
assert!(libra_dir.exists(), ".libra directory does not exist");

// Verify the contents of the other directory
verify_init(libra_dir);
}

//Test the init function with the --bare flag
#[tokio::test]
async fn test_init_bare() {
// Set up the test environment without a Libra repository
test::setup_clean_testing_env();
// Run the init function with --bare flag
let args = InitArgs {bare: true};
// Run the init function
init().await.unwrap();
init(args).await.unwrap();

let libra_dir = Path::new(".");
// Verify the contents of the other directory
verify_init(libra_dir);
}
//Test the init function with the --bare flag and an existing repository
#[tokio::test]
async fn test_init_bare_with_existing_repo() {
// Set up the test environment for a bare repository
test::setup_clean_testing_env();

// Initialize a bare repository
let init_args = InitArgs { bare: false };
init(init_args).await.unwrap(); // Execute init for bare repository

// Simulate trying to reinitialize the bare repo
let result = async {
let args = InitArgs { bare: true };
init(args).await
};

// Check for the error
let err = result.await.unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::AlreadyExists); // Check error type
assert!(err.to_string().contains("Initialization failed")); // Check error message contains "Already initialized"
}

}
25 changes: 20 additions & 5 deletions libra/src/utils/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,36 @@ pub fn setup_clean_testing_env() {
setup_env();

// Get the current directory
let mut path = util::cur_dir();
let cur_path = util::cur_dir();

// Append the Libra root directory to the current directory
path.push(util::ROOT_DIR);
let root_path=cur_path.join(util::ROOT_DIR);

// If the Libra root directory exists, remove it
if path.exists() {
fs::remove_dir_all(&path).unwrap();
if root_path.exists() {
fs::remove_dir_all(&root_path).unwrap();
}

// Define the directories that are present in a bare repository
let bare_repo_dirs = ["objects", "info", "description", "libra.db"];

// Remove the directories that are present in a bare repository if they exist
for dir in bare_repo_dirs.iter() {
let bare_repo_path = cur_path.join(dir);
if bare_repo_path.exists() && bare_repo_path.is_dir() {
fs::remove_dir_all(&bare_repo_path).unwrap();
} else if bare_repo_path.exists() && !bare_repo_path.is_dir() {
// Remove the file if it exists
fs::remove_file(&bare_repo_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();
let args = command::init::InitArgs { bare: false };
command::init::init(args).await.unwrap();
}

pub fn init_debug_logger() {
Expand Down
Loading