1362 lines
39 KiB
Rust
1362 lines
39 KiB
Rust
use crate::AppService;
|
|
use crate::error::AppError;
|
|
use crate::git::{
|
|
CommitDiffFile, CommitDiffHunk, CommitDiffStats, CommitGraph, CommitMeta, CommitRefInfo,
|
|
CommitReflogEntry, CommitSignature, CommitSort, CommitWalkOptions,
|
|
};
|
|
use models::repos::repo;
|
|
use redis::AsyncCommands;
|
|
use serde::{Deserialize, Serialize};
|
|
use session::Session;
|
|
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct CommitGetQuery {
|
|
pub oid: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CommitMetaResponse {
|
|
pub oid: String,
|
|
pub message: String,
|
|
pub summary: String,
|
|
pub author: CommitSignatureResponse,
|
|
pub committer: CommitSignatureResponse,
|
|
pub tree_id: String,
|
|
pub parent_ids: Vec<String>,
|
|
pub encoding: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CommitSignatureResponse {
|
|
pub name: String,
|
|
pub email: String,
|
|
pub time_secs: i64,
|
|
pub offset_minutes: i32,
|
|
}
|
|
|
|
impl From<CommitSignature> for CommitSignatureResponse {
|
|
fn from(s: CommitSignature) -> Self {
|
|
Self {
|
|
name: s.name,
|
|
email: s.email,
|
|
time_secs: s.time_secs,
|
|
offset_minutes: s.offset_minutes,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CommitMeta> for CommitMetaResponse {
|
|
fn from(c: CommitMeta) -> Self {
|
|
Self {
|
|
oid: c.oid.to_string(),
|
|
message: c.message,
|
|
summary: c.summary,
|
|
author: CommitSignatureResponse::from(c.author),
|
|
committer: CommitSignatureResponse::from(c.committer),
|
|
tree_id: c.tree_id.to_string(),
|
|
parent_ids: c.parent_ids.into_iter().map(|p| p.to_string()).collect(),
|
|
encoding: c.encoding,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitExistsResponse {
|
|
pub oid: String,
|
|
pub exists: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitIsCommitResponse {
|
|
pub oid: String,
|
|
pub is_commit: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitMessageResponse {
|
|
pub oid: String,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitSummaryResponse {
|
|
pub oid: String,
|
|
pub summary: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitShortIdResponse {
|
|
pub oid: String,
|
|
pub short_id: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitAuthorResponse {
|
|
pub oid: String,
|
|
pub author: CommitSignatureResponse,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitTreeIdResponse {
|
|
pub oid: String,
|
|
pub tree_id: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitParentCountResponse {
|
|
pub oid: String,
|
|
pub parent_count: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitParentIdsResponse {
|
|
pub oid: String,
|
|
pub parent_ids: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitIsMergeResponse {
|
|
pub oid: String,
|
|
pub is_merge: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitIsTipResponse {
|
|
pub oid: String,
|
|
pub is_tip: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitRefCountResponse {
|
|
pub oid: String,
|
|
pub ref_count: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CommitCountResponse {
|
|
pub count: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitRefInfoResponse {
|
|
pub name: String,
|
|
pub target: String,
|
|
pub is_remote: bool,
|
|
pub is_tag: bool,
|
|
}
|
|
|
|
impl From<CommitRefInfo> for CommitRefInfoResponse {
|
|
fn from(r: CommitRefInfo) -> Self {
|
|
Self {
|
|
name: r.name,
|
|
target: r.target.to_string(),
|
|
is_remote: r.is_remote,
|
|
is_tag: r.is_tag,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitBranchesResponse {
|
|
pub oid: String,
|
|
pub branches: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitTagsResponse {
|
|
pub oid: String,
|
|
pub tags: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitReflogEntryResponse {
|
|
pub oid_new: String,
|
|
pub oid_old: String,
|
|
pub committer_name: String,
|
|
pub committer_email: String,
|
|
pub time_secs: i64,
|
|
pub message: Option<String>,
|
|
}
|
|
|
|
impl From<CommitReflogEntry> for CommitReflogEntryResponse {
|
|
fn from(e: CommitReflogEntry) -> Self {
|
|
Self {
|
|
oid_new: e.oid_new.to_string(),
|
|
oid_old: e.oid_old.to_string(),
|
|
committer_name: e.committer_name,
|
|
committer_email: e.committer_email,
|
|
time_secs: e.time_secs,
|
|
message: e.message,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CommitGraphResponse {
|
|
pub lines: Vec<CommitGraphLineResponse>,
|
|
pub max_parents: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CommitGraphLineResponse {
|
|
pub oid: String,
|
|
pub graph_chars: String,
|
|
pub refs: String,
|
|
pub short_message: String,
|
|
}
|
|
|
|
impl From<git::CommitGraphLine> for CommitGraphLineResponse {
|
|
fn from(l: git::CommitGraphLine) -> Self {
|
|
Self {
|
|
oid: l.oid.to_string(),
|
|
graph_chars: l.graph_chars,
|
|
refs: l.refs,
|
|
short_message: l.short_message,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CommitGraph> for CommitGraphResponse {
|
|
fn from(g: CommitGraph) -> Self {
|
|
Self {
|
|
lines: g
|
|
.lines
|
|
.into_iter()
|
|
.map(CommitGraphLineResponse::from)
|
|
.collect(),
|
|
max_parents: g.max_parents,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitDiffStatsResponse {
|
|
pub oid: String,
|
|
pub files_changed: usize,
|
|
pub insertions: usize,
|
|
pub deletions: usize,
|
|
}
|
|
|
|
impl From<CommitDiffStats> for CommitDiffStatsResponse {
|
|
fn from(s: CommitDiffStats) -> Self {
|
|
Self {
|
|
oid: String::new(),
|
|
files_changed: s.files_changed,
|
|
insertions: s.insertions,
|
|
deletions: s.deletions,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitDiffFileResponse {
|
|
pub path: Option<String>,
|
|
pub status: String,
|
|
pub is_binary: bool,
|
|
pub size: u64,
|
|
}
|
|
|
|
impl From<CommitDiffFile> for CommitDiffFileResponse {
|
|
fn from(f: CommitDiffFile) -> Self {
|
|
Self {
|
|
path: f.path,
|
|
status: f.status,
|
|
is_binary: f.is_binary,
|
|
size: f.size,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitDiffHunkResponse {
|
|
pub old_start: u32,
|
|
pub old_lines: u32,
|
|
pub new_start: u32,
|
|
pub new_lines: u32,
|
|
pub header: String,
|
|
}
|
|
|
|
impl From<CommitDiffHunk> for CommitDiffHunkResponse {
|
|
fn from(h: CommitDiffHunk) -> Self {
|
|
Self {
|
|
old_start: h.old_start,
|
|
old_lines: h.old_lines,
|
|
new_start: h.new_start,
|
|
new_lines: h.new_lines,
|
|
header: h.header,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitLogQuery {
|
|
pub rev: Option<String>,
|
|
pub limit: Option<usize>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitWalkQuery {
|
|
pub rev: Option<String>,
|
|
pub limit: Option<usize>,
|
|
#[serde(default)]
|
|
pub first_parent_only: bool,
|
|
#[serde(default)]
|
|
pub topological: bool,
|
|
#[serde(default)]
|
|
pub reverse: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitAncestorsQuery {
|
|
pub oid: String,
|
|
pub limit: Option<usize>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitDescendantsQuery {
|
|
pub oid: String,
|
|
pub limit: Option<usize>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitResolveQuery {
|
|
pub rev: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitCherryPickRequest {
|
|
pub cherrypick_oid: String,
|
|
pub author_name: String,
|
|
pub author_email: String,
|
|
pub committer_name: String,
|
|
pub committer_email: String,
|
|
pub message: Option<String>,
|
|
pub mainline: Option<u32>,
|
|
pub update_ref: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitCherryPickAbortRequest {
|
|
pub reset_type: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitRevertRequest {
|
|
pub revert_oid: String,
|
|
pub author_name: String,
|
|
pub author_email: String,
|
|
pub committer_name: String,
|
|
pub committer_email: String,
|
|
pub message: Option<String>,
|
|
pub mainline: Option<u32>,
|
|
pub update_ref: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitRevertAbortRequest {
|
|
pub reset_type: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
pub struct CommitCreateResponse {
|
|
pub oid: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitCreateRequest {
|
|
pub author_name: String,
|
|
pub author_email: String,
|
|
pub committer_name: String,
|
|
pub committer_email: String,
|
|
pub message: String,
|
|
pub tree_id: String,
|
|
pub parent_ids: Vec<String>,
|
|
pub update_ref: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitAmendRequest {
|
|
pub oid: String,
|
|
pub author_name: Option<String>,
|
|
pub author_email: Option<String>,
|
|
pub committer_name: Option<String>,
|
|
pub committer_email: Option<String>,
|
|
pub message: Option<String>,
|
|
pub message_encoding: Option<String>,
|
|
pub tree_id: Option<String>,
|
|
pub update_ref: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct CommitDiffQuery {
|
|
pub oid: String,
|
|
}
|
|
|
|
macro_rules! git_spawn {
|
|
($repo:expr, $domain:ident -> $body:expr) => {{
|
|
let repo_clone = $repo.clone();
|
|
tokio::task::spawn_blocking(move || {
|
|
let $domain = git::GitDomain::from_model(repo_clone)?;
|
|
$body
|
|
})
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(format!("Task join error: {}", e)))?
|
|
.map_err(AppError::from)
|
|
}};
|
|
}
|
|
|
|
impl AppService {
|
|
pub async fn git_commit_get(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitMetaResponse, AppError> {
|
|
let cache_key = format!(
|
|
"git:commit:get:{}:{}:{}",
|
|
namespace, repo_name, query.oid
|
|
);
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
if let Ok(cached) = conn.get::<_, String>(cache_key.clone()).await {
|
|
if let Ok(cached) = serde_json::from_str(&cached) {
|
|
return Ok(cached);
|
|
}
|
|
}
|
|
}
|
|
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let meta = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_get(&oid)
|
|
})?;
|
|
|
|
let response = CommitMetaResponse::from(meta);
|
|
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
let _: Option<()> = conn
|
|
.set_ex::<String, String, ()>(
|
|
cache_key,
|
|
serde_json::to_string(&response).unwrap_or_default(),
|
|
3600,
|
|
)
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
Ok(response)
|
|
}
|
|
|
|
pub async fn git_commit_exists(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitExistsResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let exists = tokio::task::spawn_blocking(move || {
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
Ok::<_, git::GitError>(domain.commit_exists(&oid))
|
|
})
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(format!("Task join error: {}", e)))?
|
|
.map_err(AppError::from)?;
|
|
|
|
Ok(CommitExistsResponse {
|
|
oid: query.oid,
|
|
exists,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_is_commit(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitIsCommitResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let is_commit = tokio::task::spawn_blocking(move || {
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
Ok::<_, git::GitError>(domain.commit_is_commit(&oid))
|
|
})
|
|
.await
|
|
.map_err(|e| AppError::InternalServerError(format!("Task join error: {}", e)))?
|
|
.map_err(AppError::from)?;
|
|
|
|
Ok(CommitIsCommitResponse {
|
|
oid: query.oid,
|
|
is_commit,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_message(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitMessageResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let message = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_message(&oid)
|
|
})?;
|
|
|
|
Ok(CommitMessageResponse {
|
|
oid: query.oid,
|
|
message,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_summary(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitSummaryResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let summary = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_summary(&oid)
|
|
})?;
|
|
|
|
Ok(CommitSummaryResponse {
|
|
oid: query.oid,
|
|
summary,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_short_id(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitShortIdResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let short_id = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_short_id(&oid)
|
|
})?;
|
|
|
|
Ok(CommitShortIdResponse {
|
|
oid: query.oid,
|
|
short_id,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_author(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitAuthorResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let author = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_author(&oid)
|
|
})?;
|
|
|
|
Ok(CommitAuthorResponse {
|
|
oid: query.oid,
|
|
author: CommitSignatureResponse::from(author),
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_tree_id(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitTreeIdResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let tree_id = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_tree_id(&oid)
|
|
})?;
|
|
|
|
Ok(CommitTreeIdResponse {
|
|
oid: query.oid,
|
|
tree_id: tree_id.to_string(),
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_parent_count(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitParentCountResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let parent_count = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_parent_count(&oid)
|
|
})?;
|
|
|
|
Ok(CommitParentCountResponse {
|
|
oid: query.oid,
|
|
parent_count,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_parent_ids(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitParentIdsResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let parent_ids = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_parent_ids(&oid)
|
|
})?;
|
|
|
|
Ok(CommitParentIdsResponse {
|
|
oid: query.oid,
|
|
parent_ids: parent_ids.into_iter().map(|p| p.to_string()).collect(),
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_parent(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
oid: String,
|
|
index: usize,
|
|
ctx: &Session,
|
|
) -> Result<CommitMetaResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = oid.clone();
|
|
|
|
let parent = git_spawn!(repo, domain -> {
|
|
let commit_oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_parent(&commit_oid, index)
|
|
})?;
|
|
|
|
Ok(CommitMetaResponse::from(parent))
|
|
}
|
|
|
|
pub async fn git_commit_first_parent(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<Option<CommitMetaResponse>, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let parent = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_first_parent(&oid)
|
|
})?;
|
|
|
|
Ok(parent.map(CommitMetaResponse::from))
|
|
}
|
|
|
|
pub async fn git_commit_is_merge(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitIsMergeResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let is_merge = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_is_merge(&oid)
|
|
})?;
|
|
|
|
Ok(CommitIsMergeResponse {
|
|
oid: query.oid,
|
|
is_merge,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_log(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitLogQuery,
|
|
ctx: &Session,
|
|
) -> Result<Vec<CommitMetaResponse>, AppError> {
|
|
let cache_key = format!(
|
|
"git:commit:log:{}:{}:{:?}:{}",
|
|
namespace,
|
|
repo_name,
|
|
query.rev,
|
|
query.limit.unwrap_or(0),
|
|
);
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
if let Ok(cached) = conn.get::<_, String>(cache_key.clone()).await {
|
|
if let Ok(cached) = serde_json::from_str(&cached) {
|
|
return Ok(cached);
|
|
}
|
|
}
|
|
}
|
|
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let rev_clone = query.rev.clone();
|
|
let limit = query.limit.unwrap_or(0);
|
|
|
|
let commits = git_spawn!(repo, domain -> {
|
|
domain.commit_log(rev_clone.as_deref(), limit)
|
|
})?;
|
|
|
|
let response: Vec<CommitMetaResponse> =
|
|
commits.into_iter().map(CommitMetaResponse::from).collect();
|
|
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
let _: Option<()> = conn
|
|
.set_ex::<String, String, ()>(
|
|
cache_key,
|
|
serde_json::to_string(&response).unwrap_or_default(),
|
|
300,
|
|
)
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
Ok(response)
|
|
}
|
|
|
|
pub async fn git_commit_count(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
from: Option<String>,
|
|
to: Option<String>,
|
|
ctx: &Session,
|
|
) -> Result<CommitCountResponse, AppError> {
|
|
let cache_key = format!(
|
|
"git:commit:count:{}:{}:{:?}:{:?}",
|
|
namespace, repo_name, from, to,
|
|
);
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
if let Ok(cached) = conn.get::<_, String>(cache_key.clone()).await {
|
|
if let Ok(cached) = serde_json::from_str(&cached) {
|
|
return Ok(cached);
|
|
}
|
|
}
|
|
}
|
|
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let from_clone = from.clone();
|
|
let to_clone = to.clone();
|
|
|
|
let count = git_spawn!(repo, domain -> {
|
|
domain.commit_count(from_clone.as_deref(), to_clone.as_deref())
|
|
})?;
|
|
|
|
let response = CommitCountResponse { count };
|
|
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
let _: Option<()> = conn
|
|
.set_ex::<String, String, ()>(
|
|
cache_key,
|
|
serde_json::to_string(&response).unwrap_or_default(),
|
|
300,
|
|
)
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
Ok(response)
|
|
}
|
|
|
|
pub async fn git_commit_refs(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<Vec<CommitRefInfoResponse>, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let refs = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_refs(&oid)
|
|
})?;
|
|
|
|
Ok(refs.into_iter().map(CommitRefInfoResponse::from).collect())
|
|
}
|
|
|
|
pub async fn git_commit_branches(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitBranchesResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let branches = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_branches(&oid)
|
|
})?;
|
|
|
|
Ok(CommitBranchesResponse {
|
|
oid: query.oid,
|
|
branches,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_tags(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitTagsResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let tags = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_tags(&oid)
|
|
})?;
|
|
|
|
Ok(CommitTagsResponse {
|
|
oid: query.oid,
|
|
tags,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_is_tip(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitIsTipResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let is_tip = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_is_tip(&oid)
|
|
})?;
|
|
|
|
Ok(CommitIsTipResponse {
|
|
oid: query.oid,
|
|
is_tip,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_ref_count(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitRefCountResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
|
|
let ref_count = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_ref_count(&oid)
|
|
})?;
|
|
|
|
Ok(CommitRefCountResponse {
|
|
oid: query.oid,
|
|
ref_count,
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_reflog(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitGetQuery,
|
|
refname: Option<String>,
|
|
ctx: &Session,
|
|
) -> Result<Vec<CommitReflogEntryResponse>, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
let refname_clone = refname.clone();
|
|
|
|
let entries = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_reflog(&oid, refname_clone.as_deref())
|
|
})?;
|
|
|
|
Ok(entries
|
|
.into_iter()
|
|
.map(CommitReflogEntryResponse::from)
|
|
.collect())
|
|
}
|
|
|
|
pub async fn git_commit_graph(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitWalkQuery,
|
|
ctx: &Session,
|
|
) -> Result<CommitGraphResponse, AppError> {
|
|
let cache_key = format!(
|
|
"git:commit:graph:{}:{}:{:?}:{}",
|
|
namespace,
|
|
repo_name,
|
|
query.rev,
|
|
query.limit.unwrap_or(0),
|
|
);
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
if let Ok(cached) = conn.get::<_, String>(cache_key.clone()).await {
|
|
if let Ok(cached) = serde_json::from_str(&cached) {
|
|
return Ok(cached);
|
|
}
|
|
}
|
|
}
|
|
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let rev_clone = query.rev.clone();
|
|
let limit = query.limit.unwrap_or(0);
|
|
|
|
let graph = git_spawn!(repo, domain -> {
|
|
domain.commit_graph_simple(rev_clone.as_deref(), limit)
|
|
})?;
|
|
|
|
let response = CommitGraphResponse::from(graph);
|
|
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
let _: Option<()> = conn
|
|
.set_ex::<String, String, ()>(
|
|
cache_key,
|
|
serde_json::to_string(&response).unwrap_or_default(),
|
|
300,
|
|
)
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
Ok(response)
|
|
}
|
|
|
|
pub async fn git_commit_walk(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitWalkQuery,
|
|
ctx: &Session,
|
|
) -> Result<Vec<CommitMetaResponse>, AppError> {
|
|
let cache_key = format!(
|
|
"git:commit:walk:{}:{}:{:?}:{}:{}:{}:{}",
|
|
namespace,
|
|
repo_name,
|
|
query.rev,
|
|
query.limit.unwrap_or(0),
|
|
query.first_parent_only,
|
|
query.topological,
|
|
query.reverse,
|
|
);
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
if let Ok(cached) = conn.get::<_, String>(cache_key.clone()).await {
|
|
if let Ok(cached) = serde_json::from_str(&cached) {
|
|
return Ok(cached);
|
|
}
|
|
}
|
|
}
|
|
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let rev_clone = query.rev.clone();
|
|
let limit = query.limit.unwrap_or(0);
|
|
let first_parent_only = query.first_parent_only;
|
|
let topological = query.topological;
|
|
let reverse = query.reverse;
|
|
|
|
let sort = if topological && reverse {
|
|
CommitSort(CommitSort::TOPOLOGICAL.0 | CommitSort::TIME.0 | CommitSort::REVERSE.0)
|
|
} else if topological {
|
|
CommitSort(CommitSort::TOPOLOGICAL.0 | CommitSort::TIME.0)
|
|
} else if reverse {
|
|
CommitSort(CommitSort::TIME.0 | CommitSort::REVERSE.0)
|
|
} else {
|
|
CommitSort(CommitSort::TOPOLOGICAL.0 | CommitSort::TIME.0)
|
|
};
|
|
|
|
let commits = git_spawn!(repo, domain -> {
|
|
domain.commit_walk(CommitWalkOptions {
|
|
rev: rev_clone,
|
|
sort,
|
|
limit,
|
|
first_parent_only,
|
|
})
|
|
})?;
|
|
|
|
let response: Vec<CommitMetaResponse> =
|
|
commits.into_iter().map(CommitMetaResponse::from).collect();
|
|
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
let _: Option<()> = conn
|
|
.set_ex::<String, String, ()>(
|
|
cache_key,
|
|
serde_json::to_string(&response).unwrap_or_default(),
|
|
300,
|
|
)
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
Ok(response)
|
|
}
|
|
|
|
pub async fn git_commit_ancestors(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitAncestorsQuery,
|
|
ctx: &Session,
|
|
) -> Result<Vec<CommitMetaResponse>, AppError> {
|
|
let cache_key = format!(
|
|
"git:commit:ancestors:{}:{}:{}:{}",
|
|
namespace,
|
|
repo_name,
|
|
query.oid,
|
|
query.limit.unwrap_or(0),
|
|
);
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
if let Ok(cached) = conn.get::<_, String>(cache_key.clone()).await {
|
|
if let Ok(cached) = serde_json::from_str(&cached) {
|
|
return Ok(cached);
|
|
}
|
|
}
|
|
}
|
|
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
let limit = query.limit.unwrap_or(0);
|
|
|
|
let commits = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_ancestors(&oid, limit)
|
|
})?;
|
|
|
|
let response: Vec<CommitMetaResponse> =
|
|
commits.into_iter().map(CommitMetaResponse::from).collect();
|
|
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
let _: Option<()> = conn
|
|
.set_ex::<String, String, ()>(
|
|
cache_key,
|
|
serde_json::to_string(&response).unwrap_or_default(),
|
|
300,
|
|
)
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
Ok(response)
|
|
}
|
|
|
|
pub async fn git_commit_descendants(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitDescendantsQuery,
|
|
ctx: &Session,
|
|
) -> Result<Vec<CommitMetaResponse>, AppError> {
|
|
let cache_key = format!(
|
|
"git:commit:descendants:{}:{}:{}:{}",
|
|
namespace,
|
|
repo_name,
|
|
query.oid,
|
|
query.limit.unwrap_or(0),
|
|
);
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
if let Ok(cached) = conn.get::<_, String>(cache_key.clone()).await {
|
|
if let Ok(cached) = serde_json::from_str(&cached) {
|
|
return Ok(cached);
|
|
}
|
|
}
|
|
}
|
|
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let oid_str = query.oid.clone();
|
|
let limit = query.limit.unwrap_or(0);
|
|
|
|
let commits = git_spawn!(repo, domain -> {
|
|
let oid = git::CommitOid::new(&oid_str);
|
|
domain.commit_descendants(&oid, limit)
|
|
})?;
|
|
|
|
let response: Vec<CommitMetaResponse> =
|
|
commits.into_iter().map(CommitMetaResponse::from).collect();
|
|
|
|
if let Ok(mut conn) = self.cache.conn().await {
|
|
let _: Option<()> = conn
|
|
.set_ex::<String, String, ()>(
|
|
cache_key,
|
|
serde_json::to_string(&response).unwrap_or_default(),
|
|
300,
|
|
)
|
|
.await
|
|
.ok();
|
|
}
|
|
|
|
Ok(response)
|
|
}
|
|
|
|
pub async fn git_commit_resolve_rev(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: CommitResolveQuery,
|
|
ctx: &Session,
|
|
) -> Result<String, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let rev_str = query.rev.clone();
|
|
|
|
let oid = git_spawn!(repo, domain -> {
|
|
domain.resolve_rev(&rev_str)
|
|
})?;
|
|
|
|
Ok(oid.to_string())
|
|
}
|
|
|
|
pub async fn git_commit_create(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
request: CommitCreateRequest,
|
|
ctx: &Session,
|
|
) -> Result<CommitCreateResponse, AppError> {
|
|
let repo: repo::Model = self.utils_check_repo_admin(namespace, repo_name, ctx).await?;
|
|
let parent_ids: Vec<_> = request
|
|
.parent_ids
|
|
.iter()
|
|
.map(|p| git::CommitOid::new(p))
|
|
.collect();
|
|
let author = git::CommitSignature {
|
|
name: request.author_name,
|
|
email: request.author_email,
|
|
time_secs: chrono::Utc::now().timestamp(),
|
|
offset_minutes: 0,
|
|
};
|
|
let committer = git::CommitSignature {
|
|
name: request.committer_name,
|
|
email: request.committer_email,
|
|
time_secs: chrono::Utc::now().timestamp(),
|
|
offset_minutes: 0,
|
|
};
|
|
let tree_id = git::CommitOid::new(&request.tree_id);
|
|
let update_ref = request.update_ref.clone();
|
|
|
|
let oid = git_spawn!(repo, domain -> {
|
|
domain.commit_create(
|
|
update_ref.as_deref(),
|
|
&author,
|
|
&committer,
|
|
&request.message,
|
|
&tree_id,
|
|
&parent_ids,
|
|
)
|
|
})?;
|
|
|
|
Ok(CommitCreateResponse {
|
|
oid: oid.to_string(),
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_amend(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
request: CommitAmendRequest,
|
|
ctx: &Session,
|
|
) -> Result<CommitCreateResponse, AppError> {
|
|
let repo: repo::Model = self.utils_check_repo_admin(namespace, repo_name, ctx).await?;
|
|
let oid = git::CommitOid::new(&request.oid);
|
|
let author = if request.author_name.is_some() && request.author_email.is_some() {
|
|
Some(git::CommitSignature {
|
|
name: request.author_name.unwrap(),
|
|
email: request.author_email.unwrap(),
|
|
time_secs: 0,
|
|
offset_minutes: 0,
|
|
})
|
|
} else {
|
|
None
|
|
};
|
|
let committer =
|
|
if request.committer_name.is_some() && request.committer_email.is_some() {
|
|
Some(git::CommitSignature {
|
|
name: request.committer_name.unwrap(),
|
|
email: request.committer_email.unwrap(),
|
|
time_secs: 0,
|
|
offset_minutes: 0,
|
|
})
|
|
} else {
|
|
None
|
|
};
|
|
let tree_id = request.tree_id.as_ref().map(|t| git::CommitOid::new(t));
|
|
let update_ref = request.update_ref.clone();
|
|
let message_encoding = request.message_encoding.clone();
|
|
let message = request.message.clone();
|
|
|
|
let new_oid = git_spawn!(repo, domain -> {
|
|
domain.commit_amend(
|
|
&oid,
|
|
update_ref.as_deref(),
|
|
author.as_ref(),
|
|
committer.as_ref(),
|
|
message_encoding.as_deref(),
|
|
message.as_deref(),
|
|
tree_id.as_ref(),
|
|
)
|
|
})?;
|
|
|
|
Ok(CommitCreateResponse {
|
|
oid: new_oid.to_string(),
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_cherry_pick(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
request: CommitCherryPickRequest,
|
|
ctx: &Session,
|
|
) -> Result<CommitCreateResponse, AppError> {
|
|
let repo: repo::Model = self.utils_check_repo_admin(namespace, repo_name, ctx).await?;
|
|
let cherrypick_oid = git::CommitOid::new(&request.cherrypick_oid);
|
|
let author = git::CommitSignature {
|
|
name: request.author_name,
|
|
email: request.author_email,
|
|
time_secs: chrono::Utc::now().timestamp(),
|
|
offset_minutes: 0,
|
|
};
|
|
let committer = git::CommitSignature {
|
|
name: request.committer_name,
|
|
email: request.committer_email,
|
|
time_secs: chrono::Utc::now().timestamp(),
|
|
offset_minutes: 0,
|
|
};
|
|
let message = request.message.clone();
|
|
let mainline = request.mainline.unwrap_or(0);
|
|
let update_ref = request.update_ref.clone();
|
|
|
|
let oid = git_spawn!(repo, domain -> {
|
|
domain.commit_cherry_pick(
|
|
&cherrypick_oid,
|
|
&author,
|
|
&committer,
|
|
message.as_deref(),
|
|
mainline,
|
|
update_ref.as_deref(),
|
|
)
|
|
})?;
|
|
|
|
Ok(CommitCreateResponse {
|
|
oid: oid.to_string(),
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_cherry_pick_abort(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
request: CommitCherryPickAbortRequest,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let repo: repo::Model = self.utils_check_repo_admin(namespace, repo_name, ctx).await?;
|
|
let reset_type = request.reset_type.clone();
|
|
|
|
git_spawn!(repo, domain -> {
|
|
domain.commit_cherry_pick_abort(reset_type.as_deref().unwrap_or("hard"))
|
|
})?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn git_commit_revert(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
request: CommitRevertRequest,
|
|
ctx: &Session,
|
|
) -> Result<CommitCreateResponse, AppError> {
|
|
let repo: repo::Model = self.utils_check_repo_admin(namespace, repo_name, ctx).await?;
|
|
let revert_oid = git::CommitOid::new(&request.revert_oid);
|
|
let author = git::CommitSignature {
|
|
name: request.author_name,
|
|
email: request.author_email,
|
|
time_secs: chrono::Utc::now().timestamp(),
|
|
offset_minutes: 0,
|
|
};
|
|
let committer = git::CommitSignature {
|
|
name: request.committer_name,
|
|
email: request.committer_email,
|
|
time_secs: chrono::Utc::now().timestamp(),
|
|
offset_minutes: 0,
|
|
};
|
|
let message = request.message.clone();
|
|
let mainline = request.mainline.unwrap_or(0);
|
|
let update_ref = request.update_ref.clone();
|
|
|
|
let oid = git_spawn!(repo, domain -> {
|
|
domain.commit_revert(
|
|
&revert_oid,
|
|
&author,
|
|
&committer,
|
|
message.as_deref(),
|
|
mainline,
|
|
update_ref.as_deref(),
|
|
)
|
|
})?;
|
|
|
|
Ok(CommitCreateResponse {
|
|
oid: oid.to_string(),
|
|
})
|
|
}
|
|
|
|
pub async fn git_commit_revert_abort(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
request: CommitRevertAbortRequest,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let repo: repo::Model = self.utils_check_repo_admin(namespace, repo_name, ctx).await?;
|
|
let reset_type = request.reset_type.clone();
|
|
|
|
git_spawn!(repo, domain -> {
|
|
domain.commit_revert_abort(reset_type.as_deref().unwrap_or("hard"))
|
|
})?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|