Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 73 additions & 11 deletions libra/src/command/log.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::cmp::min;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};

use crate::command::load_object;
use crate::internal::branch::Branch;
Expand Down Expand Up @@ -79,32 +79,74 @@ pub async fn execute(args: LogArgs) {
// default sort with signature time
reachable_commits.sort_by(|a, b| b.committer.timestamp.cmp(&a.committer.timestamp));

let branch_commits = create_branch_commits_map().await;

let max_output_number = min(args.number.unwrap_or(usize::MAX), reachable_commits.len());
let mut output_number = 0;
for commit in reachable_commits {
if output_number >= max_output_number {
break;
}
output_number += 1;

let branches = branch_commits.get(&commit.id).cloned().unwrap_or_default();

let message = if args.oneline {
// Oneline format: <short_hash> <commit_message_first_line>
let short_hash = &commit.id.to_string()[..7];
let (msg, _) = parse_commit_msg(&commit.message);
format!("{} {}", short_hash.yellow(), msg)
if !branches.is_empty() {
let branch_info = format!(" ({})", branches.join(", "));
format!(
"{} {}{}",
short_hash.yellow().bold(),
msg,
branch_info.green()
)
} else {
format!("{} {}", short_hash.yellow(), msg)
}
} else {
// Default detailed format
let mut message = format!("{} {}", "commit".yellow(), &commit.id.to_string().yellow());
let mut message = format!(
"{} {}",
"commit".yellow(),
if !branches.is_empty() {
commit.id.to_string().yellow().bold()
} else {
commit.id.to_string().yellow()
}
);

// TODO other branch's head should shown branch name
// Show HEAD and branch info
if output_number == 1 {
message = format!("{} {}{}", message, "(".yellow(), "HEAD".blue());
if let Head::Branch(name) = head.to_owned() {
// message += &"-> ".blue();
// message += &head.name.as_ref().unwrap().green();
message = format!("{}{}{}", message, " -> ".blue(), name.green());
}
message = format!("{}{}", message, ")".yellow());
// For the first commit (HEAD), show HEAD info and all branches
let mut refs = vec![];
let current_branch = if let Head::Branch(name) = head.to_owned() {
refs.push(format!("{} -> {}", "HEAD".blue(), name.green()));
Some(name)
} else {
refs.push("HEAD".blue().to_string());
None
};

// Add other branches pointing to this commit (excluding current branch)
let other_branches: Vec<String> = branches
.iter()
.filter(|&b| current_branch.as_ref() != Some(b))
.map(|b| b.green().to_string())
.collect();

refs.extend(other_branches);

let ref_info = format!(" ({})", refs.join(", "));
message = format!("{message}{ref_info}");
} else if !branches.is_empty() {
// Show branch info for other commits that are branch heads
let branch_info = format!(" ({})", branches.join(", "));
message = format!("{}{}", message, branch_info.green());
}

message.push_str(&format!("\nAuthor: {}", commit.author));
let (msg, _) = parse_commit_msg(&commit.message);
message.push_str(&format!("\n{msg}\n"));
Expand All @@ -130,5 +172,25 @@ pub async fn execute(args: LogArgs) {
}
}

/// Create a map of commit hashes to branch names
async fn create_branch_commits_map() -> HashMap<SHA1, Vec<String>> {
let all_branches = Branch::list_branches(None).await;
let mut commit_to_branches: HashMap<SHA1, Vec<String>> = HashMap::new();

for branch in all_branches {
let branch_name = match &branch.remote {
Some(remote) => format!("{}/{}", remote, branch.name),
None => branch.name,
};

commit_to_branches
.entry(branch.commit)
.or_default()
.push(branch_name);
}

commit_to_branches
}

#[cfg(test)]
mod tests {}
13 changes: 3 additions & 10 deletions libra/src/command/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,7 @@ pub struct SwitchArgs {

pub async fn execute(args: SwitchArgs) {
// check status
let unstaged = status::changes_to_be_staged();
if !unstaged.deleted.is_empty() || !unstaged.modified.is_empty() {
status::execute().await;
eprintln!("fatal: uncommitted changes, can't switch branch");
return;
} else if !status::changes_to_be_committed().await.is_empty() {
status::execute().await;
eprintln!("fatal: unstaged changes, can't switch branch");
if check_status().await {
return;
}

Expand Down Expand Up @@ -66,11 +59,11 @@ pub async fn check_status() -> bool {
let unstaged: status::Changes = status::changes_to_be_staged();
if !unstaged.deleted.is_empty() || !unstaged.modified.is_empty() {
status::execute().await;
eprintln!("fatal: uncommitted changes, can't switch branch");
eprintln!("fatal: unstaged changes, can't switch branch");

Copilot AI Jul 25, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message is inconsistent with the condition. When checking unstaged deleted/modified files, the message should be 'fatal: uncommitted changes, can't switch branch' to match the original behavior described in the PR.

Suggested change
eprintln!("fatal: unstaged changes, can't switch branch");
eprintln!("fatal: uncommitted changes, can't switch branch");

Copilot uses AI. Check for mistakes.
true
} else if !status::changes_to_be_committed().await.is_empty() {
status::execute().await;
eprintln!("fatal: unstaged changes, can't switch branch");
eprintln!("fatal: uncommitted changes, can't switch branch");

Copilot AI Jul 25, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message is inconsistent with the condition. When checking staged changes (changes to be committed), the message should be 'fatal: unstaged changes, can't switch branch' to match the original behavior described in the PR.

Suggested change
eprintln!("fatal: uncommitted changes, can't switch branch");
eprintln!("fatal: unstaged changes, can't switch branch");

Copilot uses AI. Check for mistakes.
true
} else {
false
Expand Down
17 changes: 14 additions & 3 deletions mercury/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use std::{fmt::Display, io};

use bincode::{Encode, Decode};
use bincode::{Decode, Encode};
use colored::Colorize;
use serde::{Deserialize, Serialize};
use sha1::Digest;
Expand All @@ -28,8 +28,19 @@ use crate::internal::object::types::ObjectType;
/// understandable. - Nov 26, 2023 (by @genedna)
///
#[derive(
Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Deserialize, Serialize,
Encode, Decode
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Default,
Deserialize,
Serialize,
Encode,
Decode,
)]
pub struct SHA1(pub [u8; 20]);

Expand Down
1 change: 0 additions & 1 deletion mercury/src/internal/object/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,4 @@ mod tests {
"5dd01c177f5d7d1be5346a5bc18a569a7410c2ef"
);
}

}
6 changes: 3 additions & 3 deletions mercury/src/internal/object/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl ObjectTrait for Commit {
.as_str(),
)
.unwrap();
let binding = commit[tree_end + 1..].to_vec(); // Move past the tree id
let binding = commit[tree_end + 1..].to_vec(); // Move past the tree id
commit = &binding;

// Find the parent commit ids and remove them from the data
Expand Down Expand Up @@ -184,8 +184,8 @@ impl ObjectTrait for Commit {
// Find the author and committer and remove them from the data
// 0x0a is the newline character
let author =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

let binding = commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec();
commit = &binding;
let committer =
Expand Down
2 changes: 1 addition & 1 deletion mercury/src/internal/object/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl SignatureType {
}
}

#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize,Decode,Encode)]
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, Decode, Encode)]
pub struct Signature {
pub signature_type: SignatureType,
pub name: String,
Expand Down
3 changes: 2 additions & 1 deletion mercury/src/internal/object/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ impl ObjectTrait for Tag {
let tagger = Signature::from_data(tagger_data).unwrap();
data = &data[data.find_byte(0x0a).unwrap() + 1..];

let message = unsafe { // There may be non-UTF-8 characters, so we use `to_str_unchecked` for conversion.
let message = unsafe {
// There may be non-UTF-8 characters, so we use `to_str_unchecked` for conversion.
data[data.find_byte(0x0a).unwrap()..]
.to_vec()
.to_str_unchecked()
Expand Down
2 changes: 1 addition & 1 deletion mercury/src/internal/object/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::errors::GitError;
use crate::hash::SHA1;
use crate::internal::object::ObjectTrait;
use crate::internal::object::ObjectType;
use bincode::{Encode, Decode};
use bincode::{Decode, Encode};
use colored::Colorize;
use encoding_rs::GBK;
use serde::Deserialize;
Expand Down
2 changes: 1 addition & 1 deletion orion/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub struct BuildRequest {
pub target: String,
pub args: Option<Vec<String>>,
pub mr: String, // merge request id
// pub webhook: Option<String>, // post
// pub webhook: Option<String>, // post
}

#[derive(Debug, Serialize)]
Expand Down
8 changes: 3 additions & 5 deletions orion/src/buck_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use tokio::sync::mpsc::UnboundedSender;
static PROJECT_ROOT: Lazy<String> =
Lazy::new(|| std::env::var("BUCK_PROJECT_ROOT").expect("BUCK_PROJECT_ROOT must be set"));


/// Sends a filesystem mount request to the specified API endpoint
/// Parameters:
/// - repo: Repository path to mount
Expand All @@ -19,7 +18,7 @@ static PROJECT_ROOT: Lazy<String> =
pub async fn mount_fs(repo: &str, mr: &str) -> Result<String, reqwest::Error> {
// Create HTTP client
let client = reqwest::Client::new();

// Construct JSON request payload
let payload = json!({
"path": repo,
Expand All @@ -36,11 +35,11 @@ pub async fn mount_fs(repo: &str, mr: &str) -> Result<String, reqwest::Error> {

// Print status code
println!("Mount request status: {}", res.status());

// Get and return response body
let body = res.text().await?;
println!("Mount response body: {body}");

Ok(body)
}

Expand All @@ -52,7 +51,6 @@ pub async fn build(
mr: String,
sender: UnboundedSender<WSMessage>,
) -> io::Result<ExitStatus> {

tracing::info!("Building {} in repo {} with target {}", id, repo, target);
// Prepare the command to run
// Note: `args` is a list of additional arguments to pass to the `buck
Expand Down
17 changes: 12 additions & 5 deletions orion/src/ws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ pub enum WSMessage {

const MAX_RETRIES: u32 = 3;

/// send message and retry
async fn send_with_retry(sender: &mut (impl SinkExt<Message> + Unpin), msg: &WSMessage) -> Result<(), String> {
let msg_str = serde_json::to_string(msg)
.map_err(|e| format!("seriaz fail: {e}"))?;
/// send message and retry
async fn send_with_retry(
sender: &mut (impl SinkExt<Message> + Unpin),
msg: &WSMessage,
) -> Result<(), String> {
let msg_str = serde_json::to_string(msg).map_err(|e| format!("seriaz fail: {e}"))?;
let ws_msg = Message::Text(Utf8Bytes::from(msg_str));

for attempt in 0..=MAX_RETRIES {
Expand Down Expand Up @@ -121,7 +123,12 @@ async fn process_message(msg: Message) -> ControlFlow<(), ()> {
println!(">>> got task: id:{id}, repo:{repo}, target:{target}, args:{args:?}, mr:{mr}");
let Json(res) = buck_build(
id.parse().unwrap(),
BuildRequest { repo, target, args, mr },
BuildRequest {
repo,
target,
args,
mr,
},
SENDER.get().unwrap().clone(),
)
.await;
Expand Down
2 changes: 1 addition & 1 deletion scorpio/config.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
works = []
works = []
4 changes: 2 additions & 2 deletions scorpio/scorpio.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
lfs_url = "http://localhost:8000"
lfs_url = "http://git.gitmega.com"
store_path = "/tmp/megadir/store"
config_file = "config.toml"
git_author = "MEGA"
git_email = "admin@mega.org"
workspace = "/tmp/megadir/mount"
base_url = "http://localhost:8000"
base_url = "http://git.gitmega.com"
dicfuse_readable = "true"
load_dir_depth = "3"
fetch_file_thread = "10"
29 changes: 22 additions & 7 deletions scorpio/src/dicfuse/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,25 +234,40 @@ async fn fetch_dir(path: &str) -> Result<ApiResponseExt, DictionaryError> {

let response = match client.get(&url).send().await {
Ok(resp) => resp,
Err(_) => {
return Err(DictionaryError {
message: "Failed to fetch tree".to_string(),
Err(e) => {
eprintln!("Failed to fetch tree: {e}");

Copilot AI Jul 24, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using eprintln! for error logging is not ideal for production code. Consider using a proper logging framework like the log crate or tracing crate for structured error logging.

Copilot uses AI. Check for mistakes.
return Ok(ApiResponseExt {
_req_result: false,
data: Vec::new(),
_err_message: format!("Failed to fetch tree: {e}"),
});
}
};

let tree_info: TreeInfoResponse = match response.json().await {
Ok(info) => info,
Err(e) => {
return Err(DictionaryError {
message: format!("Failed to parse commit info: {e}"),
eprintln!("Failed to parse commit info: {e}");

Copilot AI Jul 24, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using eprintln! for error logging is not ideal for production code. Consider using a proper logging framework like the log crate or tracing crate for structured error logging.

Copilot uses AI. Check for mistakes.
return Ok(ApiResponseExt {
_req_result: false,
data: Vec::new(),
_err_message: format!("Failed to parse commit info: {e}"),
});
}
};

if !tree_info.req_result {

Copilot AI Jul 24, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented-out code blocks. If this code might be needed for reference, consider adding a TODO comment explaining why it's preserved, otherwise it should be deleted to improve code clarity.

Suggested change
if !tree_info.req_result {
if !tree_info.req_result {
// TODO: Consider using this error-handling approach if more detailed error propagation is required.

Copilot uses AI. Check for mistakes.
return Err(DictionaryError {
message: tree_info.err_message,
eprintln!(

Copilot AI Jul 24, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using eprintln! for error logging is not ideal for production code. Consider using a proper logging framework like the log crate or tracing crate for structured error logging.

Copilot uses AI. Check for mistakes.
"server response fetch dir error: {:?}",
tree_info.err_message
);
return Ok(ApiResponseExt {
_req_result: false,
data: Vec::new(),
_err_message: format!(
"server response fetch dir error: {:?}",
tree_info.err_message
),
});
}

Expand Down
Loading
Loading