464 lines
15 KiB
Rust
464 lines
15 KiB
Rust
use crate::AppService;
|
|
use crate::error::AppError;
|
|
use crate::git::MergeOptions;
|
|
use models::repos::repo;
|
|
use sea_orm::*;
|
|
use serde::{Deserialize, Serialize};
|
|
use session::Session;
|
|
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct DescriptionQuery {
|
|
pub description: String,
|
|
}
|
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct DescriptionResponse {
|
|
pub description: String,
|
|
}
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct ConfigGetQuery {
|
|
pub key: String,
|
|
}
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct ConfigSetRequest {
|
|
pub key: String,
|
|
pub value: String,
|
|
}
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct ConfigDeleteQuery {
|
|
pub key: String,
|
|
}
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct ConfigEntriesQuery {
|
|
pub prefix: Option<String>,
|
|
}
|
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct ConfigEntryResponse {
|
|
pub name: String,
|
|
pub value: String,
|
|
}
|
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct ConfigSnapshotResponse {
|
|
pub entries: Vec<ConfigEntryResponse>,
|
|
}
|
|
impl From<git::ConfigEntry> for ConfigEntryResponse {
|
|
fn from(e: git::ConfigEntry) -> Self {
|
|
Self {
|
|
name: e.name,
|
|
value: e.value,
|
|
}
|
|
}
|
|
}
|
|
impl From<git::ConfigSnapshot> for ConfigSnapshotResponse {
|
|
fn from(s: git::ConfigSnapshot) -> Self {
|
|
Self {
|
|
entries: s
|
|
.entries
|
|
.into_iter()
|
|
.map(ConfigEntryResponse::from)
|
|
.collect(),
|
|
}
|
|
}
|
|
}
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct GitUpdateRepoRequest {
|
|
pub default_branch: Option<String>,
|
|
}
|
|
#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
|
|
pub struct ConfigBoolResponse {
|
|
pub key: String,
|
|
pub value: bool,
|
|
}
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct MergeAnalysisQuery {
|
|
pub their_oid: String,
|
|
}
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct MergeRefAnalysisQuery {
|
|
pub ref_name: String,
|
|
pub their_oid: String,
|
|
}
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct MergeCommitsRequest {
|
|
pub local_oid: String,
|
|
pub remote_oid: String,
|
|
#[serde(default)]
|
|
pub find_renames: bool,
|
|
#[serde(default)]
|
|
pub fail_on_conflict: bool,
|
|
#[serde(default)]
|
|
pub skip_reuc: bool,
|
|
#[serde(default)]
|
|
pub no_recursive: bool,
|
|
#[serde(default = "default_rename_threshold")]
|
|
pub rename_threshold: u32,
|
|
#[serde(default)]
|
|
pub target_limit: u32,
|
|
#[serde(default)]
|
|
pub recursion_limit: u32,
|
|
}
|
|
fn default_rename_threshold() -> u32 {
|
|
50
|
|
}
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct MergeTreesRequest {
|
|
pub ancestor_oid: String,
|
|
pub our_oid: String,
|
|
pub their_oid: String,
|
|
#[serde(default)]
|
|
pub find_renames: bool,
|
|
#[serde(default)]
|
|
pub fail_on_conflict: bool,
|
|
#[serde(default)]
|
|
pub skip_reuc: bool,
|
|
#[serde(default)]
|
|
pub no_recursive: bool,
|
|
#[serde(default = "default_rename_threshold")]
|
|
pub rename_threshold: u32,
|
|
#[serde(default)]
|
|
pub target_limit: u32,
|
|
#[serde(default)]
|
|
pub recursion_limit: u32,
|
|
}
|
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct MergeAnalysisResponse {
|
|
pub analysis: MergeAnalysisResultInner,
|
|
pub preference: MergePreferenceResultInner,
|
|
}
|
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct MergeAnalysisResultInner {
|
|
pub is_none: bool,
|
|
pub is_normal: bool,
|
|
pub is_up_to_date: bool,
|
|
pub is_fast_forward: bool,
|
|
pub is_unborn: bool,
|
|
}
|
|
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
|
pub struct MergePreferenceResultInner {
|
|
pub is_none: bool,
|
|
pub is_no_fast_forward: bool,
|
|
pub is_fastforward_only: bool,
|
|
}
|
|
impl From<git::MergeAnalysisResult> for MergeAnalysisResultInner {
|
|
fn from(r: git::MergeAnalysisResult) -> Self {
|
|
Self {
|
|
is_none: r.is_none,
|
|
is_normal: r.is_normal,
|
|
is_up_to_date: r.is_up_to_date,
|
|
is_fast_forward: r.is_fast_forward,
|
|
is_unborn: r.is_unborn,
|
|
}
|
|
}
|
|
}
|
|
impl From<git::MergePreferenceResult> for MergePreferenceResultInner {
|
|
fn from(r: git::MergePreferenceResult) -> Self {
|
|
Self {
|
|
is_none: r.is_none,
|
|
is_no_fast_forward: r.is_no_fast_forward,
|
|
is_fastforward_only: r.is_fastforward_only,
|
|
}
|
|
}
|
|
}
|
|
#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
|
|
pub struct MergeheadInfoResponse {
|
|
pub oid: String,
|
|
}
|
|
impl From<git::MergeheadInfo> for MergeheadInfoResponse {
|
|
fn from(h: git::MergeheadInfo) -> Self {
|
|
Self {
|
|
oid: h.oid.to_string(),
|
|
}
|
|
}
|
|
}
|
|
impl AppService {
|
|
pub async fn git_description_get(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<DescriptionResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
let description = domain.description_get()?;
|
|
Ok(DescriptionResponse { description })
|
|
}
|
|
|
|
pub async fn git_description_set(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: DescriptionQuery,
|
|
ctx: &Session,
|
|
) -> Result<DescriptionResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
domain.description_set(&query.description)?;
|
|
Ok(DescriptionResponse {
|
|
description: query.description,
|
|
})
|
|
}
|
|
|
|
pub async fn git_description_reset(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<DescriptionResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
domain.description_reset()?;
|
|
Ok(DescriptionResponse {
|
|
description: "Unnamed repository".to_string(),
|
|
})
|
|
}
|
|
|
|
pub async fn git_description_exists(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<bool, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
Ok(domain.description_exists())
|
|
}
|
|
|
|
pub async fn git_config_entries(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: ConfigEntriesQuery,
|
|
ctx: &Session,
|
|
) -> Result<ConfigSnapshotResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
let snapshot = domain
|
|
.config_entries(query.prefix.as_deref())
|
|
.map_err(AppError::from)?;
|
|
Ok(ConfigSnapshotResponse::from(snapshot))
|
|
}
|
|
|
|
pub async fn git_config_get(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: ConfigGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<Option<String>, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
domain.config_get(&query.key).map_err(AppError::from)
|
|
}
|
|
|
|
pub async fn git_config_set(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
request: ConfigSetRequest,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
domain
|
|
.config_set(&request.key, &request.value)
|
|
.map_err(AppError::from)
|
|
}
|
|
|
|
pub async fn git_config_delete(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: ConfigDeleteQuery,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
domain.config_delete(&query.key).map_err(AppError::from)
|
|
}
|
|
|
|
pub async fn git_config_has(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: ConfigGetQuery,
|
|
ctx: &Session,
|
|
) -> Result<ConfigBoolResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
let exists = domain.config_has(&query.key).map_err(AppError::from)?;
|
|
Ok(ConfigBoolResponse {
|
|
key: query.key,
|
|
value: exists,
|
|
})
|
|
}
|
|
|
|
pub async fn git_merge_analysis(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: MergeAnalysisQuery,
|
|
ctx: &Session,
|
|
) -> Result<MergeAnalysisResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
let their_oid = git::CommitOid::new(&query.their_oid);
|
|
let (analysis, preference) = domain.merge_analysis(&their_oid).map_err(AppError::from)?;
|
|
Ok(MergeAnalysisResponse {
|
|
analysis: MergeAnalysisResultInner::from(analysis),
|
|
preference: MergePreferenceResultInner::from(preference),
|
|
})
|
|
}
|
|
|
|
pub async fn git_merge_analysis_for_ref(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
query: MergeRefAnalysisQuery,
|
|
ctx: &Session,
|
|
) -> Result<MergeAnalysisResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
let their_oid = git::CommitOid::new(&query.their_oid);
|
|
let (analysis, preference) = domain
|
|
.merge_analysis_for_ref(&query.ref_name, &their_oid)
|
|
.map_err(AppError::from)?;
|
|
Ok(MergeAnalysisResponse {
|
|
analysis: MergeAnalysisResultInner::from(analysis),
|
|
preference: MergePreferenceResultInner::from(preference),
|
|
})
|
|
}
|
|
|
|
pub async fn git_merge_base(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
oid1: String,
|
|
oid2: String,
|
|
ctx: &Session,
|
|
) -> Result<String, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
let base = domain
|
|
.merge_base(&git::CommitOid::new(&oid1), &git::CommitOid::new(&oid2))
|
|
.map_err(AppError::from)?;
|
|
Ok(base.to_string())
|
|
}
|
|
|
|
pub async fn git_merge_commits(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
request: MergeCommitsRequest,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let mut opts = MergeOptions::new();
|
|
opts = opts.find_renames(request.find_renames);
|
|
opts = opts.fail_on_conflict(request.fail_on_conflict);
|
|
opts = opts.skip_reuc(request.skip_reuc);
|
|
opts = opts.no_recursive(request.no_recursive);
|
|
opts = opts.rename_threshold(request.rename_threshold);
|
|
opts = opts.target_limit(request.target_limit);
|
|
opts = opts.recursion_limit(request.recursion_limit);
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
domain
|
|
.merge_commits(
|
|
&git::CommitOid::new(&request.local_oid),
|
|
&git::CommitOid::new(&request.remote_oid),
|
|
Some(opts),
|
|
)
|
|
.map_err(AppError::from)
|
|
}
|
|
|
|
pub async fn git_merge_trees(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
request: MergeTreesRequest,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let mut opts = MergeOptions::new();
|
|
opts = opts.find_renames(request.find_renames);
|
|
opts = opts.fail_on_conflict(request.fail_on_conflict);
|
|
opts = opts.skip_reuc(request.skip_reuc);
|
|
opts = opts.no_recursive(request.no_recursive);
|
|
opts = opts.rename_threshold(request.rename_threshold);
|
|
opts = opts.target_limit(request.target_limit);
|
|
opts = opts.recursion_limit(request.recursion_limit);
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
domain
|
|
.merge_trees(
|
|
&git::CommitOid::new(&request.ancestor_oid),
|
|
&git::CommitOid::new(&request.our_oid),
|
|
&git::CommitOid::new(&request.their_oid),
|
|
Some(opts),
|
|
)
|
|
.map_err(AppError::from)
|
|
}
|
|
|
|
pub async fn git_merge_abort(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
domain.merge_abort().map_err(AppError::from)
|
|
}
|
|
|
|
pub async fn git_merge_is_in_progress(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<bool, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
Ok(domain.merge_is_in_progress())
|
|
}
|
|
|
|
pub async fn git_mergehead_list(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<Vec<MergeheadInfoResponse>, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let mut domain = git::GitDomain::from_model(repo)?;
|
|
let heads = domain.mergehead_list().map_err(AppError::from)?;
|
|
Ok(heads.into_iter().map(MergeheadInfoResponse::from).collect())
|
|
}
|
|
|
|
pub async fn git_merge_is_conflicted(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<bool, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let domain = git::GitDomain::from_model(repo)?;
|
|
Ok(domain.merge_is_conflicted())
|
|
}
|
|
|
|
pub async fn git_update_repo(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
params: GitUpdateRepoRequest,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let repo = self
|
|
.utils_find_repo(namespace.clone(), repo_name.clone(), ctx)
|
|
.await?;
|
|
let txn = self.db.begin().await?;
|
|
let mut active: repo::ActiveModel = repo.clone().into_active_model();
|
|
if let Some(default_branch) = params.default_branch {
|
|
active.default_branch = Set(default_branch);
|
|
}
|
|
active.update(&txn).await?;
|
|
txn.commit().await?;
|
|
Ok(())
|
|
}
|
|
}
|