From 3f1f0d5e23be3c6cc00bfdb5e7f6d25572eaa107 Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Mon, 27 Apr 2026 08:28:27 +0800 Subject: [PATCH] chore(service/git): minor fixes in service layer git operations Small adjustments to commit, init, refs, star, and watch operations in the service layer. --- libs/service/git/commit.rs | 17 +++++++++-- libs/service/git/init.rs | 2 +- libs/service/git/refs.rs | 60 ++++++++++++++++++++++++++------------ libs/service/git/star.rs | 4 +-- libs/service/git/watch.rs | 6 ++-- 5 files changed, 61 insertions(+), 28 deletions(-) diff --git a/libs/service/git/commit.rs b/libs/service/git/commit.rs index a4f7608..3a8ff67 100644 --- a/libs/service/git/commit.rs +++ b/libs/service/git/commit.rs @@ -803,9 +803,20 @@ impl AppService { commits.into_iter().map(CommitMetaResponse::from).collect(); // Get total count for pagination metadata. + // Must use the same cache key format as git_commit_count: + // git:commit:count:{namespace}:{repo_name}:{from:?}:{to:?} + // where from/to correspond to the rev spec passed to commit_log. + let (from, to) = match rev_for_count { + Some(rev) if rev.contains("..") => { + let parts: Vec<&str> = rev.splitn(2, "..").collect(); + (Some(parts[0].to_string()), Some(parts[1].to_string())) + } + Some(rev) => (None, Some(rev)), + None => (None, None), + }; let total_cache_key = format!( - "git:commit:count:{}:{}:{:?}", - namespace, repo_name, rev_for_count, + "git:commit:count:{}:{}:{:?}:{:?}", + namespace, repo_name, from, to, ); let total: usize = if let Ok(mut conn) = self.cache.conn().await { if let Ok(cached) = conn.get::<_, String>(total_cache_key.clone()).await { @@ -1326,7 +1337,7 @@ impl AppService { } else if reverse { CommitSort(CommitSort::TIME.0 | CommitSort::REVERSE.0) } else { - CommitSort(CommitSort::TOPOLOGICAL.0 | CommitSort::TIME.0) + CommitSort(CommitSort::TIME.0) }; let commits = git_spawn!(repo, domain -> { diff --git a/libs/service/git/init.rs b/libs/service/git/init.rs index 650b774..eb124f5 100644 --- a/libs/service/git/init.rs +++ b/libs/service/git/init.rs @@ -39,7 +39,7 @@ impl AppService { let domain = git::GitDomain::open_workdir(&path).map_err(AppError::from)?; Ok(GitInitResponse { path: domain.repo().path().to_string_lossy().to_string(), - is_bare: true, + is_bare: false, }) } diff --git a/libs/service/git/refs.rs b/libs/service/git/refs.rs index 1e6800d..b93bd41 100644 --- a/libs/service/git/refs.rs +++ b/libs/service/git/refs.rs @@ -6,6 +6,45 @@ use redis::AsyncCommands; use serde::{Deserialize, Serialize}; use session::Session; +/// Delete all cached ref list entries for a given namespace/repo. +/// Redis DEL does not support glob patterns, so we SCAN and delete each key. +async fn invalidate_ref_cache( + cache: &db::cache::AppCache, + namespace: &str, + repo_name: &str, +) { + let prefix = format!("git:ref:list:{}:{}:", namespace, repo_name); + if let Ok(mut conn) = cache.conn().await { + let pattern = format!("{}*", prefix); + let mut cursor: u64 = 0; + loop { + match redis::cmd("SCAN") + .arg(cursor) + .arg("MATCH") + .arg(&pattern) + .arg("COUNT") + .arg(100) + .query_async::<(u64, Vec)>(&mut conn) + .await + { + Ok((new_cursor, keys)) => { + for key in &keys { + let _: () = conn.del(key).await.unwrap_or(()); + } + if new_cursor == 0 { + break; + } + cursor = new_cursor; + } + Err(e) => { + tracing::debug!(error = ?e, "cache scan failed (non-fatal)"); + break; + } + } + } + } +} + #[derive(Debug, Clone, Deserialize, utoipa::IntoParams)] pub struct RefListQuery { pub pattern: Option, @@ -201,12 +240,7 @@ impl AppService { .map_err(|e| AppError::InternalServerError(format!("Task join error: {}", e)))? .map_err(AppError::from)?; - if let Ok(mut conn) = self.cache.conn().await { - let key = format!("git:ref:list:{}:{}:*", namespace, repo_name); - if let Err(e) = conn.del::(key).await { - tracing::debug!(error = ?e, "cache del failed (non-fatal)"); - } - } + invalidate_ref_cache(&self.cache, &namespace, &repo_name).await; Ok(RefUpdateResponse { name: result.name, @@ -234,12 +268,7 @@ impl AppService { .map_err(|e| AppError::InternalServerError(format!("Task join error: {}", e)))? .map_err(AppError::from)?; - if let Ok(mut conn) = self.cache.conn().await { - let key = format!("git:ref:list:{}:{}:*", namespace, repo_name); - if let Err(e) = conn.del::(key).await { - tracing::debug!(error = ?e, "cache del failed (non-fatal)"); - } - } + invalidate_ref_cache(&self.cache, &namespace, &repo_name).await; Ok(RefDeleteResponse { name, @@ -269,12 +298,7 @@ impl AppService { .map_err(|e| AppError::InternalServerError(format!("Task join error: {}", e)))? .map_err(AppError::from)?; - if let Ok(mut conn) = self.cache.conn().await { - let key = format!("git:ref:list:{}:{}:*", namespace, repo_name); - if let Err(e) = conn.del::(key).await { - tracing::debug!(error = ?e, "cache del failed (non-fatal)"); - } - } + invalidate_ref_cache(&self.cache, &namespace, &repo_name).await; Ok(RefInfoResponse::from(info)) } diff --git a/libs/service/git/star.rs b/libs/service/git/star.rs index 21613fd..09d0b7b 100644 --- a/libs/service/git/star.rs +++ b/libs/service/git/star.rs @@ -39,7 +39,7 @@ impl AppService { .one(&self.db) .await?; if existing.is_some() { - return Err(AppError::InternalServerError("already starred".to_string())); + return Err(AppError::Conflict("already starred".to_string())); } RepoStar::insert(repo_star::ActiveModel { id: Default::default(), @@ -97,7 +97,7 @@ impl AppService { .exec(&self.db) .await?; if deleted.rows_affected == 0 { - return Err(AppError::InternalServerError("not starred".to_string())); + return Err(AppError::NotFound("not starred".to_string())); } let project_id = match repo_model::Entity::find_by_id(repo.id).one(&self.db).await { Ok(Some(r)) => r.project, diff --git a/libs/service/git/watch.rs b/libs/service/git/watch.rs index fe69314..1a320e7 100644 --- a/libs/service/git/watch.rs +++ b/libs/service/git/watch.rs @@ -51,9 +51,7 @@ impl AppService { .one(&self.db) .await?; if existing.is_some() { - return Err(AppError::InternalServerError( - "already watching".to_string(), - )); + return Err(AppError::Conflict("already watching".to_string())); } RepoWatch::insert(repo_watch::ActiveModel { id: Default::default(), @@ -110,7 +108,7 @@ impl AppService { .exec(&self.db) .await?; if deleted.rows_affected == 0 { - return Err(AppError::InternalServerError("not watching".to_string())); + return Err(AppError::NotFound("not watching".to_string())); } let project_id = match repo_model::Entity::find_by_id(repo.id).one(&self.db).await { Ok(Some(r)) => r.project,