7b02b53
ZhenYi
feat(lib/git): add SSH transport (server, git_service, handler/auth/channel/command/git_op)
7b02b53
use gitbaby::diff::types::{
ChangedPath, ChangedPathDiff, ChangedPathRequest, DiffOptions, DiffRequest, NumStat,
RangeDiffSpec, ShortStat,
};
use gix::ObjectId;
use serde::{Deserialize, Serialize};
use crate::service::GitService;
use crate::share::errors::{GitError, map_baby};
pub fn parse_oid(hex: &str) -> Result<ObjectId, GitError> {
ObjectId::from_hex(hex.trim().as_bytes())
.map_err(|_| GitError::InvalidArgument(format!("invalid object id `{hex}`")))
}
#[derive(Debug, Clone, Deserialize, Default)]
pub struct DiffOptionsDto {
#[serde(default)]
pub paths: Option<Vec<std::path::PathBuf>>,
#[serde(default)]
pub detect_renames: Option<u8>,
}
impl From<DiffOptionsDto> for DiffOptions {
fn from(d: DiffOptionsDto) -> Self {
Self {
paths: d.paths,
detect_renames: d.detect_renames,
..Default::default()
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct DiffTreeRecordDto {
pub status: String,
pub score: u8,
pub head_path: Option<String>,
pub base_path: Option<String>,
pub head_mode: Option<u32>,
pub base_mode: Option<u32>,
pub head_blob_id: Option<String>,
pub base_blob_id: Option<String>,
}
impl GitService {
async fn diff_request(
&self,
ctx: &crate::context::facade::GitContext,
base: &str,
head: &str,
) -> Result<DiffRequest, GitError> {
let baby = self.baby(ctx);
let left = baby.rev_parse(base).await.map_err(map_baby)?;
let right = baby.rev_parse(head).await.map_err(map_baby)?;
Ok(DiffRequest { left, right })
}
pub async fn diff_raw(
&self,
space_name: String,
repo_name: String,
base: String,
head: String,
) -> Result<String, GitError> {
let ctx = self.context(space_name, repo_name).await?;
let req = self.diff_request(&ctx, &base, &head).await?;
self.baby(&ctx).raw_diff(&req, None).await.map_err(map_baby)
}
pub async fn diff_files_changed_between(
&self,
space_name: String,
repo_name: String,
base: String,
head: String,
) -> Result<Vec<String>, GitError> {
let ctx = self.context(space_name, repo_name).await?;
let baby = self.baby(&ctx);
let left = baby.rev_parse(&base).await.map_err(map_baby)?;
let right = baby.rev_parse(&head).await.map_err(map_baby)?;
let paths = baby
.find_changed_paths(
vec![ChangedPathRequest::TreePair(left, right)],
None,
None,
false,
)
.await
.map_err(map_baby)?;
Ok(paths
.into_iter()
.map(|p| p.new_path.to_string_lossy().into_owned())
.collect())
}
pub async fn diff_short_stat(
&self,
space_name: String,
repo_name: String,
base: String,
head: String,
) -> Result<ShortStat, GitError> {
let ctx = self.context(space_name, repo_name).await?;
let req = self.diff_request(&ctx, &base, &head).await?;
self.baby(&ctx)
.shortstat(&req, &DiffOptions::default())
.await
.map_err(map_baby)
}
pub async fn diff_stats(
&self,
space_name: String,
repo_name: String,
base: String,
head: String,
opts: DiffOptionsDto,
) -> Result<Vec<NumStat>, GitError> {
let ctx = self.context(space_name, repo_name).await?;
let req = self.diff_request(&ctx, &base, &head).await?;
self.baby(&ctx)
.diff_stats(&req, &opts.into())
.await
.map_err(map_baby)
}
pub async fn diff_files(
&self,
space_name: String,
repo_name: String,
base: String,
head: String,
opts: DiffOptionsDto,
) -> Result<Vec<ChangedPathDiff>, GitError> {
let ctx = self.context(space_name, repo_name).await?;
let req = self.diff_request(&ctx, &base, &head).await?;
self.baby(&ctx)
.commit_diff(&req, &opts.into(), None)
.await
.map_err(map_baby)
}
pub async fn diff_for_commit(
&self,
space_name: String,
repo_name: String,
commit: String,
) -> Result<Vec<ChangedPath>, GitError> {
let ctx = self.context(space_name, repo_name).await?;
let oid = parse_oid(&commit)?;
self.baby(&ctx)
.find_changed_paths(vec![ChangedPathRequest::Commit(oid)], None, None, false)
.await
.map_err(map_baby)
}
pub async fn diff_tree(
&self,
space_name: String,
repo_name: String,
base: String,
head: String,
use_merge_base: bool,
) -> Result<Vec<DiffTreeRecordDto>, GitError> {
let ctx = self.context(space_name, repo_name).await?;
let baby = self.baby(&ctx);
let left = if use_merge_base {
let mb = baby.merge_base(&base, &head).await.map_err(map_baby)?;
mb.to_string()
} else {
baby.rev_parse(&base).await.map_err(map_baby)?.to_string()
};
let right = baby.rev_parse(&head).await.map_err(map_baby)?;
let rows = baby
.diff_trees(
&left,
&right.to_string(),
std::path::Path::new(""),
gitbaby::tree::types::DiffTreesOptions {
recurse: true,
detect_renames: true,
..Default::default()
},
)
.await
.map_err(map_baby)?;
Ok(rows
.into_iter()
.map(|d| DiffTreeRecordDto {
status: format!("{:?}", d.status).to_lowercase(),
score: 0,
head_path: d.new_path.map(|p| p.to_string_lossy().into_owned()),
base_path: d.old_path.map(|p| p.to_string_lossy().into_owned()),
head_mode: d.new_mode.map(|m| m as u32),
base_mode: d.old_mode.map(|m| m as u32),
head_blob_id: d.new_oid.map(|o| o.to_string()),
base_blob_id: d.old_oid.map(|o| o.to_string()),
})
.collect())
}
pub async fn range_diff(
&self,
space_name: String,
repo_name: String,
spec: RangeDiffSpec,
) -> Result<String, GitError> {
let ctx = self.context(space_name, repo_name).await?;
self.baby(&ctx)
.range_diff(&spec, &DiffOptions::default(), None)
.await
.map_err(map_baby)
}
}